From 71b95bd558e137958f7236ca706949901f541d3b Mon Sep 17 00:00:00 2001 From: Ernesto Tejeda Date: Wed, 13 May 2020 00:27:46 -0400 Subject: [PATCH 001/108] [ADD] rma: new module [UPD] Update rma.pot Update translation files Updated by "Update PO files to match POT (msgmerge)" hook in Weblate. Translation: rma-12.0/rma-12.0-rma Translate-URL: https://translation.odoo-community.org/projects/rma-12-0/rma-12-0-rma/ --- rma/README.rst | 152 ++ rma/__init__.py | 6 + rma/__manifest__.py | 37 + rma/controllers/__init__.py | 3 + rma/controllers/main.py | 132 ++ rma/data/mail_data.xml | 46 + rma/data/rma_operation_data.xml | 12 + rma/hooks.py | 70 + rma/i18n/es.po | 1635 +++++++++++++++++++++ rma/i18n/rma.pot | 1485 +++++++++++++++++++ rma/models/__init__.py | 12 + rma/models/account_invoice.py | 35 + rma/models/res_company.py | 23 + rma/models/res_partner.py | 41 + rma/models/res_users.py | 14 + rma/models/rma.py | 1144 ++++++++++++++ rma/models/rma_operation.py | 15 + rma/models/rma_team.py | 56 + rma/models/stock_move.py | 96 ++ rma/models/stock_picking.py | 41 + rma/models/stock_warehouse.py | 118 ++ rma/readme/CONFIGURE.rst | 8 + rma/readme/CONTRIBUTORS.rst | 5 + rma/readme/DESCRIPTION.rst | 8 + rma/readme/ROADMAP.rst | 3 + rma/readme/USAGE.rst | 37 + rma/report/report.xml | 14 + rma/security/ir.model.access.csv | 8 + rma/security/rma_security.xml | 60 + rma/static/description/icon.png | Bin 0 -> 7450 bytes rma/static/description/index.html | 496 +++++++ rma/tests/__init__.py | 3 + rma/tests/test_rma.py | 638 ++++++++ rma/views/menus.xml | 24 + rma/views/report_rma.xml | 96 ++ rma/views/res_partner_views.xml | 23 + rma/views/rma_portal_templates.xml | 269 ++++ rma/views/rma_team_views.xml | 90 ++ rma/views/rma_views.xml | 285 ++++ rma/views/stock_picking_views.xml | 23 + rma/views/stock_warehouse_views.xml | 17 + rma/wizard/__init__.py | 5 + rma/wizard/rma_delivery.py | 102 ++ rma/wizard/rma_delivery_views.xml | 50 + rma/wizard/rma_split.py | 70 + rma/wizard/rma_split_views.xml | 33 + rma/wizard/stock_picking_return.py | 83 ++ rma/wizard/stock_picking_return_views.xml | 20 + 48 files changed, 7643 insertions(+) create mode 100644 rma/README.rst create mode 100644 rma/__init__.py create mode 100644 rma/__manifest__.py create mode 100644 rma/controllers/__init__.py create mode 100644 rma/controllers/main.py create mode 100644 rma/data/mail_data.xml create mode 100644 rma/data/rma_operation_data.xml create mode 100644 rma/hooks.py create mode 100644 rma/i18n/es.po create mode 100644 rma/i18n/rma.pot create mode 100644 rma/models/__init__.py create mode 100644 rma/models/account_invoice.py create mode 100644 rma/models/res_company.py create mode 100644 rma/models/res_partner.py create mode 100644 rma/models/res_users.py create mode 100644 rma/models/rma.py create mode 100644 rma/models/rma_operation.py create mode 100644 rma/models/rma_team.py create mode 100644 rma/models/stock_move.py create mode 100644 rma/models/stock_picking.py create mode 100644 rma/models/stock_warehouse.py create mode 100644 rma/readme/CONFIGURE.rst create mode 100644 rma/readme/CONTRIBUTORS.rst create mode 100644 rma/readme/DESCRIPTION.rst create mode 100644 rma/readme/ROADMAP.rst create mode 100644 rma/readme/USAGE.rst create mode 100644 rma/report/report.xml create mode 100644 rma/security/ir.model.access.csv create mode 100644 rma/security/rma_security.xml create mode 100644 rma/static/description/icon.png create mode 100644 rma/static/description/index.html create mode 100644 rma/tests/__init__.py create mode 100644 rma/tests/test_rma.py create mode 100644 rma/views/menus.xml create mode 100644 rma/views/report_rma.xml create mode 100644 rma/views/res_partner_views.xml create mode 100644 rma/views/rma_portal_templates.xml create mode 100644 rma/views/rma_team_views.xml create mode 100644 rma/views/rma_views.xml create mode 100644 rma/views/stock_picking_views.xml create mode 100644 rma/views/stock_warehouse_views.xml create mode 100644 rma/wizard/__init__.py create mode 100644 rma/wizard/rma_delivery.py create mode 100644 rma/wizard/rma_delivery_views.xml create mode 100644 rma/wizard/rma_split.py create mode 100644 rma/wizard/rma_split_views.xml create mode 100644 rma/wizard/stock_picking_return.py create mode 100644 rma/wizard/stock_picking_return_views.xml diff --git a/rma/README.rst b/rma/README.rst new file mode 100644 index 000000000..788f6684a --- /dev/null +++ b/rma/README.rst @@ -0,0 +1,152 @@ +=========================================== +Return Merchandise Authorization Management +=========================================== + +.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png + :target: https://odoo-community.org/page/development-status + :alt: Beta +.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Frma-lightgray.png?logo=github + :target: https://github.com/OCA/rma/tree/12.0/rma + :alt: OCA/rma +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/rma-12-0/rma-12-0-rma + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png + :target: https://runbot.odoo-community.org/runbot/145/12.0 + :alt: Try me on Runbot + +|badge1| |badge2| |badge3| |badge4| |badge5| + +This module allows you to manage `Return Merchandise Authorization (RMA) +`_. +RMA documents can be created from scratch, from a delivery order or from +an incoming email. Product receptions and returning delivery operations +of the RMA module are fully integrated with the Receipts and Deliveries +Operations of Odoo inventory core module. It also allows you to generate +refunds in the same way as Odoo generates it. +Besides, you have full integration of the RMA documents in the customer portal. + +**Table of contents** + +.. contents:: + :local: + +Configuration +============= + +If you want RMAs to be created from incoming emails, you need to: + +#. Go to *Settings > General Settings*. +#. Check 'External Email Servers' checkbox under *Discuss* section. +#. Set an 'alias domain' and an incoming server. +#. Go to *RMA > Configuration > RMA Team* and select a team or create a new + one. +#. Go to 'Email' tab and set an 'Email Alias'. + +Usage +===== + +To use this module, you need to: + +#. Go to *RMA > Orders* and create a new RMA. +#. Select a partner, an invoice address, select a product + (or select a picking and a move instead), write a quantity, fill the rest + of the form and click on 'confirm' button in the status bar. +#. You will see an smart button labeled 'Receipt'. Click on that button to see + the reception operation form. +#. If everything is right, validate the operation and go back to the RMA to + see it in a 'received' state. +#. Now you are able to generate a refund, generate a delivery order to return + to the customer the same product or another product as a replacement, split + the RMA by extracting a part of the remaining quantity to another RMA, + preview the RMA in the website. All of these operations can be done by + clicking on the buttons in the status bar. + + * If you click on 'Refund' button, a refund will be created, and it will be + accessible via the smart button labeled Refund. The RMA will be set + automatically to 'Refunded' state when the refund is validated. + * If you click on 'Replace' or 'Return to customer' button instead, + a popup wizard will guide you to create a Delivery order to the client + and this order will be accessible via the smart button labeled Delivery. + The RMA will be set automatically to 'Replaced' or 'Returned' state when + the RMA quantity is equal or lower than the quantity in done delivery + orders linked to it. + +An RMA can also be created from a return of a delivery order: + +#. Select a delivery order and click on 'Return' button to create a return. +#. Check "Create RMAs" checkbox in the returning wizard, select the RMA + stock location and click on 'Return' button. +#. An RMA will be created for each product returned in the previous step. + Every RMA will be in confirmed state and they will + be linked to the returning operation generated previously. + +**Note: An RMA can also be created from an incoming email (See configuration +section).** + +Known issues / Roadmap +====================== + +* As soon as the picking is selected, the user should select the move, + but perhaps stock.move _rec_name could be improved to better show what + the product of that move is. + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues `_. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us smashing it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +~~~~~~~ + +* Tecnativa + +Contributors +~~~~~~~~~~~~ + +* `Tecnativa `_: + + * Ernesto Tejeda + * Pedro M. Baeza + * David Vidal + +Maintainers +~~~~~~~~~~~ + +This module is maintained by the OCA. + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use. + +.. |maintainer-ernestotejeda| image:: https://github.com/ernestotejeda.png?size=40px + :target: https://github.com/ernestotejeda + :alt: ernestotejeda + +Current `maintainer `__: + +|maintainer-ernestotejeda| + +This module is part of the `OCA/rma `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/rma/__init__.py b/rma/__init__.py new file mode 100644 index 000000000..336227cc4 --- /dev/null +++ b/rma/__init__.py @@ -0,0 +1,6 @@ +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from . import controllers +from . import models +from . import wizard +from .hooks import post_init_hook diff --git a/rma/__manifest__.py b/rma/__manifest__.py new file mode 100644 index 000000000..c06a9a0ad --- /dev/null +++ b/rma/__manifest__.py @@ -0,0 +1,37 @@ +# Copyright 2020 Tecnativa - Ernesto Tejeda +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +{ + "name": "Return Merchandise Authorization Management", + "summary": "Return Merchandise Authorization (RMA)", + "version": "12.0.1.0.0", + "development_status": "Beta", + "category": "RMA", + "website": "https://github.com/OCA/rma", + "author": "Tecnativa, Odoo Community Association (OCA)", + "maintainers": ["ernestotejeda"], + "license": "AGPL-3", + "depends": [ + "account", + "stock", + ], + "data": [ + "views/report_rma.xml", + "report/report.xml", + "data/mail_data.xml", + "data/rma_operation_data.xml", + "security/rma_security.xml", + "security/ir.model.access.csv", + "wizard/stock_picking_return_views.xml", + "wizard/rma_delivery_views.xml", + "wizard/rma_split_views.xml", + "views/menus.xml", + "views/res_partner_views.xml", + "views/rma_portal_templates.xml", + "views/rma_team_views.xml", + "views/rma_views.xml", + "views/stock_picking_views.xml", + "views/stock_warehouse_views.xml", + ], + 'post_init_hook': 'post_init_hook', + "application": True, +} diff --git a/rma/controllers/__init__.py b/rma/controllers/__init__.py new file mode 100644 index 000000000..f43232f01 --- /dev/null +++ b/rma/controllers/__init__.py @@ -0,0 +1,3 @@ +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from . import main diff --git a/rma/controllers/main.py b/rma/controllers/main.py new file mode 100644 index 000000000..f53942f1f --- /dev/null +++ b/rma/controllers/main.py @@ -0,0 +1,132 @@ +# Copyright 2020 Tecnativa - Ernesto Tejeda +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import _, exceptions, http +from odoo.exceptions import AccessError, MissingError +from odoo.http import request +from odoo.addons.portal.controllers.portal import CustomerPortal,\ + pager as portal_pager +from odoo.tools import consteq + + +class PortalRma(CustomerPortal): + + def _prepare_portal_layout_values(self): + values = super()._prepare_portal_layout_values() + values['rma_count'] = request.env['rma'].search_count([]) + return values + + def _rma_get_page_view_values(self, rma, access_token, **kwargs): + values = { + 'page_name': 'RMA', + 'rma': rma, + } + return self._get_page_view_values( + rma, access_token, values, 'my_rmas_history', False, **kwargs) + + def _get_filter_domain(self, kw): + return [] + + @http.route(['/my/rmas', '/my/rmas/page/'], + type='http', auth="user", website=True) + def portal_my_rmas(self, page=1, date_begin=None, date_end=None, + sortby=None, **kw): + values = self._prepare_portal_layout_values() + rma_obj = request.env['rma'] + domain = self._get_filter_domain(kw) + searchbar_sortings = { + 'date': {'label': _('Date'), 'order': 'date desc'}, + 'name': {'label': _('Name'), 'order': 'name desc'}, + 'state': {'label': _('Status'), 'order': 'state'}, + } + # default sort by order + if not sortby: + sortby = 'date' + order = searchbar_sortings[sortby]['order'] + archive_groups = self._get_archive_groups('rma', domain) + if date_begin and date_end: + domain += [ + ('create_date', '>', date_begin), + ('create_date', '<=', date_end), + ] + # count for pager + rma_count = rma_obj.search_count(domain) + # pager + pager = portal_pager( + url="/my/rmas", + url_args={ + 'date_begin': date_begin, + 'date_end': date_end, + 'sortby': sortby, + }, + total=rma_count, + page=page, + step=self._items_per_page + ) + # content according to pager and archive selected + rmas = rma_obj.search( + domain, + order=order, + limit=self._items_per_page, + offset=pager['offset'] + ) + request.session['my_rmas_history'] = rmas.ids[:100] + values.update({ + 'date': date_begin, + 'rmas': rmas, + 'page_name': 'RMA', + 'pager': pager, + 'archive_groups': archive_groups, + 'default_url': '/my/rmas', + 'searchbar_sortings': searchbar_sortings, + 'sortby': sortby, + }) + return request.render("rma.portal_my_rmas", values) + + @http.route(['/my/rmas/'], + type='http', auth="public", website=True) + def portal_my_rma_detail(self, rma_id, access_token=None, + report_type=None, download=False, **kw): + try: + rma_sudo = self._document_check_access('rma', rma_id, access_token) + except (AccessError, MissingError): + return request.redirect('/my') + if report_type in ('html', 'pdf', 'text'): + return self._show_report( + model=rma_sudo, + report_type=report_type, + report_ref='rma.report_rma_action', + download=download, + ) + + values = self._rma_get_page_view_values(rma_sudo, access_token, **kw) + return request.render("rma.portal_rma_page", values) + + @http.route(['/my/rma/picking/pdf/'], + type='http', auth="public", website=True) + def portal_my_rma_picking_report(self, picking_id, access_token=None, + **kw): + try: + picking_sudo = self._stock_picking_check_access( + picking_id, access_token=access_token) + except exceptions.AccessError: + return request.redirect('/my') + report_sudo = request.env.ref('stock.action_report_delivery').sudo() + pdf = report_sudo.render_qweb_pdf([picking_sudo.id])[0] + pdfhttpheaders = [ + ('Content-Type', 'application/pdf'), + ('Content-Length', len(pdf)), + ] + return request.make_response(pdf, headers=pdfhttpheaders) + + def _stock_picking_check_access(self, picking_id, access_token=None): + picking = request.env['stock.picking'].browse([picking_id]) + picking_sudo = picking.sudo() + try: + picking.check_access_rights('read') + picking.check_access_rule('read') + except exceptions.AccessError: + if not access_token or not consteq( + picking_sudo.sale_id.access_token, access_token): + raise + return picking_sudo diff --git a/rma/data/mail_data.xml b/rma/data/mail_data.xml new file mode 100644 index 000000000..207747bc6 --- /dev/null +++ b/rma/data/mail_data.xml @@ -0,0 +1,46 @@ + + + + + Draft RMA + rma + + New RMA in draft state + + + + Draft RMA + 10 + rma.team + + + team_id + + + + RMA Notification + + ${object.user_id.email_formatted |safe} + ${object.partner_id.id} + ${object.company_id.name} RMA (Ref ${object.name or 'n/a' }) + + ${(object.name or '')} + ${object.partner_id.lang} + + + +
+

+ Dear ${object.partner_id.name} + % if object.partner_id.parent_id: + (${object.partner_id.parent_id.name}) + % endif +

+ Here is the RMA ${object.name} from ${object.company_id.name}. +

+ Do not hesitate to contact us if you have any question. +

+
+
+
+
diff --git a/rma/data/rma_operation_data.xml b/rma/data/rma_operation_data.xml new file mode 100644 index 000000000..60ea6e044 --- /dev/null +++ b/rma/data/rma_operation_data.xml @@ -0,0 +1,12 @@ + + + + Replace + + + Repair + + + Refund + + diff --git a/rma/hooks.py b/rma/hooks.py new file mode 100644 index 000000000..f5e24d848 --- /dev/null +++ b/rma/hooks.py @@ -0,0 +1,70 @@ +# Copyright 2020 Tecnativa - Ernesto Tejeda +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import api, SUPERUSER_ID + + +def post_init_hook(cr, registry): + env = api.Environment(cr, SUPERUSER_ID, {}) + + def _get_next_picking_type_color(): + """ Choose the next available color for the operation types.""" + stock_picking_type = env['stock.picking.type'] + picking_type = stock_picking_type.search_read( + [('warehouse_id', '!=', False), ('color', '!=', False)], + ['color'], + order='color', + ) + all_used_colors = [res['color'] for res in picking_type] + available_colors = [color for color in range(0, 12) + if color not in all_used_colors] + return available_colors[0] if available_colors else 0 + + def create_rma_locations(warehouse): + stock_location = env['stock.location'] + location_vals = warehouse._get_locations_values({}) + for field_name, values in location_vals.items(): + if field_name == 'rma_loc_id' and not warehouse.rma_loc_id: + warehouse.rma_loc_id = stock_location.with_context( + active_test=False).create(values).id + + def create_rma_picking_types(whs): + ir_sequence_sudo = env['ir.sequence'].sudo() + stock_picking_type = env['stock.picking.type'] + color = _get_next_picking_type_color() + stock_picking = stock_picking_type.search( + [('sequence', '!=', False)], limit=1, order='sequence desc') + max_sequence = stock_picking.sequence or 0 + create_data = whs._get_picking_type_create_values(max_sequence)[0] + sequence_data = whs._get_sequence_values() + data = {} + for picking_type, values in create_data.items(): + if (picking_type in ['rma_in_type_id', 'rma_out_type_id'] + and not whs[picking_type]): + picking_sequence = sequence_data[picking_type] + sequence = ir_sequence_sudo.create(picking_sequence) + values.update( + warehouse_id=whs.id, + color=color, + sequence_id=sequence.id, + ) + data[picking_type] = stock_picking_type.create(values).id + + rma_out_type = stock_picking_type.browse(data['rma_out_type_id']) + rma_out_type.write({ + 'return_picking_type_id': data.get('rma_in_type_id', False) + }) + rma_in_type = stock_picking_type.browse(data['rma_in_type_id']) + rma_in_type.write({ + 'return_picking_type_id': data.get('rma_out_type_id', False) + }) + whs.write(data) + + # Create rma locations and picking types + warehouses = env['stock.warehouse'].search([]) + for warehouse in warehouses: + create_rma_locations(warehouse) + create_rma_picking_types(warehouse) + # Create rma sequence per company + for company in env['res.company'].search([]): + company.create_rma_index() diff --git a/rma/i18n/es.po b/rma/i18n/es.po new file mode 100644 index 000000000..242403e8d --- /dev/null +++ b/rma/i18n/es.po @@ -0,0 +1,1635 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * rma +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 12.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2020-06-21 05:13+0000\n" +"PO-Revision-Date: 2020-06-21 01:16-0400\n" +"Last-Translator: <>\n" +"Language-Team: \n" +"Language: es\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: \n" +"X-Generator: Poedit 2.0.6\n" + +#. module: rma +#: model:mail.template,report_name:rma.mail_template_rma_notification +msgid "${(object.name or '')}" +msgstr "" + +#. module: rma +#: model:mail.template,subject:rma.mail_template_rma_notification +msgid "${object.company_id.name} RMA (Ref ${object.name or 'n/a' })" +msgstr "" + +#. module: rma +#: code:addons/rma/models/rma_team.py:43 +#, python-format +msgid "%s (copy)" +msgstr "%s (copia)" + +#. module: rma +#: model:mail.template,body_html:rma.mail_template_rma_notification +msgid "" +"
\n" +"

\n" +" Dear ${object.partner_id.name}\n" +" % if object.partner_id.parent_id:\n" +" (${object.partner_id.parent_id.name})\n" +" % endif\n" +"

\n" +" Here is the RMA ${object.name} from ${object.company_id." +"name}.\n" +"

\n" +" Do not hesitate to contact us if you have any question.\n" +"

\n" +"
\n" +" " +msgstr "" +"
\n" +"

\n" +" Estimado ${object.partner_id.name}\n" +" % if object.partner_id.parent_id:\n" +" (${object.partner_id.parent_id.name})\n" +" % endif\n" +"

\n" +" Aquí tiene el RMA ${object.name} Desde ${object." +"company_id.name}.\n" +"

\n" +" No dude en ponerse en contacto con nosotros si tiene alguna pregunta.\n" +"

\n" +"
\n" +" " + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.portal_rma_page +msgid "" +"" +msgstr "" +"" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.portal_rma_page +msgid " Paid" +msgstr "Pagado" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.portal_rma_page +msgid " Waiting Payment" +msgstr "Esperando Pago" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.portal_rma_page +msgid "" +"" +msgstr "" +"" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.portal_rma_page +msgid "" +"" +msgstr "" +"" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.portal_rma_page +msgid "" +" Cancelled" +msgstr "" +"Cancelado" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.portal_rma_page +msgid "" +" Preparation" +msgstr "" +"Preparación" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.portal_rma_page +msgid "" +" Shipped" +msgstr "" +" Enviado" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.portal_rma_page +msgid "" +" Partially Available" +msgstr "" +"Disponible parcialmente" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.portal_rma_page +msgid "Delivery" +msgstr "Órdenes de entrega" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.portal_rma_page +msgid "Reception" +msgstr "Recepción" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.portal_rma_page +msgid "Refund" +msgstr "Factura rectificativa" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.portal_rma_page +msgid "Customer:" +msgstr "Cliente:" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.report_rma_document +msgid "Date:" +msgstr "Fecha:" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.report_rma_document +msgid "Deadline:" +msgstr "Fecha límite:" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.portal_rma_page +msgid "Deadline" +msgstr "Fecha límite:" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.report_rma_document +msgid "Delivered qty:" +msgstr "Cantidad entregada:" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.portal_rma_page +msgid "Delivered quantity" +msgstr "Cantidad entregada" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.report_rma_document +msgid "Invoicing address:" +msgstr "Dirección de facturación:" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.report_rma_document +msgid "Move:" +msgstr "Movimiento:" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.report_rma_document +msgid "Origin delivery:" +msgstr "Orden de Entrega:" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.portal_rma_page +#, fuzzy +#| msgid "Origin delivery:" +msgid "Origin delivery" +msgstr "Orden de Entrega:" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.report_rma_document +msgid "Origin:" +msgstr "Referencia:" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.portal_rma_page +msgid "Origin" +msgstr "Referencia:" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.report_rma_document +msgid "Product:" +msgstr "Producto:" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.portal_rma_page +msgid "Product" +msgstr "Producto" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.report_rma_document +msgid "Quantity:" +msgstr "Cantidad:" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.portal_rma_page +msgid "Quantity" +msgstr "Cantidad" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.portal_rma_page +msgid "RMA Date" +msgstr "Fecha" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.report_rma_document +msgid "RMA Note:" +msgstr "Nota:" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.portal_rma_page +#: model_terms:ir.ui.view,arch_db:rma.report_rma_document +msgid "Responsible:" +msgstr "Responsable:" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.report_rma_document +msgid "State:" +msgstr "Estado:" + +#. module: rma +#: model:ir.model.fields,help:rma.field_rma_team__alias_defaults +msgid "" +"A Python dictionary that will be evaluated to provide default values when " +"creating new records for this alias." +msgstr "" +"Diccionario Python a evaluar para proporcionar valores por defecto cuando un " +"nuevo registro se cree para este seudónimo." + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.rma_team_view_form +msgid "Accept Emails From" +msgstr "Aceptar los correos electrónicos de" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__access_warning +msgid "Access warning" +msgstr "Alerta de acceso" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__message_needaction +#: model:ir.model.fields,field_description:rma.field_rma_team__message_needaction +msgid "Action Needed" +msgstr "Acción Necesaria" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma_team__active +msgid "Active" +msgstr "Activo" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__activity_ids +msgid "Activities" +msgstr "Actividades" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__activity_state +msgid "Activity State" +msgstr "Estado de la actividad" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma_team__alias_id +msgid "Alias" +msgstr "Seudónimo" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma_team__alias_contact +msgid "Alias Contact Security" +msgstr "Seudónimo del contacto de seguridad" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma_team__alias_name +msgid "Alias Name" +msgstr "Seudónimo" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma_team__alias_domain +msgid "Alias domain" +msgstr "Seudónimo del dominio" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma_team__alias_model_id +msgid "Aliased Model" +msgstr "Modelo con seudónimo" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.rma_view_form +msgid "Are you sure you want to cancel this RMA" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__message_attachment_count +#: model:ir.model.fields,field_description:rma.field_rma_team__message_attachment_count +msgid "Attachment Count" +msgstr "Conteo de archivos adjuntos" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.rma_team_view_form +msgid "Avatar" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__can_be_locked +msgid "Can Be Locked" +msgstr "Puede ser bloquedo" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__can_be_refunded +msgid "Can Be Refunded" +msgstr "Puede ser reembolsado" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__can_be_replaced +msgid "Can Be Replaced" +msgstr "Puede ser reemplazado" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__can_be_returned +msgid "Can Be Returned" +msgstr "Puede ser devuelto" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__can_be_split +msgid "Can Be Split" +msgstr "Puede ser dividido" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.rma_redelivery_wizard_view_form +#: model_terms:ir.ui.view,arch_db:rma.rma_split_wizard_view_form2 +#: model_terms:ir.ui.view,arch_db:rma.rma_view_form +msgid "Cancel" +msgstr "Cancelar" + +#. module: rma +#: selection:rma,state:0 +msgid "Canceled" +msgstr "Cancelado" + +#. module: rma +#: model_terms:ir.actions.act_window,help:rma.rma_action +#: model_terms:ir.actions.act_window,help:rma.rma_team_action +msgid "Click to add a new RMA." +msgstr "Click para agregar un nuevo RMA." + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__commercial_partner_id +msgid "Commercial Entity" +msgstr "Entidad comercial" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.portal_rma_page +msgid "Communication" +msgstr "Comunicación" + +#. module: rma +#: model:ir.model,name:rma.model_res_company +msgid "Companies" +msgstr "Compañías" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__company_id +#: model:ir.model.fields,field_description:rma.field_rma_team__company_id +msgid "Company" +msgstr "Compañía" + +#. module: rma +#: model:ir.ui.menu,name:rma.rma_configuration_menu +msgid "Configuration" +msgstr "Configuración" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.rma_view_form +msgid "Confirm" +msgstr "Confirmar" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.rma_view_search selection:rma,state:0 +msgid "Confirmed" +msgstr "Confirmado" + +#. module: rma +#: model:ir.model,name:rma.model_res_partner +#: model_terms:ir.ui.view,arch_db:rma.portal_rma_page +msgid "Contact" +msgstr "Contacto" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_stock_return_picking__create_rma +msgid "Create RMAs" +msgstr "Crear RMAs" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__create_uid +#: model:ir.model.fields,field_description:rma.field_rma_delivery_wizard__create_uid +#: model:ir.model.fields,field_description:rma.field_rma_operation__create_uid +#: model:ir.model.fields,field_description:rma.field_rma_split_wizard__create_uid +#: model:ir.model.fields,field_description:rma.field_rma_team__create_uid +msgid "Created by" +msgstr "Creado por" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__create_date +#: model:ir.model.fields,field_description:rma.field_rma_delivery_wizard__create_date +#: model:ir.model.fields,field_description:rma.field_rma_operation__create_date +#: model:ir.model.fields,field_description:rma.field_rma_split_wizard__create_date +#: model:ir.model.fields,field_description:rma.field_rma_team__create_date +msgid "Created on" +msgstr "Creado el" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__partner_id +msgid "Customer" +msgstr "Cliente" + +#. module: rma +#: model:ir.model.fields,help:rma.field_rma__access_url +msgid "Customer Portal URL" +msgstr "URL del portal de cliente" + +#. module: rma +#: code:addons/rma/controllers/main.py:38 +#: model:ir.model.fields,field_description:rma.field_rma__date +#: model_terms:ir.ui.view,arch_db:rma.portal_my_rmas +#: model_terms:ir.ui.view,arch_db:rma.rma_view_search +#, python-format +msgid "Date" +msgstr "Fecha" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.portal_rma_page +msgid "Date:" +msgstr "Fecha:" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__deadline +#: model_terms:ir.ui.view,arch_db:rma.rma_view_search +msgid "Deadline" +msgstr "Fecha límite" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma_team__alias_defaults +msgid "Default Values" +msgstr "Valores por defecto" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.rma_redelivery_wizard_view_form +msgid "Deliver" +msgstr "Entregar" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__delivered_qty +msgid "Delivered qty" +msgstr "Ctd. entregada" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__delivered_qty_done +msgid "Delivered qty done" +msgstr "Ctd. entregada realizada" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.rma_view_form +msgid "Delivery" +msgstr "Entrega" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__delivery_picking_count +msgid "Delivery count" +msgstr "Cantidad de entregas" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__delivery_move_ids +msgid "Delivery reservation" +msgstr "Movimientos de entrega" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__description +#: model_terms:ir.ui.view,arch_db:rma.portal_rma_page +msgid "Description" +msgstr "Descripción" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__display_name +#: model:ir.model.fields,field_description:rma.field_rma_delivery_wizard__display_name +#: model:ir.model.fields,field_description:rma.field_rma_operation__display_name +#: model:ir.model.fields,field_description:rma.field_rma_split_wizard__display_name +#: model:ir.model.fields,field_description:rma.field_rma_team__display_name +msgid "Display Name" +msgstr "Nombre mostrado" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.rma_view_search selection:rma,state:0 +msgid "Draft" +msgstr "Borrador" + +#. module: rma +#: model:mail.message.subtype,name:rma.mt_rma_draft +#: model:mail.message.subtype,name:rma.mt_rma_team_rma_draft +msgid "Draft RMA" +msgstr "RMA en estado Borrador" + +#. module: rma +#: code:addons/rma/models/rma.py:1076 +#, python-format +msgid "" +"E-mail subject: %s\n" +"\n" +"E-mail body:\n" +"%s" +msgstr "" +"Asunto del correo electrónico: %s\n" +"\n" +"Cuerpo del correo electrónico:\n" +"%s" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.rma_team_view_form +msgid "Email" +msgstr "Correo electrónico" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.rma_team_view_form +msgid "Email Alias" +msgstr "Pseudónimo de correo" + +#. module: rma +#: code:addons/rma/wizard/rma_split.py:63 +#, python-format +msgid "Extracted RMA" +msgstr "RMA Extraído" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__origin_split_rma_id +msgid "Extracted from" +msgstr "Extraído de" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__message_follower_ids +#: model:ir.model.fields,field_description:rma.field_rma_team__message_follower_ids +msgid "Followers" +msgstr "Seguidores" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__message_channel_ids +#: model:ir.model.fields,field_description:rma.field_rma_team__message_channel_ids +msgid "Followers (Channels)" +msgstr "Seguidores (Canales)" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__message_partner_ids +#: model:ir.model.fields,field_description:rma.field_rma_team__message_partner_ids +msgid "Followers (Partners)" +msgstr "Seguidores (Empresas)" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.rma_view_search +msgid "Group By" +msgstr "Agrupar por" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__id +#: model:ir.model.fields,field_description:rma.field_rma_delivery_wizard__id +#: model:ir.model.fields,field_description:rma.field_rma_operation__id +#: model:ir.model.fields,field_description:rma.field_rma_split_wizard__id +#: model:ir.model.fields,field_description:rma.field_rma_team__id +msgid "ID" +msgstr "" + +#. module: rma +#: model:ir.model.fields,help:rma.field_rma_team__alias_parent_thread_id +msgid "" +"ID of the parent record holding the alias (example: project holding the task " +"creation alias)" +msgstr "" +"ID del registro padre que tiene el seudónimo. (ejemplo: el proyecto que " +"contiene el seudónimo para la creación de tareas)" + +#. module: rma +#: model:ir.model.fields,help:rma.field_rma__message_unread +#: model:ir.model.fields,help:rma.field_rma_team__message_unread +msgid "If checked new messages require your attention." +msgstr "Si está marcado, hay nuevos mensajes que requieren su atención" + +#. module: rma +#: model:ir.model.fields,help:rma.field_rma__message_needaction +#: model:ir.model.fields,help:rma.field_rma_team__message_needaction +msgid "If checked, new messages require your attention." +msgstr "Si está marcado, hay nuevos mensajes que requieren su atención." + +#. module: rma +#: model:ir.model.fields,help:rma.field_rma__message_has_error +#: model:ir.model.fields,help:rma.field_rma_team__message_has_error +msgid "If checked, some messages have a delivery error." +msgstr "Si se encuentra marcado, algunos mensajes tienen error de envío." + +#. module: rma +#: model:ir.model.fields,help:rma.field_rma_team__active +msgid "" +"If the active field is set to false, it will allow you to hide the RMA Team " +"without removing it." +msgstr "" +"Si el campo activo se establece a Falso, permitirá ocultar El equipo de RMA " +"sin eliminarlo." + +#. module: rma +#: code:addons/rma/models/rma.py:1080 +#, python-format +msgid "Incoming e-mail" +msgstr "Correo electrónico entrante" + +#. module: rma +#: model:ir.model,name:rma.model_account_invoice +msgid "Invoice" +msgstr "Factura" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__partner_invoice_id +msgid "Invoice Address" +msgstr "Dirección de factura" + +#. module: rma +#: model:ir.model,name:rma.model_account_invoice_line +msgid "Invoice Line" +msgstr "Linea de factura" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__message_is_follower +#: model:ir.model.fields,field_description:rma.field_rma_team__message_is_follower +msgid "Is Follower" +msgstr "Es un seguidor" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma____last_update +#: model:ir.model.fields,field_description:rma.field_rma_delivery_wizard____last_update +#: model:ir.model.fields,field_description:rma.field_rma_operation____last_update +#: model:ir.model.fields,field_description:rma.field_rma_split_wizard____last_update +#: model:ir.model.fields,field_description:rma.field_rma_team____last_update +msgid "Last Modified on" +msgstr "Última modificación en" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__write_uid +#: model:ir.model.fields,field_description:rma.field_rma_delivery_wizard__write_uid +#: model:ir.model.fields,field_description:rma.field_rma_operation__write_uid +#: model:ir.model.fields,field_description:rma.field_rma_split_wizard__write_uid +#: model:ir.model.fields,field_description:rma.field_rma_team__write_uid +msgid "Last Updated by" +msgstr "Última actualización por" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__write_date +#: model:ir.model.fields,field_description:rma.field_rma_delivery_wizard__write_date +#: model:ir.model.fields,field_description:rma.field_rma_operation__write_date +#: model:ir.model.fields,field_description:rma.field_rma_split_wizard__write_date +#: model:ir.model.fields,field_description:rma.field_rma_team__write_date +msgid "Last Updated on" +msgstr "Última actualización el" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__location_id +msgid "Location" +msgstr "Ubicación" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.rma_view_form +msgid "Lock" +msgstr "Bloquear" + +#. module: rma +#: selection:rma,state:0 +msgid "Locked" +msgstr "Bloqueado" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__message_main_attachment_id +#: model:ir.model.fields,field_description:rma.field_rma_team__message_main_attachment_id +msgid "Main Attachment" +msgstr "Adjuntos principales" + +#. module: rma +#: model:ir.module.category,description:rma.rma_module_category +msgid "Manage Return Merchandise Authorizations (RMAs)." +msgstr "Autorización de Devolución de Mercancía (RMA)." + +#. module: rma +#: model:res.groups,name:rma.rma_group_manager +msgid "Manager" +msgstr "Responsable" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__message_has_error +#: model:ir.model.fields,field_description:rma.field_rma_team__message_has_error +msgid "Message Delivery error" +msgstr "Error de Envío de Mensaje" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__message_ids +#: model:ir.model.fields,field_description:rma.field_rma_team__message_ids +msgid "Messages" +msgstr "Mensajes" + +#. module: rma +#: code:addons/rma/controllers/main.py:39 +#: model:ir.model.fields,field_description:rma.field_rma__name +#: model:ir.model.fields,field_description:rma.field_rma_operation__name +#: model:ir.model.fields,field_description:rma.field_rma_team__name +#, python-format +msgid "Name" +msgstr "Nombre" + +#. module: rma +#: code:addons/rma/models/rma.py:31 code:addons/rma/models/rma.py:496 +#: code:addons/rma/models/rma.py:1079 +#, python-format +msgid "New" +msgstr "Nuevo" + +#. module: rma +#: model:mail.message.subtype,description:rma.mt_rma_draft +msgid "New RMA in draft state" +msgstr "Nuevo RMA en estado Borrador" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__activity_date_deadline +msgid "Next Activity Deadline" +msgstr "Siguiente plazo de actividad" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__activity_summary +msgid "Next Activity Summary" +msgstr "Resumen de la siguiente actividad" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__activity_type_id +msgid "Next Activity Type" +msgstr "Siguiente tipo de actividad" + +#. module: rma +#: code:addons/rma/models/rma.py:757 +#, python-format +msgid "None of the selected RMAs can perform a replacement." +msgstr "Ninguno de los RMAs seleccionados puede realizar un reemplazo." + +#. module: rma +#: code:addons/rma/models/rma.py:740 +#, python-format +msgid "None of the selected RMAs can perform a return." +msgstr "Ninguno de los RMAs seleccionados puede realizar una devolución." + +#. module: rma +#: selection:rma,priority:0 +msgid "Normal" +msgstr "" + +#. module: rma +#: selection:rma,priority:0 +msgid "Not urgent" +msgstr "No Urgente" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__message_needaction_counter +#: model:ir.model.fields,field_description:rma.field_rma_team__message_needaction_counter +msgid "Number of Actions" +msgstr "Número de acciones" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__message_has_error_counter +#: model:ir.model.fields,field_description:rma.field_rma_team__message_has_error_counter +msgid "Number of error" +msgstr "Número de error" + +#. module: rma +#: model:ir.model.fields,help:rma.field_rma__message_needaction_counter +#: model:ir.model.fields,help:rma.field_rma_team__message_needaction_counter +msgid "Number of messages which requires an action" +msgstr "Número de mensajes que requieren una acción" + +#. module: rma +#: model:ir.model.fields,help:rma.field_rma__message_has_error_counter +#: model:ir.model.fields,help:rma.field_rma_team__message_has_error_counter +msgid "Number of messages with delivery error" +msgstr "Número de mensajes con error de envío" + +#. module: rma +#: model:ir.model.fields,help:rma.field_rma__message_unread_counter +#: model:ir.model.fields,help:rma.field_rma_team__message_unread_counter +msgid "Number of unread messages" +msgstr "Número de mensajes no leidos" + +#. module: rma +#: model:ir.model.fields,help:rma.field_rma_team__alias_force_thread_id +msgid "" +"Optional ID of a thread (record) to which all incoming messages will be " +"attached, even if they did not reply to it. If set, this will disable the " +"creation of new records completely." +msgstr "" +"Id. opcional de un hilo (registro) al que todos los mensajes entrantes serán " +"adjuntados, incluso si no fueron respuestas del mismo. Si se establece, se " +"deshabilitará completamente la creación de nuevos registros." + +#. module: rma +#: model:ir.ui.menu,name:rma.rma_orders_menu +msgid "Orders" +msgstr "Órdenes" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__picking_id +msgid "Origin Delivery" +msgstr "Orden de Entrega" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__move_id +msgid "Origin move" +msgstr "Movimiento" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.rma_view_form +msgid "Other Information" +msgstr "Otra información" + +#. module: rma +#: selection:rma,activity_state:0 +msgid "Overdue" +msgstr "Vencidas" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma_team__alias_user_id +msgid "Owner" +msgstr "Propietario" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma_team__alias_parent_model_id +msgid "Parent Model" +msgstr "Modelo padre" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma_team__alias_parent_thread_id +msgid "Parent Record Thread ID" +msgstr "ID del hilo del registro padre" + +#. module: rma +#: model:ir.model.fields,help:rma.field_rma_team__alias_parent_model_id +msgid "" +"Parent model holding the alias. The model holding the alias reference is not " +"necessarily the model given by alias_model_id (example: project " +"(parent_model) and task (model))" +msgstr "" +"Modelo padre que contiene el alias. El modelo que contiene la referencia " +"alias no es necesariamente el modelo dado por alias_model_id" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.rma_view_search +msgid "Partner" +msgstr "Empresa" + +#. module: rma +#: selection:rma,activity_state:0 +msgid "Planned" +msgstr "Planeado" + +#. module: rma +#: model:ir.model.fields,help:rma.field_rma_team__alias_contact +msgid "" +"Policy to post a message on the document using the mailgateway.\n" +"- everyone: everyone can post\n" +"- partners: only authenticated partners\n" +"- followers: only followers of the related document or members of following " +"channels\n" +msgstr "" +"Política para publicar un mensaje en el documento utilizando el servidor de " +"correo.\n" +"- todo el mundo: todos pueden publicar\n" +"- socios: sólo socios autenticados\n" +"- seguidores: sólo seguidores del documento relacionado o miembros de los " +"siguientes canales\n" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__access_url +msgid "Portal Access URL" +msgstr "URL de acceso al portal" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.rma_view_form +msgid "Preview" +msgstr "Previsualizar" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__priority +msgid "Priority" +msgstr "Prioridad" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__procurement_group_id +msgid "Procurement group" +msgstr "Grupo de abastecimiento" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__product_id +#: model_terms:ir.ui.view,arch_db:rma.portal_my_rmas +msgid "Product" +msgstr "Producto" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma_delivery_wizard__product_uom_qty +msgid "Product qty" +msgstr "Cantidad" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__product_uom_qty +#: model_terms:ir.ui.view,arch_db:rma.portal_my_rmas +msgid "Quantity" +msgstr "Cantidad" + +#. module: rma +#: code:addons/rma/wizard/rma_delivery.py:49 sql_constraint:rma.split.wizard:0 +#, python-format +msgid "Quantity must be greater than 0." +msgstr "La cantidad debe ser mayor que cero." + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma_split_wizard__product_uom_qty +msgid "Quantity to extract" +msgstr "Cantidad a extraer" + +#. module: rma +#: code:addons/rma/models/rma.py:790 +#, python-format +msgid "" +"Quantity to extract cannot be greater than remaining delivery quantity (%s " +"%s)" +msgstr "" +"La cantidad a extraer no puede ser mayor que la cantidad de entrega " +"restante(%s %s)" + +#. module: rma +#: model:ir.model.fields,help:rma.field_rma_split_wizard__product_uom_qty +msgid "Quantity to extract to a new RMA." +msgstr "Cantidad a extraer en nuevo RMA" + +#. module: rma +#: model:ir.actions.act_window,name:rma.rma_action +#: model:ir.model,name:rma.model_rma +#: model:ir.model.fields,field_description:rma.field_account_invoice_line__rma_id +#: model:ir.model.fields,field_description:rma.field_rma_split_wizard__rma_id +#: model:ir.model.fields,field_description:rma.field_stock_warehouse__rma +#: model:ir.module.category,name:rma.rma_module_category +#: model:ir.ui.menu,name:rma.rma_menu +#: model_terms:ir.ui.view,arch_db:rma.view_partner_form +#: model_terms:ir.ui.view,arch_db:rma.view_picking_form +msgid "RMA" +msgstr "" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.portal_my_rmas +#: model_terms:ir.ui.view,arch_db:rma.report_rma_document +msgid "RMA #" +msgstr "" + +#. module: rma +#: code:addons/rma/models/res_company.py:18 +#, python-format +msgid "RMA Code" +msgstr "Código de RMA" + +#. module: rma +#: code:addons/rma/models/stock_warehouse.py:82 +#, python-format +msgid "RMA Delivery Orders" +msgstr "Órdenes de entrega de RMA" + +#. module: rma +#: model:ir.model,name:rma.model_rma_delivery_wizard +msgid "RMA Delivery Wizard" +msgstr "Asistente de entrega de RMA" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_stock_warehouse__rma_in_type_id +msgid "RMA In Type" +msgstr "Tipo de operación para recepción de RMA" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_stock_warehouse__rma_loc_id +msgid "RMA Location" +msgstr "Ubicación de RMA" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.portal_rma_page +msgid "RMA Order -" +msgstr "Orden de RMA -" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.portal_my_home_menu_rma +#: model_terms:ir.ui.view,arch_db:rma.portal_my_home_rma +#: model_terms:ir.ui.view,arch_db:rma.portal_my_rmas +msgid "RMA Orders" +msgstr "Órdenes de RMA" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_stock_warehouse__rma_out_type_id +msgid "RMA Out Type" +msgstr "Tipo de operación para entrega de RMA" + +#. module: rma +#: code:addons/rma/models/stock_warehouse.py:73 +#, python-format +msgid "RMA Receipts" +msgstr "Recepciones de RMA" + +#. module: rma +#: model:ir.actions.report,name:rma.report_rma_action +msgid "RMA Report" +msgstr "Reporte de RMA" + +#. module: rma +#: model:ir.model,name:rma.model_rma_split_wizard +msgid "RMA Split Wizard" +msgstr "Asistente para dividir RMA" + +#. module: rma +#: model:ir.model,name:rma.model_rma_team +#: model:ir.model.fields,field_description:rma.field_res_users__rma_team_id +#: model:ir.ui.menu,name:rma.rma_configuration_rma_team_menu +#: model_terms:ir.ui.view,arch_db:rma.rma_team_view_form +msgid "RMA Team" +msgstr "Equipo de RMA" + +#. module: rma +#: model:ir.model.fields,help:rma.field_res_users__rma_team_id +msgid "RMA Team the user is member of." +msgstr "Equipo de RMA del cual el usuario es miembro." + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_res_partner__rma_count +#: model:ir.model.fields,field_description:rma.field_res_users__rma_count +#: model:ir.model.fields,field_description:rma.field_stock_picking__rma_count +msgid "RMA count" +msgstr "Cantidad de RMAs" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_stock_move__rma_receiver_ids +msgid "RMA receivers" +msgstr "RMAs que originaron esta orden" + +#. module: rma +#: model:ir.model.fields,help:rma.field_stock_warehouse__rma +msgid "RMA related products can be stored in this warehouse." +msgstr "" + +#. module: rma +#: model:ir.model,name:rma.model_rma_operation +msgid "RMA requested operation" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_stock_move__rma_id +msgid "RMA return" +msgstr "RMA que realizó esta devolución" + +#. module: rma +#: model:ir.actions.act_window,name:rma.rma_team_action +#: model:ir.model.fields,field_description:rma.field_rma__team_id +msgid "RMA team" +msgstr "Equipo de RMA" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_res_partner__rma_ids +#: model:ir.model.fields,field_description:rma.field_res_users__rma_ids +#: model:ir.model.fields,field_description:rma.field_stock_move__rma_ids +msgid "RMAs" +msgstr "" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.rma_view_form +msgid "Receipt" +msgstr "Recepción" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.rma_view_search selection:rma,state:0 +msgid "Received" +msgstr "Recibido" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__reception_move_id +msgid "Reception move" +msgstr "Movimiento de recepción" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma_team__alias_force_thread_id +msgid "Record Thread ID" +msgstr "Id. del hilo de registro" + +#. module: rma +#: model:ir.model.fields,help:rma.field_rma__origin +msgid "Reference of the document that generated this RMA." +msgstr "Referencia al documento que generó este RMA." + +#. module: rma +#: code:addons/rma/models/rma.py:679 +#: model:ir.model.fields,field_description:rma.field_rma__refund_id +#: model_terms:ir.ui.view,arch_db:rma.rma_view_form +#: model:rma.operation,name:rma.rma_operation_refund +#, python-format +msgid "Refund" +msgstr "Factura rectificativa" + +#. module: rma +#: model:ir.model.fields,help:rma.field_rma__partner_invoice_id +msgid "Refund address for current RMA." +msgstr "Dirección de facturación de este RMA." + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__refund_line_id +msgid "Refund line" +msgstr "Línea de factura rectificativa" + +#. module: rma +#: selection:rma,state:0 +msgid "Refunded" +msgstr "reembolsado" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__remaining_qty +msgid "Remaining delivered qty" +msgstr "Ctd. entregada restante" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__remaining_qty_to_done +msgid "Remaining delivered qty to done" +msgstr "Ctd. entregada restante por realizar" + +#. module: rma +#: model:rma.operation,name:rma.rma_operation_return +msgid "Repair" +msgstr "" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.rma_view_form +#: selection:rma.delivery.wizard,type:0 +#: model:rma.operation,name:rma.rma_operation_replace +msgid "Replace" +msgstr "Reemplazar" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma_delivery_wizard__product_id +msgid "Replace Product" +msgstr "Reemplazar producto" + +#. module: rma +#: selection:rma,state:0 +msgid "Replaced" +msgstr "Reemplazado" + +#. module: rma +#: code:addons/rma/models/rma.py:995 +#, python-format +msgid "" +"Replacement: Move %s (Picking %s) has been created." +msgstr "" +"Reemplazo: El movimiento %s (Orden de entrega %s) ha sido creado." + +#. module: rma +#: code:addons/rma/models/rma.py:1006 +#, python-format +msgid "" +"Replacement:
Product %s
Quantity %f %s
This replacement did not " +"create a new move, but one of the previously created moves was updated with " +"this data." +msgstr "" +"Reemplazo:
Producto %s
Cantidad %f %s
El reemplazo realizado no creó un " +"movimiento nuevo, pero uno de los movimientos creados anteriormente fué " +"actualizado con estos datos." + +#. module: rma +#: model:ir.ui.menu,name:rma.rma_reporting_menu +msgid "Reporting" +msgstr "Informes" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__operation_id +#, fuzzy +#| msgid "Type of Operation" +msgid "Requested operation" +msgstr "Tipo de operación" + +#. module: rma +#: code:addons/rma/models/rma.py:721 +#, python-format +msgid "Required field(s):%s" +msgstr "Campo(s) requerido(s):%s" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__user_id +#: model_terms:ir.ui.view,arch_db:rma.rma_view_search +msgid "Responsible" +msgstr "Responsable" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__activity_user_id +msgid "Responsible User" +msgstr "Usuario responsable" + +#. module: rma +#: model:ir.model,name:rma.model_stock_return_picking +msgid "Return Picking" +msgstr "Albarán de devolución" + +#. module: rma +#: model:ir.actions.act_window,name:rma.rma_delivery_wizard_action +#: model_terms:ir.ui.view,arch_db:rma.rma_view_form +#: selection:rma.delivery.wizard,type:0 +msgid "Return to customer" +msgstr "Devolver al cliente" + +#. module: rma +#: code:addons/rma/models/rma.py:956 +#, python-format +msgid "" +"Return: %s has been created." +msgstr "" +"Devolución: La orden de entrega %s ha sido creada." + +#. module: rma +#: selection:rma,state:0 +msgid "Returned" +msgstr "Devuelto" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma_delivery_wizard__rma_count +msgid "Rma Count" +msgstr "Cantidad de RMAs" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma_delivery_wizard__scheduled_date +msgid "Scheduled Date" +msgstr "Fecha prevista" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__access_token +msgid "Security Token" +msgstr "Token de seguridad" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.rma_view_form +msgid "Send by Email" +msgstr "Enviar por correo electrónico" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.rma_view_form +msgid "Send by Mail" +msgstr "Enviar por correo" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__sent +msgid "Sent" +msgstr "Enviado" + +#. module: rma +#: code:addons/rma/models/stock_warehouse.py:48 +#, python-format +msgid "Sequence RMA in" +msgstr "Secuencia de recepción de RMA" + +#. module: rma +#: code:addons/rma/models/stock_warehouse.py:53 +#, python-format +msgid "Sequence RMA out" +msgstr "Secuencia de entrega de RMA" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.rma_view_form +msgid "Set to draft" +msgstr "Establecer a borrador" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.rma_view_form +msgid "Share" +msgstr "Compartir" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__origin +msgid "Source Document" +msgstr "Documento origen" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.rma_split_wizard_view_form2 +#: model_terms:ir.ui.view,arch_db:rma.rma_view_form +msgid "Split" +msgstr "Dividir" + +#. module: rma +#: model:ir.actions.act_window,name:rma.rma_split_wizard_action +msgid "Split RMA" +msgstr "Dividir RMA" + +#. module: rma +#: code:addons/rma/models/rma.py:874 +#, python-format +msgid "" +"Split: %s has been " +"created." +msgstr "" +"División: El RMA %s ha sido creado." + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__state +#: model_terms:ir.ui.view,arch_db:rma.rma_view_search +msgid "State" +msgstr "Provincia" + +#. module: rma +#: code:addons/rma/controllers/main.py:40 +#: model_terms:ir.ui.view,arch_db:rma.portal_my_rmas +#, python-format +msgid "Status" +msgstr "Estado" + +#. module: rma +#: model:ir.model.fields,help:rma.field_rma__activity_state +msgid "" +"Status based on activities\n" +"Overdue: Due date is already passed\n" +"Today: Activity date is today\n" +"Planned: Future activities." +msgstr "" +"Estado basado en actividades\n" +"Vencida: la fecha tope ya ha pasado\n" +"Hoy: La fecha tope es hoy\n" +"Planificada: futuras actividades." + +#. module: rma +#: model:ir.model,name:rma.model_stock_move +msgid "Stock Move" +msgstr "Movimiento de existencias" + +#. module: rma +#: model:ir.model,name:rma.model_stock_rule +msgid "Stock Rule" +msgstr "Regla de Inventario" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma_team__user_id +msgid "Team Leader" +msgstr "Líder del equipo" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma_team__member_ids +#: model_terms:ir.ui.view,arch_db:rma.rma_team_view_form +msgid "Team Members" +msgstr "Miembros del equipo" + +#. module: rma +#: sql_constraint:rma.operation:0 +msgid "That operation name already exists !" +msgstr "" + +#. module: rma +#: model:ir.model.fields,help:rma.field_rma_team__alias_model_id +msgid "" +"The model (Odoo Document Kind) to which this alias corresponds. Any incoming " +"email that does not reply to an existing record will cause the creation of a " +"new record of this model (e.g. a Project Task)" +msgstr "" +"El modelo (Tipo de documento de Odoo) al que corresponde este seudónimo. " +"Cualquier correo entrante que no sea respuesta a un registro existente, " +"causará la creación de un nuevo registro de este modelo" + +#. module: rma +#: model:ir.model.fields,help:rma.field_rma_team__alias_name +msgid "" +"The name of the email alias, e.g. 'jobs' if you want to catch emails for " +"" +msgstr "" +"El nombre de este seudónimo de correo electrónico. Por ejemplo, \"trabajos" +"\", si lo que quiere es obtener los correos para ." + +#. module: rma +#: model:ir.model.fields,help:rma.field_rma_team__alias_user_id +msgid "" +"The owner of records created upon receiving emails on this alias. If this " +"field is not set the system will attempt to find the right owner based on " +"the sender (From) address, or will use the Administrator account if no " +"system user is found for that address." +msgstr "" +"El propietario de los registros creados al recibir correos electrónicos en " +"este seudónimo. Si el campo no está establecido, el sistema tratará de " +"encontrar el propietario adecuado basado en la dirección del emisor (De), o " +"usará la cuenta de administrador si no se encuentra un usuario para esa " +"dirección." + +#. module: rma +#: code:addons/rma/models/stock_move.py:60 +#, python-format +msgid "" +"The quantity done for the product '%s' must be equal to its initial demand " +"because the stock move is linked to an RMA (%s)." +msgstr "" +"La cantidad realizada para el producto '%s' debe ser igual a la demanda " +"inicial porque el movimiento está enlazado a un RMA (%s)." + +#. module: rma +#: code:addons/rma/models/rma.py:778 +#, python-format +msgid "The quantity to return is greater than remaining quantity." +msgstr "La cantidad a devolver es mayor que la cantidad restante del RMA." + +#. module: rma +#: code:addons/rma/models/account_invoice.py:22 +#, python-format +msgid "" +"There is at least one invoice lines whose quantity is less than the quantity " +"specified in its linked RMA." +msgstr "" +"Hay al menos una linea de factura que tiene una cantidad menor que la " +"cantidad especificada en el RMA asociado." + +#. module: rma +#: code:addons/rma/models/rma.py:767 +#, python-format +msgid "This RMA cannot be split." +msgstr "Este RMA no puede ser dividido." + +#. module: rma +#: code:addons/rma/models/rma.py:754 +#, python-format +msgid "This RMA cannot perform a replacement." +msgstr "Este RMA no puede realizar un reemplazo." + +#. module: rma +#: code:addons/rma/models/rma.py:737 +#, python-format +msgid "This RMA cannot perform a return." +msgstr "Este RMA no puede realizar una devolución." + +#. module: rma +#: model:ir.actions.server,name:rma.rma_refund_action_server +#: model_terms:ir.ui.view,arch_db:rma.rma_view_form +msgid "To Refund" +msgstr "Reembolsar" + +#. module: rma +#: selection:rma,activity_state:0 +msgid "Today" +msgstr "Hoy" + +#. module: rma +#: model:ir.model,name:rma.model_stock_picking +msgid "Transfer" +msgstr "Transferir" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma_delivery_wizard__type +msgid "Type" +msgstr "Tipo" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_stock_return_picking__picking_type_code +msgid "Type of Operation" +msgstr "Tipo de operación" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.rma_view_search +msgid "Unassigned RMAs" +msgstr "RMAs no asignados" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma_delivery_wizard__product_uom +#: model:ir.model.fields,field_description:rma.field_rma_split_wizard__product_uom +msgid "Unit of measure" +msgstr "Unidad de Medida" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.rma_view_form +msgid "Unlock" +msgstr "Desbloquear" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__message_unread +#: model:ir.model.fields,field_description:rma.field_rma_team__message_unread +msgid "Unread Messages" +msgstr "Mensajes por leer" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__message_unread_counter +#: model:ir.model.fields,field_description:rma.field_rma_team__message_unread_counter +msgid "Unread Messages Counter" +msgstr "Contador de mensajes sin leer" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__product_uom +msgid "UoM" +msgstr "UdM" + +#. module: rma +#: selection:rma,priority:0 +msgid "Urgent" +msgstr "Urgente" + +#. module: rma +#: model:res.groups,name:rma.rma_group_user_all +msgid "User: All Documents" +msgstr "Usuario: Mostrar todos los documentos" + +#. module: rma +#: model:res.groups,name:rma.rma_group_user_own +msgid "User: Own Documents Only" +msgstr "Usuario: Solo mostrar documentos propios" + +#. module: rma +#: model:ir.model,name:rma.model_res_users +msgid "Users" +msgstr "Usuarios" + +#. module: rma +#: selection:rma,priority:0 +msgid "Very Urgent" +msgstr "Muy Urgente" + +#. module: rma +#: selection:rma,state:0 +msgid "Waiting for refund" +msgstr "Esperando por reembolso" + +#. module: rma +#: selection:rma,state:0 +msgid "Waiting for replacement" +msgstr "Esperando por reemplazo" + +#. module: rma +#: selection:rma,state:0 +msgid "Waiting for return" +msgstr "Esperando por devolución" + +#. module: rma +#: model:ir.model,name:rma.model_stock_warehouse +#: model:ir.model.fields,field_description:rma.field_rma__warehouse_id +#: model:ir.model.fields,field_description:rma.field_rma_delivery_wizard__warehouse_id +msgid "Warehouse" +msgstr "Almacén" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__website_message_ids +#: model:ir.model.fields,field_description:rma.field_rma_team__website_message_ids +msgid "Website Messages" +msgstr "Mensajes del sitio web" + +#. module: rma +#: model:ir.model.fields,help:rma.field_rma__website_message_ids +#: model:ir.model.fields,help:rma.field_rma_team__website_message_ids +msgid "Website communication history" +msgstr "Historial de comunicaciones del sitio web" + +#. module: rma +#: code:addons/rma/models/rma.py:514 +#, python-format +msgid "You cannot delete RMAs that are not in draft state" +msgstr "No puede " + +#. module: rma +#: code:addons/rma/wizard/stock_picking_return.py:56 +#, python-format +msgid "" +"You must specify the 'Customer' in the 'Stock Picking' from which RMAs will " +"be created" +msgstr "" +"Debe seleccionar el 'Cliente' en la 'Orden de Entrega' desde la cual los " +"RMAs serán creados." + +#. module: rma +#: model:res.groups,comment:rma.rma_group_user_all +msgid "" +"the user will have access to all records of everyone in the RMA application." +msgstr "" +"El usuario tendrá acceso a todos los registros de RMA de todos lo usuarios." + +#. module: rma +#: model:res.groups,comment:rma.rma_group_user_own +msgid "the user will have access to his own data in the RMA application." +msgstr "El usuario tendrá acceso solo a sus propios RMAs" + +#. module: rma +#: model:res.groups,comment:rma.rma_group_manager +msgid "" +"the user will have an access to the RMA configuration as well as statistic " +"reports." +msgstr "" +"El usuario tendrá acceso a la configuración de RMA y a los informes " +"estadísticos." diff --git a/rma/i18n/rma.pot b/rma/i18n/rma.pot new file mode 100644 index 000000000..7b327b2b5 --- /dev/null +++ b/rma/i18n/rma.pot @@ -0,0 +1,1485 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * rma +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 12.0\n" +"Report-Msgid-Bugs-To: \n" +"Last-Translator: <>\n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: rma +#: model:mail.template,report_name:rma.mail_template_rma_notification +msgid "${(object.name or '')}" +msgstr "" + +#. module: rma +#: model:mail.template,subject:rma.mail_template_rma_notification +msgid "${object.company_id.name} RMA (Ref ${object.name or 'n/a' })" +msgstr "" + +#. module: rma +#: code:addons/rma/models/rma_team.py:43 +#, python-format +msgid "%s (copy)" +msgstr "" + +#. module: rma +#: model:mail.template,body_html:rma.mail_template_rma_notification +msgid "
\n" +"

\n" +" Dear ${object.partner_id.name}\n" +" % if object.partner_id.parent_id:\n" +" (${object.partner_id.parent_id.name})\n" +" % endif\n" +"

\n" +" Here is the RMA ${object.name} from ${object.company_id.name}.\n" +"

\n" +" Do not hesitate to contact us if you have any question.\n" +"

\n" +"
\n" +" " +msgstr "" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.portal_rma_page +msgid "" +msgstr "" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.portal_rma_page +msgid " Paid" +msgstr "" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.portal_rma_page +msgid " Waiting Payment" +msgstr "" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.portal_rma_page +msgid "" +msgstr "" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.portal_rma_page +msgid "" +msgstr "" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.portal_rma_page +msgid " Cancelled" +msgstr "" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.portal_rma_page +msgid " Preparation" +msgstr "" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.portal_rma_page +msgid " Shipped" +msgstr "" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.portal_rma_page +msgid " Partially Available" +msgstr "" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.portal_rma_page +msgid "Delivery" +msgstr "" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.portal_rma_page +msgid "Reception" +msgstr "" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.portal_rma_page +msgid "Refund" +msgstr "" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.portal_rma_page +msgid "Customer:" +msgstr "" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.report_rma_document +msgid "Date:" +msgstr "" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.report_rma_document +msgid "Deadline:" +msgstr "" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.portal_rma_page +msgid "Deadline" +msgstr "" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.report_rma_document +msgid "Delivered qty:" +msgstr "" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.portal_rma_page +msgid "Delivered quantity" +msgstr "" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.report_rma_document +msgid "Invoicing address:" +msgstr "" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.report_rma_document +msgid "Move:" +msgstr "" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.report_rma_document +msgid "Origin delivery:" +msgstr "" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.portal_rma_page +msgid "Origin delivery" +msgstr "" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.report_rma_document +msgid "Origin:" +msgstr "" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.portal_rma_page +msgid "Origin" +msgstr "" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.report_rma_document +msgid "Product:" +msgstr "" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.portal_rma_page +msgid "Product" +msgstr "" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.report_rma_document +msgid "Quantity:" +msgstr "" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.portal_rma_page +msgid "Quantity" +msgstr "" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.portal_rma_page +msgid "RMA Date" +msgstr "" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.report_rma_document +msgid "RMA Note:" +msgstr "" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.portal_rma_page +#: model_terms:ir.ui.view,arch_db:rma.report_rma_document +msgid "Responsible:" +msgstr "" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.report_rma_document +msgid "State:" +msgstr "" + +#. module: rma +#: model:ir.model.fields,help:rma.field_rma_team__alias_defaults +msgid "A Python dictionary that will be evaluated to provide default values when creating new records for this alias." +msgstr "" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.rma_team_view_form +msgid "Accept Emails From" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__access_warning +msgid "Access warning" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__message_needaction +#: model:ir.model.fields,field_description:rma.field_rma_team__message_needaction +msgid "Action Needed" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma_team__active +msgid "Active" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__activity_ids +msgid "Activities" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__activity_state +msgid "Activity State" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma_team__alias_id +msgid "Alias" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma_team__alias_contact +msgid "Alias Contact Security" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma_team__alias_name +msgid "Alias Name" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma_team__alias_domain +msgid "Alias domain" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma_team__alias_model_id +msgid "Aliased Model" +msgstr "" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.rma_view_form +msgid "Are you sure you want to cancel this RMA" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__message_attachment_count +#: model:ir.model.fields,field_description:rma.field_rma_team__message_attachment_count +msgid "Attachment Count" +msgstr "" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.rma_team_view_form +msgid "Avatar" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__can_be_locked +msgid "Can Be Locked" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__can_be_refunded +msgid "Can Be Refunded" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__can_be_replaced +msgid "Can Be Replaced" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__can_be_returned +msgid "Can Be Returned" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__can_be_split +msgid "Can Be Split" +msgstr "" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.rma_redelivery_wizard_view_form +#: model_terms:ir.ui.view,arch_db:rma.rma_split_wizard_view_form2 +#: model_terms:ir.ui.view,arch_db:rma.rma_view_form +msgid "Cancel" +msgstr "" + +#. module: rma +#: selection:rma,state:0 +msgid "Canceled" +msgstr "" + +#. module: rma +#: model_terms:ir.actions.act_window,help:rma.rma_action +#: model_terms:ir.actions.act_window,help:rma.rma_team_action +msgid "Click to add a new RMA." +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__commercial_partner_id +msgid "Commercial Entity" +msgstr "" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.portal_rma_page +msgid "Communication" +msgstr "" + +#. module: rma +#: model:ir.model,name:rma.model_res_company +msgid "Companies" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__company_id +#: model:ir.model.fields,field_description:rma.field_rma_team__company_id +msgid "Company" +msgstr "" + +#. module: rma +#: model:ir.ui.menu,name:rma.rma_configuration_menu +msgid "Configuration" +msgstr "" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.rma_view_form +msgid "Confirm" +msgstr "" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.rma_view_search +#: selection:rma,state:0 +msgid "Confirmed" +msgstr "" + +#. module: rma +#: model:ir.model,name:rma.model_res_partner +#: model_terms:ir.ui.view,arch_db:rma.portal_rma_page +msgid "Contact" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_stock_return_picking__create_rma +msgid "Create RMAs" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__create_uid +#: model:ir.model.fields,field_description:rma.field_rma_delivery_wizard__create_uid +#: model:ir.model.fields,field_description:rma.field_rma_operation__create_uid +#: model:ir.model.fields,field_description:rma.field_rma_split_wizard__create_uid +#: model:ir.model.fields,field_description:rma.field_rma_team__create_uid +msgid "Created by" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__create_date +#: model:ir.model.fields,field_description:rma.field_rma_delivery_wizard__create_date +#: model:ir.model.fields,field_description:rma.field_rma_operation__create_date +#: model:ir.model.fields,field_description:rma.field_rma_split_wizard__create_date +#: model:ir.model.fields,field_description:rma.field_rma_team__create_date +msgid "Created on" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__partner_id +msgid "Customer" +msgstr "" + +#. module: rma +#: model:ir.model.fields,help:rma.field_rma__access_url +msgid "Customer Portal URL" +msgstr "" + +#. module: rma +#: code:addons/rma/controllers/main.py:38 +#: model:ir.model.fields,field_description:rma.field_rma__date +#: model_terms:ir.ui.view,arch_db:rma.portal_my_rmas +#: model_terms:ir.ui.view,arch_db:rma.rma_view_search +#, python-format +msgid "Date" +msgstr "" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.portal_rma_page +msgid "Date:" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__deadline +#: model_terms:ir.ui.view,arch_db:rma.rma_view_search +msgid "Deadline" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma_team__alias_defaults +msgid "Default Values" +msgstr "" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.rma_redelivery_wizard_view_form +msgid "Deliver" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__delivered_qty +msgid "Delivered qty" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__delivered_qty_done +msgid "Delivered qty done" +msgstr "" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.rma_view_form +msgid "Delivery" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__delivery_picking_count +msgid "Delivery count" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__delivery_move_ids +msgid "Delivery reservation" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__description +#: model_terms:ir.ui.view,arch_db:rma.portal_rma_page +msgid "Description" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__display_name +#: model:ir.model.fields,field_description:rma.field_rma_delivery_wizard__display_name +#: model:ir.model.fields,field_description:rma.field_rma_operation__display_name +#: model:ir.model.fields,field_description:rma.field_rma_split_wizard__display_name +#: model:ir.model.fields,field_description:rma.field_rma_team__display_name +msgid "Display Name" +msgstr "" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.rma_view_search +#: selection:rma,state:0 +msgid "Draft" +msgstr "" + +#. module: rma +#: model:mail.message.subtype,name:rma.mt_rma_draft +#: model:mail.message.subtype,name:rma.mt_rma_team_rma_draft +msgid "Draft RMA" +msgstr "" + +#. module: rma +#: code:addons/rma/models/rma.py:1076 +#, python-format +msgid "E-mail subject: %s\n" +"\n" +"E-mail body:\n" +"%s" +msgstr "" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.rma_team_view_form +msgid "Email" +msgstr "" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.rma_team_view_form +msgid "Email Alias" +msgstr "" + +#. module: rma +#: code:addons/rma/wizard/rma_split.py:63 +#, python-format +msgid "Extracted RMA" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__origin_split_rma_id +msgid "Extracted from" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__message_follower_ids +#: model:ir.model.fields,field_description:rma.field_rma_team__message_follower_ids +msgid "Followers" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__message_channel_ids +#: model:ir.model.fields,field_description:rma.field_rma_team__message_channel_ids +msgid "Followers (Channels)" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__message_partner_ids +#: model:ir.model.fields,field_description:rma.field_rma_team__message_partner_ids +msgid "Followers (Partners)" +msgstr "" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.rma_view_search +msgid "Group By" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__id +#: model:ir.model.fields,field_description:rma.field_rma_delivery_wizard__id +#: model:ir.model.fields,field_description:rma.field_rma_operation__id +#: model:ir.model.fields,field_description:rma.field_rma_split_wizard__id +#: model:ir.model.fields,field_description:rma.field_rma_team__id +msgid "ID" +msgstr "" + +#. module: rma +#: model:ir.model.fields,help:rma.field_rma_team__alias_parent_thread_id +msgid "ID of the parent record holding the alias (example: project holding the task creation alias)" +msgstr "" + +#. module: rma +#: model:ir.model.fields,help:rma.field_rma__message_unread +#: model:ir.model.fields,help:rma.field_rma_team__message_unread +msgid "If checked new messages require your attention." +msgstr "" + +#. module: rma +#: model:ir.model.fields,help:rma.field_rma__message_needaction +#: model:ir.model.fields,help:rma.field_rma_team__message_needaction +msgid "If checked, new messages require your attention." +msgstr "" + +#. module: rma +#: model:ir.model.fields,help:rma.field_rma__message_has_error +#: model:ir.model.fields,help:rma.field_rma_team__message_has_error +msgid "If checked, some messages have a delivery error." +msgstr "" + +#. module: rma +#: model:ir.model.fields,help:rma.field_rma_team__active +msgid "If the active field is set to false, it will allow you to hide the RMA Team without removing it." +msgstr "" + +#. module: rma +#: code:addons/rma/models/rma.py:1080 +#, python-format +msgid "Incoming e-mail" +msgstr "" + +#. module: rma +#: model:ir.model,name:rma.model_account_invoice +msgid "Invoice" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__partner_invoice_id +msgid "Invoice Address" +msgstr "" + +#. module: rma +#: model:ir.model,name:rma.model_account_invoice_line +msgid "Invoice Line" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__message_is_follower +#: model:ir.model.fields,field_description:rma.field_rma_team__message_is_follower +msgid "Is Follower" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma____last_update +#: model:ir.model.fields,field_description:rma.field_rma_delivery_wizard____last_update +#: model:ir.model.fields,field_description:rma.field_rma_operation____last_update +#: model:ir.model.fields,field_description:rma.field_rma_split_wizard____last_update +#: model:ir.model.fields,field_description:rma.field_rma_team____last_update +msgid "Last Modified on" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__write_uid +#: model:ir.model.fields,field_description:rma.field_rma_delivery_wizard__write_uid +#: model:ir.model.fields,field_description:rma.field_rma_operation__write_uid +#: model:ir.model.fields,field_description:rma.field_rma_split_wizard__write_uid +#: model:ir.model.fields,field_description:rma.field_rma_team__write_uid +msgid "Last Updated by" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__write_date +#: model:ir.model.fields,field_description:rma.field_rma_delivery_wizard__write_date +#: model:ir.model.fields,field_description:rma.field_rma_operation__write_date +#: model:ir.model.fields,field_description:rma.field_rma_split_wizard__write_date +#: model:ir.model.fields,field_description:rma.field_rma_team__write_date +msgid "Last Updated on" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__location_id +msgid "Location" +msgstr "" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.rma_view_form +msgid "Lock" +msgstr "" + +#. module: rma +#: selection:rma,state:0 +msgid "Locked" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__message_main_attachment_id +#: model:ir.model.fields,field_description:rma.field_rma_team__message_main_attachment_id +msgid "Main Attachment" +msgstr "" + +#. module: rma +#: model:ir.module.category,description:rma.rma_module_category +msgid "Manage Return Merchandise Authorizations (RMAs)." +msgstr "" + +#. module: rma +#: model:res.groups,name:rma.rma_group_manager +msgid "Manager" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__message_has_error +#: model:ir.model.fields,field_description:rma.field_rma_team__message_has_error +msgid "Message Delivery error" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__message_ids +#: model:ir.model.fields,field_description:rma.field_rma_team__message_ids +msgid "Messages" +msgstr "" + +#. module: rma +#: code:addons/rma/controllers/main.py:39 +#: model:ir.model.fields,field_description:rma.field_rma__name +#: model:ir.model.fields,field_description:rma.field_rma_operation__name +#: model:ir.model.fields,field_description:rma.field_rma_team__name +#, python-format +msgid "Name" +msgstr "" + +#. module: rma +#: code:addons/rma/models/rma.py:31 +#: code:addons/rma/models/rma.py:496 +#: code:addons/rma/models/rma.py:1079 +#, python-format +msgid "New" +msgstr "" + +#. module: rma +#: model:mail.message.subtype,description:rma.mt_rma_draft +msgid "New RMA in draft state" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__activity_date_deadline +msgid "Next Activity Deadline" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__activity_summary +msgid "Next Activity Summary" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__activity_type_id +msgid "Next Activity Type" +msgstr "" + +#. module: rma +#: code:addons/rma/models/rma.py:757 +#, python-format +msgid "None of the selected RMAs can perform a replacement." +msgstr "" + +#. module: rma +#: code:addons/rma/models/rma.py:740 +#, python-format +msgid "None of the selected RMAs can perform a return." +msgstr "" + +#. module: rma +#: selection:rma,priority:0 +msgid "Normal" +msgstr "" + +#. module: rma +#: selection:rma,priority:0 +msgid "Not urgent" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__message_needaction_counter +#: model:ir.model.fields,field_description:rma.field_rma_team__message_needaction_counter +msgid "Number of Actions" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__message_has_error_counter +#: model:ir.model.fields,field_description:rma.field_rma_team__message_has_error_counter +msgid "Number of error" +msgstr "" + +#. module: rma +#: model:ir.model.fields,help:rma.field_rma__message_needaction_counter +#: model:ir.model.fields,help:rma.field_rma_team__message_needaction_counter +msgid "Number of messages which requires an action" +msgstr "" + +#. module: rma +#: model:ir.model.fields,help:rma.field_rma__message_has_error_counter +#: model:ir.model.fields,help:rma.field_rma_team__message_has_error_counter +msgid "Number of messages with delivery error" +msgstr "" + +#. module: rma +#: model:ir.model.fields,help:rma.field_rma__message_unread_counter +#: model:ir.model.fields,help:rma.field_rma_team__message_unread_counter +msgid "Number of unread messages" +msgstr "" + +#. module: rma +#: model:ir.model.fields,help:rma.field_rma_team__alias_force_thread_id +msgid "Optional ID of a thread (record) to which all incoming messages will be attached, even if they did not reply to it. If set, this will disable the creation of new records completely." +msgstr "" + +#. module: rma +#: model:ir.ui.menu,name:rma.rma_orders_menu +msgid "Orders" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__picking_id +msgid "Origin Delivery" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__move_id +msgid "Origin move" +msgstr "" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.rma_view_form +msgid "Other Information" +msgstr "" + +#. module: rma +#: selection:rma,activity_state:0 +msgid "Overdue" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma_team__alias_user_id +msgid "Owner" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma_team__alias_parent_model_id +msgid "Parent Model" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma_team__alias_parent_thread_id +msgid "Parent Record Thread ID" +msgstr "" + +#. module: rma +#: model:ir.model.fields,help:rma.field_rma_team__alias_parent_model_id +msgid "Parent model holding the alias. The model holding the alias reference is not necessarily the model given by alias_model_id (example: project (parent_model) and task (model))" +msgstr "" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.rma_view_search +msgid "Partner" +msgstr "" + +#. module: rma +#: selection:rma,activity_state:0 +msgid "Planned" +msgstr "" + +#. module: rma +#: model:ir.model.fields,help:rma.field_rma_team__alias_contact +msgid "Policy to post a message on the document using the mailgateway.\n" +"- everyone: everyone can post\n" +"- partners: only authenticated partners\n" +"- followers: only followers of the related document or members of following channels\n" +"" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__access_url +msgid "Portal Access URL" +msgstr "" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.rma_view_form +msgid "Preview" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__priority +msgid "Priority" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__procurement_group_id +msgid "Procurement group" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__product_id +#: model_terms:ir.ui.view,arch_db:rma.portal_my_rmas +msgid "Product" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma_delivery_wizard__product_uom_qty +msgid "Product qty" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__product_uom_qty +#: model_terms:ir.ui.view,arch_db:rma.portal_my_rmas +msgid "Quantity" +msgstr "" + +#. module: rma +#: code:addons/rma/wizard/rma_delivery.py:49 +#: sql_constraint:rma.split.wizard:0 +#, python-format +msgid "Quantity must be greater than 0." +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma_split_wizard__product_uom_qty +msgid "Quantity to extract" +msgstr "" + +#. module: rma +#: code:addons/rma/models/rma.py:790 +#, python-format +msgid "Quantity to extract cannot be greater than remaining delivery quantity (%s %s)" +msgstr "" + +#. module: rma +#: model:ir.model.fields,help:rma.field_rma_split_wizard__product_uom_qty +msgid "Quantity to extract to a new RMA." +msgstr "" + +#. module: rma +#: model:ir.actions.act_window,name:rma.rma_action +#: model:ir.model,name:rma.model_rma +#: model:ir.model.fields,field_description:rma.field_account_invoice_line__rma_id +#: model:ir.model.fields,field_description:rma.field_rma_split_wizard__rma_id +#: model:ir.model.fields,field_description:rma.field_stock_warehouse__rma +#: model:ir.module.category,name:rma.rma_module_category +#: model:ir.ui.menu,name:rma.rma_menu +#: model_terms:ir.ui.view,arch_db:rma.view_partner_form +#: model_terms:ir.ui.view,arch_db:rma.view_picking_form +msgid "RMA" +msgstr "" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.portal_my_rmas +#: model_terms:ir.ui.view,arch_db:rma.report_rma_document +msgid "RMA #" +msgstr "" + +#. module: rma +#: code:addons/rma/models/res_company.py:18 +#, python-format +msgid "RMA Code" +msgstr "" + +#. module: rma +#: code:addons/rma/models/stock_warehouse.py:82 +#, python-format +msgid "RMA Delivery Orders" +msgstr "" + +#. module: rma +#: model:ir.model,name:rma.model_rma_delivery_wizard +msgid "RMA Delivery Wizard" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_stock_warehouse__rma_in_type_id +msgid "RMA In Type" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_stock_warehouse__rma_loc_id +msgid "RMA Location" +msgstr "" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.portal_rma_page +msgid "RMA Order -" +msgstr "" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.portal_my_home_menu_rma +#: model_terms:ir.ui.view,arch_db:rma.portal_my_home_rma +#: model_terms:ir.ui.view,arch_db:rma.portal_my_rmas +msgid "RMA Orders" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_stock_warehouse__rma_out_type_id +msgid "RMA Out Type" +msgstr "" + +#. module: rma +#: code:addons/rma/models/stock_warehouse.py:73 +#, python-format +msgid "RMA Receipts" +msgstr "" + +#. module: rma +#: model:ir.actions.report,name:rma.report_rma_action +msgid "RMA Report" +msgstr "" + +#. module: rma +#: model:ir.model,name:rma.model_rma_split_wizard +msgid "RMA Split Wizard" +msgstr "" + +#. module: rma +#: model:ir.model,name:rma.model_rma_team +#: model:ir.model.fields,field_description:rma.field_res_users__rma_team_id +#: model:ir.ui.menu,name:rma.rma_configuration_rma_team_menu +#: model_terms:ir.ui.view,arch_db:rma.rma_team_view_form +msgid "RMA Team" +msgstr "" + +#. module: rma +#: model:ir.model.fields,help:rma.field_res_users__rma_team_id +msgid "RMA Team the user is member of." +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_res_partner__rma_count +#: model:ir.model.fields,field_description:rma.field_res_users__rma_count +#: model:ir.model.fields,field_description:rma.field_stock_picking__rma_count +msgid "RMA count" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_stock_move__rma_receiver_ids +msgid "RMA receivers" +msgstr "" + +#. module: rma +#: model:ir.model.fields,help:rma.field_stock_warehouse__rma +msgid "RMA related products can be stored in this warehouse." +msgstr "" + +#. module: rma +#: model:ir.model,name:rma.model_rma_operation +msgid "RMA requested operation" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_stock_move__rma_id +msgid "RMA return" +msgstr "" + +#. module: rma +#: model:ir.actions.act_window,name:rma.rma_team_action +#: model:ir.model.fields,field_description:rma.field_rma__team_id +msgid "RMA team" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_res_partner__rma_ids +#: model:ir.model.fields,field_description:rma.field_res_users__rma_ids +#: model:ir.model.fields,field_description:rma.field_stock_move__rma_ids +msgid "RMAs" +msgstr "" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.rma_view_form +msgid "Receipt" +msgstr "" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.rma_view_search +#: selection:rma,state:0 +msgid "Received" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__reception_move_id +msgid "Reception move" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma_team__alias_force_thread_id +msgid "Record Thread ID" +msgstr "" + +#. module: rma +#: model:ir.model.fields,help:rma.field_rma__origin +msgid "Reference of the document that generated this RMA." +msgstr "" + +#. module: rma +#: code:addons/rma/models/rma.py:679 +#: model:ir.model.fields,field_description:rma.field_rma__refund_id +#: model_terms:ir.ui.view,arch_db:rma.rma_view_form +#: model:rma.operation,name:rma.rma_operation_refund +#, python-format +msgid "Refund" +msgstr "" + +#. module: rma +#: model:ir.model.fields,help:rma.field_rma__partner_invoice_id +msgid "Refund address for current RMA." +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__refund_line_id +msgid "Refund line" +msgstr "" + +#. module: rma +#: selection:rma,state:0 +msgid "Refunded" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__remaining_qty +msgid "Remaining delivered qty" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__remaining_qty_to_done +msgid "Remaining delivered qty to done" +msgstr "" + +#. module: rma +#: model:rma.operation,name:rma.rma_operation_return +msgid "Repair" +msgstr "" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.rma_view_form +#: selection:rma.delivery.wizard,type:0 +#: model:rma.operation,name:rma.rma_operation_replace +msgid "Replace" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma_delivery_wizard__product_id +msgid "Replace Product" +msgstr "" + +#. module: rma +#: selection:rma,state:0 +msgid "Replaced" +msgstr "" + +#. module: rma +#: code:addons/rma/models/rma.py:995 +#, python-format +msgid "Replacement: Move
%s (Picking %s) has been created." +msgstr "" + +#. module: rma +#: code:addons/rma/models/rma.py:1006 +#, python-format +msgid "Replacement:
Product %s
Quantity %f %s
This replacement did not create a new move, but one of the previously created moves was updated with this data." +msgstr "" + +#. module: rma +#: model:ir.ui.menu,name:rma.rma_reporting_menu +msgid "Reporting" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__operation_id +msgid "Requested operation" +msgstr "" + +#. module: rma +#: code:addons/rma/models/rma.py:721 +#, python-format +msgid "Required field(s):%s" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__user_id +#: model_terms:ir.ui.view,arch_db:rma.rma_view_search +msgid "Responsible" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__activity_user_id +msgid "Responsible User" +msgstr "" + +#. module: rma +#: model:ir.model,name:rma.model_stock_return_picking +msgid "Return Picking" +msgstr "" + +#. module: rma +#: model:ir.actions.act_window,name:rma.rma_delivery_wizard_action +#: model_terms:ir.ui.view,arch_db:rma.rma_view_form +#: selection:rma.delivery.wizard,type:0 +msgid "Return to customer" +msgstr "" + +#. module: rma +#: code:addons/rma/models/rma.py:956 +#, python-format +msgid "Return: %s has been created." +msgstr "" + +#. module: rma +#: selection:rma,state:0 +msgid "Returned" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma_delivery_wizard__rma_count +msgid "Rma Count" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma_delivery_wizard__scheduled_date +msgid "Scheduled Date" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__access_token +msgid "Security Token" +msgstr "" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.rma_view_form +msgid "Send by Email" +msgstr "" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.rma_view_form +msgid "Send by Mail" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__sent +msgid "Sent" +msgstr "" + +#. module: rma +#: code:addons/rma/models/stock_warehouse.py:48 +#, python-format +msgid "Sequence RMA in" +msgstr "" + +#. module: rma +#: code:addons/rma/models/stock_warehouse.py:53 +#, python-format +msgid "Sequence RMA out" +msgstr "" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.rma_view_form +msgid "Set to draft" +msgstr "" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.rma_view_form +msgid "Share" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__origin +msgid "Source Document" +msgstr "" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.rma_split_wizard_view_form2 +#: model_terms:ir.ui.view,arch_db:rma.rma_view_form +msgid "Split" +msgstr "" + +#. module: rma +#: model:ir.actions.act_window,name:rma.rma_split_wizard_action +msgid "Split RMA" +msgstr "" + +#. module: rma +#: code:addons/rma/models/rma.py:874 +#, python-format +msgid "Split: %s has been created." +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__state +#: model_terms:ir.ui.view,arch_db:rma.rma_view_search +msgid "State" +msgstr "" + +#. module: rma +#: code:addons/rma/controllers/main.py:40 +#: model_terms:ir.ui.view,arch_db:rma.portal_my_rmas +#, python-format +msgid "Status" +msgstr "" + +#. module: rma +#: model:ir.model.fields,help:rma.field_rma__activity_state +msgid "Status based on activities\n" +"Overdue: Due date is already passed\n" +"Today: Activity date is today\n" +"Planned: Future activities." +msgstr "" + +#. module: rma +#: model:ir.model,name:rma.model_stock_move +msgid "Stock Move" +msgstr "" + +#. module: rma +#: model:ir.model,name:rma.model_stock_rule +msgid "Stock Rule" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma_team__user_id +msgid "Team Leader" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma_team__member_ids +#: model_terms:ir.ui.view,arch_db:rma.rma_team_view_form +msgid "Team Members" +msgstr "" + +#. module: rma +#: sql_constraint:rma.operation:0 +msgid "That operation name already exists !" +msgstr "" + +#. module: rma +#: model:ir.model.fields,help:rma.field_rma_team__alias_model_id +msgid "The model (Odoo Document Kind) to which this alias corresponds. Any incoming email that does not reply to an existing record will cause the creation of a new record of this model (e.g. a Project Task)" +msgstr "" + +#. module: rma +#: model:ir.model.fields,help:rma.field_rma_team__alias_name +msgid "The name of the email alias, e.g. 'jobs' if you want to catch emails for " +msgstr "" + +#. module: rma +#: model:ir.model.fields,help:rma.field_rma_team__alias_user_id +msgid "The owner of records created upon receiving emails on this alias. If this field is not set the system will attempt to find the right owner based on the sender (From) address, or will use the Administrator account if no system user is found for that address." +msgstr "" + +#. module: rma +#: code:addons/rma/models/stock_move.py:60 +#, python-format +msgid "The quantity done for the product '%s' must be equal to its initial demand because the stock move is linked to an RMA (%s)." +msgstr "" + +#. module: rma +#: code:addons/rma/models/rma.py:778 +#, python-format +msgid "The quantity to return is greater than remaining quantity." +msgstr "" + +#. module: rma +#: code:addons/rma/models/account_invoice.py:22 +#, python-format +msgid "There is at least one invoice lines whose quantity is less than the quantity specified in its linked RMA." +msgstr "" + +#. module: rma +#: code:addons/rma/models/rma.py:767 +#, python-format +msgid "This RMA cannot be split." +msgstr "" + +#. module: rma +#: code:addons/rma/models/rma.py:754 +#, python-format +msgid "This RMA cannot perform a replacement." +msgstr "" + +#. module: rma +#: code:addons/rma/models/rma.py:737 +#, python-format +msgid "This RMA cannot perform a return." +msgstr "" + +#. module: rma +#: model:ir.actions.server,name:rma.rma_refund_action_server +#: model_terms:ir.ui.view,arch_db:rma.rma_view_form +msgid "To Refund" +msgstr "" + +#. module: rma +#: selection:rma,activity_state:0 +msgid "Today" +msgstr "" + +#. module: rma +#: model:ir.model,name:rma.model_stock_picking +msgid "Transfer" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma_delivery_wizard__type +msgid "Type" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_stock_return_picking__picking_type_code +msgid "Type of Operation" +msgstr "" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.rma_view_search +msgid "Unassigned RMAs" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma_delivery_wizard__product_uom +#: model:ir.model.fields,field_description:rma.field_rma_split_wizard__product_uom +msgid "Unit of measure" +msgstr "" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.rma_view_form +msgid "Unlock" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__message_unread +#: model:ir.model.fields,field_description:rma.field_rma_team__message_unread +msgid "Unread Messages" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__message_unread_counter +#: model:ir.model.fields,field_description:rma.field_rma_team__message_unread_counter +msgid "Unread Messages Counter" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__product_uom +msgid "UoM" +msgstr "" + +#. module: rma +#: selection:rma,priority:0 +msgid "Urgent" +msgstr "" + +#. module: rma +#: model:res.groups,name:rma.rma_group_user_all +msgid "User: All Documents" +msgstr "" + +#. module: rma +#: model:res.groups,name:rma.rma_group_user_own +msgid "User: Own Documents Only" +msgstr "" + +#. module: rma +#: model:ir.model,name:rma.model_res_users +msgid "Users" +msgstr "" + +#. module: rma +#: selection:rma,priority:0 +msgid "Very Urgent" +msgstr "" + +#. module: rma +#: selection:rma,state:0 +msgid "Waiting for refund" +msgstr "" + +#. module: rma +#: selection:rma,state:0 +msgid "Waiting for replacement" +msgstr "" + +#. module: rma +#: selection:rma,state:0 +msgid "Waiting for return" +msgstr "" + +#. module: rma +#: model:ir.model,name:rma.model_stock_warehouse +#: model:ir.model.fields,field_description:rma.field_rma__warehouse_id +#: model:ir.model.fields,field_description:rma.field_rma_delivery_wizard__warehouse_id +msgid "Warehouse" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__website_message_ids +#: model:ir.model.fields,field_description:rma.field_rma_team__website_message_ids +msgid "Website Messages" +msgstr "" + +#. module: rma +#: model:ir.model.fields,help:rma.field_rma__website_message_ids +#: model:ir.model.fields,help:rma.field_rma_team__website_message_ids +msgid "Website communication history" +msgstr "" + +#. module: rma +#: code:addons/rma/models/rma.py:514 +#, python-format +msgid "You cannot delete RMAs that are not in draft state" +msgstr "" + +#. module: rma +#: code:addons/rma/wizard/stock_picking_return.py:56 +#, python-format +msgid "You must specify the 'Customer' in the 'Stock Picking' from which RMAs will be created" +msgstr "" + +#. module: rma +#: model:res.groups,comment:rma.rma_group_user_all +msgid "the user will have access to all records of everyone in the RMA application." +msgstr "" + +#. module: rma +#: model:res.groups,comment:rma.rma_group_user_own +msgid "the user will have access to his own data in the RMA application." +msgstr "" + +#. module: rma +#: model:res.groups,comment:rma.rma_group_manager +msgid "the user will have an access to the RMA configuration as well as statistic reports." +msgstr "" + diff --git a/rma/models/__init__.py b/rma/models/__init__.py new file mode 100644 index 000000000..134da114c --- /dev/null +++ b/rma/models/__init__.py @@ -0,0 +1,12 @@ +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from . import account_invoice +from . import rma +from . import rma_operation +from . import rma_team +from . import res_company +from . import res_partner +from . import res_users +from . import stock_move +from . import stock_picking +from . import stock_warehouse diff --git a/rma/models/account_invoice.py b/rma/models/account_invoice.py new file mode 100644 index 000000000..731891cff --- /dev/null +++ b/rma/models/account_invoice.py @@ -0,0 +1,35 @@ +# Copyright 2020 Tecnativa - Ernesto Tejeda +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import _, fields, models +from odoo.exceptions import ValidationError +from odoo.tools import float_compare + + +class AccountInvoice(models.Model): + _inherit = 'account.invoice' + + def action_invoice_open(self): + """ Avoids to validate a refund with less quantity of product than + quantity in the linked RMA. + """ + precision = self.env['decimal.precision'].precision_get( + 'Product Unit of Measure') + if self.mapped('invoice_line_ids').filtered( + lambda r: (r.rma_id and float_compare( + r.quantity, r.rma_id.product_uom_qty, precision) < 0)): + raise ValidationError( + _("There is at least one invoice lines whose quantity is " + "less than the quantity specified in its linked RMA.")) + res = super().action_invoice_open() + self.mapped('invoice_line_ids.rma_id').write({'state': 'refunded'}) + return res + + +class AccountInvoiceLine(models.Model): + _inherit = 'account.invoice.line' + + rma_id = fields.Many2one( + comodel_name='rma', + string='RMA', + ) diff --git a/rma/models/res_company.py b/rma/models/res_company.py new file mode 100644 index 000000000..555f09bd9 --- /dev/null +++ b/rma/models/res_company.py @@ -0,0 +1,23 @@ +# Copyright 2020 Tecnativa - Ernesto Tejeda +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import api, models, _ + + +class Company(models.Model): + _inherit = "res.company" + + @api.model + def create(self, vals): + company = super(Company, self).create(vals) + company.create_rma_index() + return company + + def create_rma_index(self): + self.env['ir.sequence'].sudo().create({ + 'name': _('RMA Code'), + 'prefix': 'RMA', + 'code': 'rma', + 'padding': 4, + 'company_id': self.id, + }) diff --git a/rma/models/res_partner.py b/rma/models/res_partner.py new file mode 100644 index 000000000..494c267a3 --- /dev/null +++ b/rma/models/res_partner.py @@ -0,0 +1,41 @@ +# Copyright 2020 Tecnativa - Ernesto Tejeda +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class ResPartner(models.Model): + _inherit = 'res.partner' + + rma_ids = fields.One2many( + comodel_name='rma', + inverse_name='partner_id', + string='RMAs', + ) + rma_count = fields.Integer( + string='RMA count', + compute='_compute_rma_count', + ) + + def _compute_rma_count(self): + rma_data = self.env['rma'].read_group( + [('partner_id', 'in', self.ids)], ['partner_id'], ['partner_id']) + mapped_data = dict( + [(r['partner_id'][0], r['partner_id_count']) for r in rma_data]) + for record in self: + record.rma_count = mapped_data.get(record.id, 0) + + def action_view_rma(self): + self.ensure_one() + action = self.env.ref('rma.rma_action').read()[0] + rma = self.rma_ids + if len(rma) == 1: + action.update( + res_id=rma.id, + view_mode="form", + view_id=False, + views=False, + ) + else: + action['domain'] = [('partner_id', 'in', self.ids)] + return action diff --git a/rma/models/res_users.py b/rma/models/res_users.py new file mode 100644 index 000000000..4cce3c9a1 --- /dev/null +++ b/rma/models/res_users.py @@ -0,0 +1,14 @@ +# Copyright 2020 Tecnativa - Ernesto Tejeda +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class ResUsers(models.Model): + _inherit = 'res.users' + + rma_team_id = fields.Many2one( + comodel_name='rma.team', + string="RMA Team", + help='RMA Team the user is member of.', + ) diff --git a/rma/models/rma.py b/rma/models/rma.py new file mode 100644 index 000000000..8367c5b36 --- /dev/null +++ b/rma/models/rma.py @@ -0,0 +1,1144 @@ +# Copyright 2020 Tecnativa - Ernesto Tejeda +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import _, api, fields, models +from odoo.exceptions import ValidationError +from odoo.tests import Form +from odoo.tools import html2plaintext +import odoo.addons.decimal_precision as dp +from odoo.addons.stock.models.stock_move import PROCUREMENT_PRIORITIES +from collections import Counter + + +class Rma(models.Model): + _name = "rma" + _description = "RMA" + _order = "date desc, priority" + _inherit = ["mail.thread", "portal.mixin", "mail.activity.mixin"] + + def _domain_location_id(self): + rma_loc = self.env['stock.warehouse'].search([]).mapped('rma_loc_id') + return [('id', 'child_of', rma_loc.ids)] + + # General fields + sent = fields.Boolean() + name = fields.Char( + string='Name', + index=True, + readonly=True, + states={'draft': [('readonly', False)]}, + copy=False, + default=lambda self: _('New'), + ) + origin = fields.Char( + string='Source Document', + states={ + 'locked': [('readonly', True)], + 'cancelled': [('readonly', True)], + }, + help="Reference of the document that generated this RMA.", + ) + date = fields.Datetime( + default=lambda self: fields.Datetime.now(), + index=True, + required=True, + readonly=True, + states={'draft': [('readonly', False)]}, + ) + deadline = fields.Date( + states={ + 'locked': [('readonly', True)], + 'cancelled': [('readonly', True)], + }, + ) + user_id = fields.Many2one( + comodel_name="res.users", + string="Responsible", + track_visibility="always", + default=lambda self: self.env.user, + states={ + 'locked': [('readonly', True)], + 'cancelled': [('readonly', True)], + }, + ) + team_id = fields.Many2one( + comodel_name="rma.team", + string="RMA team", + index=True, + states={ + 'locked': [('readonly', True)], + 'cancelled': [('readonly', True)], + }, + ) + company_id = fields.Many2one( + comodel_name="res.company", + default=lambda self: self.env.user.company_id, + states={ + 'locked': [('readonly', True)], + 'cancelled': [('readonly', True)], + }, + ) + partner_id = fields.Many2one( + string="Customer", + comodel_name="res.partner", + readonly=True, + states={'draft': [('readonly', False)]}, + index=True, + track_visibility='always' + ) + partner_invoice_id = fields.Many2one( + string="Invoice Address", + comodel_name="res.partner", + readonly=True, + states={'draft': [('readonly', False)]}, + domain=[('customer', '=', True)], + help="Refund address for current RMA." + ) + commercial_partner_id = fields.Many2one( + comodel_name="res.partner", + related="partner_id.commercial_partner_id", + ) + picking_id = fields.Many2one( + comodel_name="stock.picking", + string="Origin Delivery", + domain="[" + " ('state', '=', 'done')," + " ('picking_type_id.code', '=', 'outgoing')," + " ('partner_id', 'child_of', commercial_partner_id)," + "]", + readonly=True, + states={'draft': [('readonly', False)]}, + ) + move_id = fields.Many2one( + comodel_name='stock.move', + string='Origin move', + domain="[" + " ('picking_id', '=', picking_id)," + " ('picking_id', '!=', False)" + "]", + readonly=True, + states={'draft': [('readonly', False)]}, + ) + product_id = fields.Many2one( + comodel_name="product.product", + domain=[('type', 'in', ['consu', 'product'])], + ) + product_uom_qty = fields.Float( + string="Quantity", + required=True, + default=1.0, + digits=dp.get_precision('Product Unit of Measure'), + readonly=True, + states={'draft': [('readonly', False)]}, + ) + product_uom = fields.Many2one( + comodel_name="uom.uom", + string="UoM", + required=True, + readonly=True, + states={'draft': [('readonly', False)]}, + default=lambda self: self.env.ref('uom.product_uom_unit').id, + ) + procurement_group_id = fields.Many2one( + comodel_name='procurement.group', + string='Procurement group', + readonly=True, + states={ + 'draft': [('readonly', False)], + 'confirmed': [('readonly', False)], + 'received': [('readonly', False)], + }, + ) + priority = fields.Selection( + string="Priority", + selection=PROCUREMENT_PRIORITIES, + default="1", + readonly=True, + states={'draft': [('readonly', False)]}, + ) + operation_id = fields.Many2one( + comodel_name='rma.operation', + string='Requested operation', + ) + state = fields.Selection( + [ + ("draft", "Draft"), + ("confirmed", "Confirmed"), + ("received", "Received"), + ("waiting_refund", "Waiting for refund"), + ("waiting_return", "Waiting for return"), + ("waiting_replacement", "Waiting for replacement"), + ("refunded", "Refunded"), + ("returned", "Returned"), + ("replaced", "Replaced"), + ("locked", "Locked"), + ("cancelled", "Canceled"), + ], + default="draft", + copy=False, + track_visibility="onchange", + ) + description = fields.Text( + states={ + 'locked': [('readonly', True)], + 'cancelled': [('readonly', True)], + }, + ) + # Reception fields + location_id = fields.Many2one( + comodel_name='stock.location', + domain=_domain_location_id, + readonly=True, + states={'draft': [('readonly', False)]}, + ) + warehouse_id = fields.Many2one( + comodel_name='stock.warehouse', + compute="_compute_warehouse_id", + store=True, + ) + reception_move_id = fields.Many2one( + comodel_name='stock.move', + string='Reception move', + copy=False, + ) + # Refund fields + refund_id = fields.Many2one( + comodel_name='account.invoice', + string='Refund', + readonly=True, + copy=False, + ) + refund_line_id = fields.Many2one( + comodel_name='account.invoice.line', + string='Refund line', + readonly=True, + copy=False, + ) + can_be_refunded = fields.Boolean( + compute="_compute_can_be_refunded" + ) + # Delivery fields + delivery_move_ids = fields.One2many( + comodel_name='stock.move', + inverse_name='rma_id', + string='Delivery reservation', + readonly=True, + copy=False, + ) + delivery_picking_count = fields.Integer( + string='Delivery count', + compute='_compute_delivery_picking_count', + ) + delivered_qty = fields.Float( + string="Delivered qty", + digits=dp.get_precision('Product Unit of Measure'), + compute='_compute_delivered_qty', + store=True, + ) + delivered_qty_done = fields.Float( + string="Delivered qty done", + digits=dp.get_precision('Product Unit of Measure'), + compute='_compute_delivered_qty', + ) + can_be_returned = fields.Boolean( + compute="_compute_can_be_returned", + ) + can_be_replaced = fields.Boolean( + compute="_compute_can_be_replaced", + ) + can_be_locked = fields.Boolean( + compute="_compute_can_be_locked", + ) + remaining_qty = fields.Float( + string="Remaining delivered qty", + digits=dp.get_precision('Product Unit of Measure'), + compute='_compute_remaining_qty', + ) + remaining_qty_to_done = fields.Float( + string="Remaining delivered qty to done", + digits=dp.get_precision('Product Unit of Measure'), + compute='_compute_remaining_qty', + ) + # Split fields + can_be_split = fields.Boolean( + compute="_compute_can_be_split", + ) + origin_split_rma_id = fields.Many2one( + comodel_name='rma', + string='Extracted from', + readonly=True, + copy=False, + ) + + def _compute_delivery_picking_count(self): + # It is enough to count the moves to know how many pickings + # there are because there will be a unique move linked to the + # same picking and the same rma. + rma_data = self.env['stock.move'].read_group( + [('rma_id', 'in', self.ids)], + ['rma_id', 'picking_id'], + ['rma_id', 'picking_id'], + lazy=False, + ) + mapped_data = Counter(map(lambda r: r['rma_id'][0], rma_data)) + for record in self: + record.delivery_picking_count = mapped_data.get(record.id, 0) + + @api.depends( + 'delivery_move_ids', + 'delivery_move_ids.state', + 'delivery_move_ids.scrapped', + 'delivery_move_ids.product_uom_qty', + 'delivery_move_ids.reserved_availability', + 'delivery_move_ids.quantity_done', + 'delivery_move_ids.product_uom', + 'product_uom', + ) + def _compute_delivered_qty(self): + """ Compute 'delivered_qty' and 'delivered_qty_done' fields. + + delivered_qty: represents the quantity delivery or to be + delivery. For each move in delivery_move_ids the quantity done + is taken, if it is empty the reserved quantity is taken, + otherwise the initial demand is taken. + + delivered_qty_done: represents the quantity delivered and done. + For each 'done' move in delivery_move_ids the quantity done is + taken. This field is used to control when the RMA cam be set + to 'delivered' state. + """ + for record in self: + delivered_qty = 0.0 + delivered_qty_done = 0.0 + for move in record.delivery_move_ids.filtered( + lambda r: r.state != 'cancel' and not r.scrapped): + if move.quantity_done: + quantity_done = move.product_uom._compute_quantity( + move.quantity_done, record.product_uom) + if move.state == 'done': + delivered_qty_done += quantity_done + delivered_qty += quantity_done + elif move.reserved_availability: + delivered_qty += move.product_uom._compute_quantity( + move.reserved_availability, record.product_uom) + elif move.product_uom_qty: + delivered_qty += move.product_uom._compute_quantity( + move.product_uom_qty, record.product_uom) + record.delivered_qty = delivered_qty + record.delivered_qty_done = delivered_qty_done + + @api.depends('product_uom_qty', 'delivered_qty', 'delivered_qty_done') + def _compute_remaining_qty(self): + """ Compute 'remaining_qty' and 'remaining_qty_to_done' fields. + + remaining_qty: is used to set a default quantity of replacing + or returning of product to the customer. + + remaining_qty_to_done: the aim of this field to control when the + RMA cam be set to 'delivered' state. An RMA with + remaining_qty_to_done <= 0 can be set to 'delivery'. It is used + in stock.move._action_done method of stock.move and + rma.extract_quantity. + """ + for r in self: + r.remaining_qty = r.product_uom_qty - r.delivered_qty + r.remaining_qty_to_done = r.product_uom_qty - r.delivered_qty_done + + @api.depends( + 'state', + ) + def _compute_can_be_refunded(self): + """ Compute 'can_be_refunded'. This field controls the visibility + of 'Refund' button in the rma form view and determinates if + an rma can be refunded. It is used in rma.action_refund method. + """ + for record in self: + record.can_be_refunded = record.state == 'received' + + @api.depends('remaining_qty', 'state') + def _compute_can_be_returned(self): + """ Compute 'can_be_returned'. This field controls the visibility + of the 'Return to customer' button in the rma form + view and determinates if an rma can be returned to the customer. + This field is used in: + rma._compute_can_be_split + rma._ensure_can_be_returned. + """ + for r in self: + r.can_be_returned = (r.state in ['received', 'waiting_return'] + and r.remaining_qty > 0) + + @api.depends('state') + def _compute_can_be_replaced(self): + """ Compute 'can_be_replaced'. This field controls the visibility + of 'Replace' button in the rma form + view and determinates if an rma can be replaced. + This field is used in: + rma._compute_can_be_split + rma._ensure_can_be_replaced. + """ + for r in self: + r.can_be_replaced = r.state in ['received', 'waiting_replacement', + 'replaced'] + + @api.depends('product_uom_qty', 'state', 'remaining_qty', + 'remaining_qty_to_done') + def _compute_can_be_split(self): + """ Compute 'can_be_split'. This field controls the + visibility of 'Split' button in the rma form view and + determinates if an rma can be split. + This field is used in: + rma._ensure_can_be_split + """ + for r in self: + if (r.product_uom_qty > 1 + and ((r.state == 'waiting_return' and r.remaining_qty > 0) + or (r.state == 'waiting_replacement' + and r.remaining_qty_to_done > 0))): + r.can_be_split = True + else: + r.can_be_split = False + + @api.depends('remaining_qty_to_done', 'state') + def _compute_can_be_locked(self): + for r in self: + r.can_be_locked = (r.remaining_qty_to_done > 0 + and r.state in ['received', 'waiting_refund', + 'waiting_return', + 'waiting_replacement']) + + @api.depends('location_id') + def _compute_warehouse_id(self): + for record in self.filtered('location_id'): + record.warehouse_id = self.env['stock.warehouse'].search( + [('rma_loc_id', 'parent_of', record.location_id.id)], limit=1) + + def _compute_access_url(self): + for record in self: + record.access_url = '/my/rmas/{}'.format(record.id) + + # Constrains methods (@api.constrains) + @api.constrains('state', 'partner_id', 'partner_invoice_id', 'product_id') + def _check_required_after_draft(self): + """ Check that RMAs are being created or edited with the + necessary fields filled out. Only applies to 'Draft' and + 'Cancelled' states. + """ + rma = self.filtered(lambda r: r.state not in ['draft', 'cancelled']) + rma._ensure_required_fields() + + # onchange methods (@api.onchange) + @api.onchange("user_id") + def _onchange_user_id(self): + if self.user_id: + self.team_id = self.env['rma.team'].sudo().search([ + '|', + ('user_id', '=', self.user_id.id), + ('member_ids', '=', self.user_id.id), + '|', + ('company_id', '=', False), + ('company_id', 'child_of', self.company_id.ids) + ], limit=1) + else: + self.team_id = False + + @api.onchange("partner_id") + def _onchange_partner_id(self): + self.picking_id = False + partner_invoice_id = False + if self.partner_id: + address = self.partner_id.address_get(['invoice']) + partner_invoice_id = address.get('invoice', False) + self.partner_invoice_id = partner_invoice_id + + @api.onchange("picking_id") + def _onchange_picking_id(self): + location = False + if self.picking_id: + warehouse = self.picking_id.picking_type_id.warehouse_id + location = warehouse.rma_loc_id.id + self.location_id = location + self.move_id = False + self.product_id = False + + @api.onchange("move_id") + def _onchange_move_id(self): + if self.move_id: + self.product_id = self.move_id.product_id + self.product_uom_qty = self.move_id.product_uom_qty + self.product_uom = self.move_id.product_uom + + @api.onchange("product_id") + def _onchange_product_id(self): + domain_product_uom = [] + if self.product_id: + # Set UoM and UoM domain (product_uom) + domain_product_uom = [ + ('category_id', '=', self.product_id.uom_id.category_id.id) + ] + if (not self.product_uom + or self.product_id.uom_id.id != self.product_uom.id): + self.product_uom = self.product_id.uom_id + # Set stock location (location_id) + user = self.env.user + if (not user.has_group('stock.group_stock_multi_locations') + and not self.location_id): + # If this condition is True, it is because a picking is not set + company = self.company_id or self.env.user.company_id + warehouse = self.env['stock.warehouse'].search( + [('company_id', '=', company.id)], limit=1) + self.location_id = warehouse.rma_loc_id.id + return {'domain': {'product_uom': domain_product_uom}} + + # CRUD methods (ORM overrides) + @api.model + def create(self, vals): + if vals.get('name', _('New')) == _('New'): + ir_sequence = self.env['ir.sequence'] + if 'company_id' in vals: + ir_sequence = ir_sequence.with_context( + force_company=vals['company_id']) + vals['name'] = ir_sequence.next_by_code('rma') + return super().create(vals) + + def copy(self, default=None): + team = super().copy(default) + for follower in self.message_follower_ids: + team.message_subscribe(partner_ids=follower.partner_id.ids, + subtype_ids=follower.subtype_ids.ids) + return team + + def unlink(self): + if self.filtered(lambda r: r.state != 'draft'): + raise ValidationError( + _("You cannot delete RMAs that are not in draft state")) + return super().unlink() + + # Action methods + def action_rma_send(self): + self.ensure_one() + template = self.env.ref('rma.mail_template_rma_notification', False) + form = self.env.ref('mail.email_compose_message_wizard_form', False) + ctx = { + 'default_model': 'rma', + 'default_res_id': self.ids[0], + 'default_use_template': bool(template), + 'default_template_id': template and template.id or False, + 'default_composition_mode': 'comment', + 'mark_rma_as_sent': True, + 'model_description': 'RMA', + 'force_email': True + } + return { + 'type': 'ir.actions.act_window', + 'view_type': 'form', + 'view_mode': 'form', + 'res_model': 'mail.compose.message', + 'views': [(form.id, 'form')], + 'view_id': form.id, + 'target': 'new', + 'context': ctx, + } + + def action_confirm(self): + """Invoked when 'Confirm' button in rma form view is clicked.""" + self.ensure_one() + self._ensure_required_fields() + if self.state == 'draft': + if self.picking_id: + reception_move = self._create_receptions_from_picking() + else: + reception_move = self._create_receptions_from_product() + self.write({ + 'reception_move_id': reception_move.id, + 'state': 'confirmed', + }) + if self.partner_id not in self.message_partner_ids: + self.message_subscribe([self.partner_id.id]) + + def action_refund(self): + """Invoked when 'Refund' button in rma form view is clicked + and 'rma_refund_action_server' server action is run. + """ + group_dict = {} + for record in self.filtered("can_be_refunded"): + key = (record.partner_invoice_id.id, record.company_id.id) + group_dict.setdefault(key, self.env['rma']) + group_dict[key] |= record + for rmas in group_dict.values(): + origin = ', '.join(rmas.mapped('name')) + invoice_form = Form(self.env['account.invoice'].with_context( + default_type='out_refund', + company_id=rmas[0].company_id.id, + )) + rmas[0]._prepare_refund(invoice_form, origin) + refund = invoice_form.save() + for rma in rmas: + with invoice_form.invoice_line_ids.new() as line_form: + rma._prepare_refund_line(line_form) + # rma_id is not present in the form view, so we need to get + # the 'values to save' to add the rma id and use the + # create method instead of save the form. We also need + # the new refund line id to be linked to the rma. + refund_vals = invoice_form._values_to_save(all_fields=True) + line_vals = refund_vals['invoice_line_ids'][-1][2] + line_vals.update(invoice_id=refund.id, rma_id=rma.id) + line = self.env['account.invoice.line'].create(line_vals) + rma.write({ + 'refund_line_id': line.id, + 'refund_id': refund.id, + 'state': 'waiting_refund', + }) + refund.message_post_with_view( + 'mail.message_origin_link', + values={'self': refund, 'origin': rmas}, + subtype_id=self.env.ref('mail.mt_note').id, + ) + + def action_replace(self): + """Invoked when 'Replace' button in rma form view is clicked.""" + self.ensure_one() + self._ensure_can_be_replaced() + action = self.env.ref("rma.rma_delivery_wizard_action").read()[0] + action['name'] = 'Replace product(s)' + action['context'] = dict(self.env.context) + action['context'].update( + active_id=self.id, + active_ids=self.ids, + rma_delivery_type='replace', + ) + return action + + def action_return(self): + """Invoked when 'Return to customer' button in rma form + view is clicked. + """ + self.ensure_one() + self._ensure_can_be_returned() + action = self.env.ref("rma.rma_delivery_wizard_action").read()[0] + action['context'] = dict(self.env.context) + action['context'].update( + active_id=self.id, + active_ids=self.ids, + rma_delivery_type='return', + ) + return action + + def action_split(self): + """Invoked when 'Split' button in rma form view is clicked.""" + self.ensure_one() + self._ensure_can_be_split() + action = self.env.ref("rma.rma_split_wizard_action").read()[0] + action['context'] = dict(self.env.context) + action['context'].update(active_ids=self.ids) + return action + + def action_cancel(self): + """Invoked when 'Cancel' button in rma form view is clicked.""" + self.mapped('reception_move_id')._action_cancel() + self.write({'state': 'cancelled'}) + + def action_draft(self): + cancelled_rma = self.filtered(lambda r: r.state == 'cancelled') + cancelled_rma.write({'state': 'draft'}) + + def action_lock(self): + """Invoked when 'Lock' button in rma form view is clicked.""" + self.filtered("can_be_locked").write({'state': 'locked'}) + + def action_unlock(self): + """Invoked when 'Unlock' button in rma form view is clicked.""" + locked_rma = self.filtered(lambda r: r.state == 'locked') + locked_rma.write({'state': 'received'}) + + def action_preview(self): + """Invoked when 'Preview' button in rma form view is clicked.""" + self.ensure_one() + return { + 'type': 'ir.actions.act_url', + 'target': 'self', + 'url': self.get_portal_url(), + } + + def action_view_receipt(self): + """Invoked when 'Receipt' smart button in rma form view is clicked.""" + self.ensure_one() + action = self.env.ref('stock.action_picking_tree_all').read()[0] + action.update( + res_id=self.reception_move_id.picking_id.id, + view_mode="form", + view_id=False, + views=False, + ) + return action + + def action_view_refund(self): + """Invoked when 'Refund' smart button in rma form view is clicked.""" + self.ensure_one() + return { + 'name': _('Refund'), + 'type': 'ir.actions.act_window', + 'view_type': 'form', + 'view_mode': 'form', + 'res_model': 'account.invoice', + 'views': [(self.env.ref('account.invoice_form').id, 'form')], + 'res_id': self.refund_id.id, + } + + def action_view_delivery(self): + """Invoked when 'Delivery' smart button in rma form view is clicked.""" + action = self.env.ref('stock.action_picking_tree_all').read()[0] + picking = self.delivery_move_ids.mapped('picking_id') + if len(picking) > 1: + action['domain'] = [('id', 'in', picking.ids)] + elif picking: + action.update( + res_id=picking.id, + view_mode="form", + view_id=False, + views=False, + ) + return action + + # Validation business methods + def _ensure_required_fields(self): + """ This method is used to ensure the following fields are not empty: + ['partner_id', 'partner_invoice_id', 'product_id', 'location_id'] + + This method is intended to be called on confirm RMA action and is + invoked by: + rma._check_required_after_draft + rma.action_confirm + """ + ir_translation = self.env['ir.translation'] + required = ['partner_id', 'partner_invoice_id', 'product_id', + 'location_id'] + for record in self: + desc = "" + for field in filter(lambda item: not record[item], required): + desc += "\n%s" % ir_translation.get_field_string("rma")[field] + if desc: + raise ValidationError(_('Required field(s):%s') % desc) + + def _ensure_can_be_returned(self): + """ This method is intended to be invoked after user click on + 'Replace' or 'Return to customer' button (before the delivery wizard + is launched) and after confirm the wizard. + + This method is invoked by: + rma.action_replace + rma.action_return + rma.create_replace + rma.create_return + """ + if len(self) == 1: + if not self.can_be_returned: + raise ValidationError( + _("This RMA cannot perform a return.")) + elif not self.filtered("can_be_returned"): + raise ValidationError( + _("None of the selected RMAs can perform a return.")) + + def _ensure_can_be_replaced(self): + """ This method is intended to be invoked after user click on + 'Replace' button (before the delivery wizard + is launched) and after confirm the wizard. + + This method is invoked by: + rma.action_replace + rma.create_replace + """ + if len(self) == 1: + if not self.can_be_replaced: + raise ValidationError( + _("This RMA cannot perform a replacement.")) + elif not self.filtered("can_be_replaced"): + raise ValidationError( + _("None of the selected RMAs can perform a replacement.")) + + def _ensure_can_be_split(self): + """intended to be called before launch and after save the split wizard. + invoked by: + rma.action_split + rma.extract_quantity + """ + self.ensure_one() + if not self.can_be_split: + raise ValidationError(_("This RMA cannot be split.")) + + def _ensure_qty_to_return(self, qty=None, uom=None): + """ This method is intended to be invoked after confirm the wizard. + invoked by: rma.create_return + """ + if qty and uom: + if uom != self.product_uom: + qty = uom._compute_quantity(qty, self.product_uom) + if qty > self.remaining_qty: + raise ValidationError( + _("The quantity to return is greater than " + "remaining quantity.")) + + def _ensure_qty_to_extract(self, qty, uom): + """ This method is intended to be invoked after confirm the wizard. + invoked by: rma.extract_quantity + """ + to_split_uom_qty = qty + if uom != self.product_uom: + to_split_uom_qty = uom._compute_quantity(qty, self.product_uom) + if to_split_uom_qty > self.remaining_qty: + raise ValidationError( + _("Quantity to extract cannot be greater than remaining " + "delivery quantity (%s %s)") + % (self.remaining_qty, self.product_uom.name) + ) + + # Reception business methods + def _create_receptions_from_picking(self): + self.ensure_one() + create_vals = {} + if self.location_id: + create_vals['location_id'] = self.location_id.id + return_wizard = self.env['stock.return.picking'].with_context( + active_id=self.picking_id.id, + active_ids=self.picking_id.ids, + ).create(create_vals) + return_wizard.product_return_moves.filtered( + lambda r: r.move_id != self.move_id + ).unlink() + return_line = return_wizard.product_return_moves + return_line.quantity = self.product_uom_qty + # set_rma_picking_type is to override the copy() method of stock + # picking and change the default picking type to rma picking type. + picking_action = return_wizard.with_context( + set_rma_picking_type=True).create_returns() + picking_id = picking_action['res_id'] + picking = self.env['stock.picking'].browse(picking_id) + picking.origin = "%s (%s)" % (self.name, picking.origin) + move = picking.move_lines + move.priority = self.priority + return move + + def _create_receptions_from_product(self): + self.ensure_one() + picking_form = Form( + recordp=self.env['stock.picking'].with_context( + default_picking_type_id=self.warehouse_id.rma_in_type_id.id), + view="stock.view_picking_form", + ) + self._prepare_picking(picking_form) + picking = picking_form.save() + picking.action_confirm() + picking.action_assign() + picking.message_post_with_view( + 'mail.message_origin_link', + values={'self': picking, 'origin': self}, + subtype_id=self.env.ref('mail.mt_note').id, + ) + return picking.move_lines + + def _prepare_picking(self, picking_form): + picking_form.company_id = self.company_id + picking_form.origin = self.name + picking_form.partner_id = self.partner_id + picking_form.location_dest_id = self.location_id + with picking_form.move_ids_without_package.new() as move_form: + move_form.product_id = self.product_id + move_form.product_uom_qty = self.product_uom_qty + move_form.product_uom = self.product_uom + + # Extract business methods + def extract_quantity(self, qty, uom): + self.ensure_one() + self._ensure_can_be_split() + self._ensure_qty_to_extract(qty, uom) + self.product_uom_qty -= uom._compute_quantity(qty, self.product_uom) + if self.remaining_qty_to_done <= 0: + if self.state == 'waiting_return': + self.state = 'returned' + elif self.state == 'waiting_replacement': + self.state = 'replaced' + extracted_rma = self.copy({ + 'origin': self.name, + 'product_uom_qty': qty, + 'product_uom': uom.id, + 'state': 'received', + 'reception_move_id': self.reception_move_id.id, + 'origin_split_rma_id': self.id, + }) + extracted_rma.message_post_with_view( + 'mail.message_origin_link', + values={'self': extracted_rma, 'origin': self}, + subtype_id=self.env.ref('mail.mt_note').id, + ) + self.message_post( + body=_( + 'Split: %s has been created.' + ) % ( + extracted_rma.id, + extracted_rma.name, + ) + ) + return extracted_rma + + # Refund business methods + def _prepare_refund(self, invoice_form, origin): + """ Hook method for preparing the refund Form. + + This method could be override in order to add new custom field + values in the refund creation. + + invoked by: + rma.action_refund + """ + self.ensure_one() + invoice_form.company_id = self.company_id + invoice_form.partner_id = self.partner_invoice_id + invoice_form.origin = origin + + def _prepare_refund_line(self, line_form): + """ Hook method for preparing a refund line Form. + + This method could be override in order to add new custom field + values in the refund line creation. + + invoked by: + rma.action_refund + """ + self.ensure_one() + line_form.product_id = self.product_id + line_form.quantity = self.product_uom_qty + line_form.uom_id = self.product_uom + line_form.price_unit = self._get_refund_line_price_unit() + + def _get_refund_line_price_unit(self): + """To be overriden in a third module with the proper origin values + in case a sale order is linked to the original move""" + return self.product_id.lst_price + + # Returning business methods + def create_return(self, scheduled_date, qty=None, uom=None): + """Intended to be invoked by the delivery wizard""" + self._ensure_can_be_returned() + self._ensure_qty_to_return(qty, uom) + group_dict = {} + for record in self.filtered('can_be_returned'): + key = (record.partner_id.id, record.company_id.id, + record.warehouse_id) + group_dict.setdefault(key, self.env['rma']) + group_dict[key] |= record + for rmas in group_dict.values(): + origin = ', '.join(rmas.mapped('name')) + rma_out_type = rmas[0].warehouse_id.rma_out_type_id + picking_form = Form( + recordp=self.env['stock.picking'].with_context( + default_picking_type_id=rma_out_type.id), + view="stock.view_picking_form", + ) + rmas[0]._prepare_returning_picking(picking_form, origin) + picking = picking_form.save() + for rma in rmas: + with picking_form.move_ids_without_package.new() as move_form: + rma._prepare_returning_move( + move_form, scheduled_date, qty, uom) + # rma_id is not present in the form view, so we need to get + # the 'values to save' to add the rma id and use the + # create method intead of save the form. + picking_vals = picking_form._values_to_save(all_fields=True) + move_vals = picking_vals['move_ids_without_package'][-1][2] + move_vals.update( + picking_id=picking.id, + rma_id=rma.id, + move_orig_ids=[(4, rma.reception_move_id.id)], + ) + self.env['stock.move'].create(move_vals) + rma.message_post( + body=_( + 'Return: %s has been created.' + ) % (picking.id, picking.name) + ) + picking.action_confirm() + picking.action_assign() + picking.message_post_with_view( + 'mail.message_origin_link', + values={'self': picking, 'origin': rmas}, + subtype_id=self.env.ref('mail.mt_note').id, + ) + self.write({'state': 'waiting_return'}) + + def _prepare_returning_picking(self, picking_form, origin=None): + picking_form.picking_type_id = self.warehouse_id.rma_out_type_id + picking_form.company_id = self.company_id + picking_form.origin = origin or self.name + picking_form.partner_id = self.partner_id + + def _prepare_returning_move(self, move_form, scheduled_date, + quantity=None, uom=None): + move_form.product_id = self.product_id + move_form.product_uom_qty = quantity or self.product_uom_qty + move_form.product_uom = uom or self.product_uom + move_form.date_expected = scheduled_date + + # Replacing business methods + def create_replace(self, scheduled_date, warehouse, product, qty, uom): + """Intended to be invoked by the delivery wizard""" + self.ensure_one() + self._ensure_can_be_replaced() + moves_before = self.delivery_move_ids + self._action_launch_stock_rule(scheduled_date, warehouse, product, + qty, uom) + new_move = self.delivery_move_ids - moves_before + if new_move: + self.reception_move_id.move_dest_ids = [(4, new_move.id)] + self.message_post( + body=_( + 'Replacement: ' + 'Move %s (Picking %s) ' + 'has been created.' + ) % (new_move.id, new_move.name_get()[0][1], + new_move.picking_id.id, new_move.picking_id.name) + ) + else: + self.message_post( + body=_( + 'Replacement:
' + 'Product %s
' + 'Quantity %f %s
' + 'This replacement did not create a new move, but one of ' + 'the previously created moves was updated with this data.' + ) % (product.id, product.display_name, qty, uom.name) + ) + if self.state != 'waiting_replacement': + self.state = 'waiting_replacement' + + def _action_launch_stock_rule( + self, + scheduled_date, + warehouse, + product, + qty, + uom, + ): + """ Creates a delivery picking and launch stock rule. It is invoked by: + rma.create_replace + """ + self.ensure_one() + if self.product_id.type not in ('consu', 'product'): + return + if not self.procurement_group_id: + self.procurement_group_id = self.env['procurement.group'].create({ + 'name': self.name, + 'move_type': 'direct', + 'partner_id': self.partner_id.id, + }).id + values = self._prepare_procurement_values( + self.procurement_group_id, scheduled_date, warehouse) + self.env['procurement.group'].run( + product, + qty, + uom, + self.partner_id.property_stock_customer, + self.product_id.display_name, + self.procurement_group_id.name, + values, + ) + + def _prepare_procurement_values( + self, + group_id, + scheduled_date, + warehouse, + ): + self.ensure_one() + return { + 'company_id': self.company_id.id, + 'group_id': group_id, + 'date_planned': scheduled_date, + 'warehouse_id': warehouse, + 'partner_id': self.partner_id.id, + 'rma_id': self.id, + 'priority': self.priority, + } + + # Mail business methods + def message_new(self, msg_dict, custom_values=None): + """Extract the needed values from an incoming rma emails data-set + to be used to create an RMA. + """ + if custom_values is None: + custom_values = {} + subject = msg_dict.get('subject', '') + body = html2plaintext(msg_dict.get('body', '')) + desc = _("E-mail subject: %s\n\nE-mail body:\n%s") % (subject, body) + defaults = { + 'description': desc, + 'name': _('New'), + 'origin': _('Incoming e-mail'), + } + if msg_dict.get('author_id'): + partner = self.env['res.partner'].browse(msg_dict.get('author_id')) + defaults.update( + partner_id=partner.id, + partner_invoice_id=partner.address_get( + ['invoice']).get('invoice', False), + ) + if msg_dict.get("priority"): + defaults["priority"] = msg_dict.get("priority") + defaults.update(custom_values) + rma = super().message_new(msg_dict, custom_values=defaults) + if (rma.user_id + and rma.user_id.partner_id not in rma.message_partner_ids): + rma.message_subscribe([rma.user_id.partner_id.id]) + return rma + + @api.returns('mail.message', lambda value: value.id) + def message_post(self, **kwargs): + """ Set 'sent' field to True when an email is sent from rma form + view. This field (sent) is used to set the appropriate style to the + 'Send by Email' button in the rma form view. + """ + if self.env.context.get('mark_rma_as_sent'): + self.write({'sent': True}) + # mail_post_autofollow=True to include email recipient contacts as + # RMA followers + self_with_context = self.with_context(mail_post_autofollow=True) + return super(Rma, self_with_context).message_post(**kwargs) + + # Reporting business methods + def _get_report_base_filename(self): + self.ensure_one() + return 'RMA Report - %s' % self.name + + # Other business methods + def update_received_state(self): + """ Invoked by: + [stock.move].unlink + [stock.move]._action_cancel + """ + rma = self.filtered(lambda r: r.delivered_qty == 0) + if rma: + rma.write({'state': 'received'}) + + def update_replaced_state(self): + """ Invoked by: + [stock.move]._action_done + [stock.move].unlink + [stock.move]._action_cancel + """ + rma = self.filtered( + lambda r: (r.state == 'waiting_replacement' + and 0 >= r.remaining_qty_to_done == r.remaining_qty)) + if rma: + rma.write({'state': 'replaced'}) + + def update_returned_state(self): + """ Invoked by [stock.move]._action_done""" + rma = self.filtered( + lambda r: (r.state == 'waiting_return' + and r.remaining_qty_to_done <= 0)) + if rma: + rma.write({'state': 'returned'}) diff --git a/rma/models/rma_operation.py b/rma/models/rma_operation.py new file mode 100644 index 000000000..85271aebf --- /dev/null +++ b/rma/models/rma_operation.py @@ -0,0 +1,15 @@ +# Copyright 2020 Tecnativa - Ernesto Tejeda +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class RmaOperation(models.Model): + _name = "rma.operation" + _description = "RMA requested operation" + + name = fields.Char(required=True, translate=True) + + _sql_constraints = [ + ('name_uniq', 'unique (name)', "That operation name already exists !"), + ] diff --git a/rma/models/rma_team.py b/rma/models/rma_team.py new file mode 100644 index 000000000..31b600972 --- /dev/null +++ b/rma/models/rma_team.py @@ -0,0 +1,56 @@ +# Copyright 2020 Tecnativa - Ernesto Tejeda +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import _, fields, models + + +class RmaTeam(models.Model): + _name = "rma.team" + _inherit = ['mail.alias.mixin', 'mail.thread'] + _description = "RMA Team" + _order = "name" + + name = fields.Char( + required=True, + translate=True, + ) + active = fields.Boolean( + default=True, + help="If the active field is set to false, it will allow you " + "to hide the RMA Team without removing it.", + ) + company_id = fields.Many2one( + comodel_name="res.company", + string="Company", + default=lambda self: self.env.user.company_id, + ) + user_id = fields.Many2one( + comodel_name="res.users", + string="Team Leader", + domain=[('share', '=', False)], + default=lambda self: self.env.user, + ) + member_ids = fields.One2many( + comodel_name='res.users', + inverse_name='rma_team_id', + string='Team Members', + ) + + def copy(self, default=None): + if default is None: + default = {} + if not default.get('name'): + default['name'] = _("%s (copy)") % self.name + team = super().copy(default) + for follower in self.message_follower_ids: + team.message_subscribe(partner_ids=follower.partner_id.ids, + subtype_ids=follower.subtype_ids.ids) + return team + + def get_alias_model_name(self, vals): + return vals.get('alias_model', 'rma') + + def get_alias_values(self): + values = super().get_alias_values() + values['alias_defaults'] = {'team_id': self.id} + return values diff --git a/rma/models/stock_move.py b/rma/models/stock_move.py new file mode 100644 index 000000000..8ab511cec --- /dev/null +++ b/rma/models/stock_move.py @@ -0,0 +1,96 @@ +# Copyright 2020 Tecnativa - Ernesto Tejeda +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import _, api, fields, models +from odoo.exceptions import ValidationError + + +class StockMove(models.Model): + _inherit = "stock.move" + + # RMAs that were created from the delivery move + rma_ids = fields.One2many( + comodel_name='rma', + inverse_name='move_id', + string='RMAs', + copy=False, + ) + # RMAs linked to the incoming movement from client + rma_receiver_ids = fields.One2many( + comodel_name='rma', + inverse_name='reception_move_id', + string='RMA receivers', + copy=False, + ) + # RMA that create the delivery movement to the customer + rma_id = fields.Many2one( + comodel_name='rma', + string='RMA return', + copy=False, + ) + + def unlink(self): + rma_receiver = self.mapped('rma_receiver_ids') + rma = self.mapped('rma_id') + res = super().unlink() + rma_receiver.write({'state': 'draft'}) + rma.update_received_state() + rma.update_replaced_state() + return res + + def _action_cancel(self): + res = super()._action_cancel() + cancelled_moves = self.filtered(lambda r: r.state == 'cancel') + cancelled_moves.mapped('rma_receiver_ids').write({'state': 'draft'}) + cancelled_moves.mapped('rma_id').update_received_state() + cancelled_moves.mapped('rma_id').update_replaced_state() + return res + + def _action_done(self): + """ Avoids to validate stock.move with less quantity than the + quantity in the linked receiver RMA. It also set the appropriated + linked RMA to 'received' or 'delivered'. + """ + for move in self.filtered( + lambda r: r.state not in ('done', 'cancel')): + rma_receiver = move.rma_receiver_ids + if (rma_receiver + and move.quantity_done != rma_receiver.product_uom_qty): + raise ValidationError( + _("The quantity done for the product '%s' must " + "be equal to its initial demand because the " + "stock move is linked to an RMA (%s).") + % (move.product_id.name, move.rma_receiver_ids.name) + ) + res = super()._action_done() + move_done = self.filtered(lambda r: r.state == 'done') + # set RMAs as received + to_be_received = move_done.mapped('rma_receiver_ids').filtered( + lambda r: r.state == 'confirmed') + to_be_received.write({'state': 'received'}) + # set RMAs as delivered + move_done.mapped('rma_id').update_replaced_state() + move_done.mapped('rma_id').update_returned_state() + return res + + @api.model + def _prepare_merge_moves_distinct_fields(self): + """ The main use is that launched delivery RMAs doesn't merge + two moves if they are linked to a different RMAs. + """ + return super()._prepare_merge_moves_distinct_fields() + ['rma_id'] + + def _prepare_move_split_vals(self, qty): + """ Intended to the backport of picking linked to RMAs propagates the + RMA link id. + """ + res = super()._prepare_move_split_vals(qty) + res['rma_id'] = self.rma_id.id + return res + + +class StockRule(models.Model): + _inherit = 'stock.rule' + + def _get_custom_move_fields(self): + return super()._get_custom_move_fields() + ['rma_id'] diff --git a/rma/models/stock_picking.py b/rma/models/stock_picking.py new file mode 100644 index 000000000..e341be0b6 --- /dev/null +++ b/rma/models/stock_picking.py @@ -0,0 +1,41 @@ +# Copyright 2020 Tecnativa - Ernesto Tejeda +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class StockPicking(models.Model): + _inherit = 'stock.picking' + + rma_count = fields.Integer( + string='RMA count', + compute='_compute_rma_count', + ) + + def _compute_rma_count(self): + for rec in self: + rec.rma_count = len(rec.move_lines.mapped('rma_ids')) + + def copy(self, default=None): + if self.env.context.get('set_rma_picking_type'): + location_dest_id = default['location_dest_id'] + warehouse = self.env['stock.warehouse'].search( + [('rma_loc_id', 'parent_of', location_dest_id)], limit=1) + if warehouse: + default['picking_type_id'] = warehouse.rma_in_type_id.id + return super().copy(default) + + def action_view_rma(self): + self.ensure_one() + action = self.env.ref('rma.rma_action').read()[0] + rma = self.move_lines.mapped('rma_ids') + if len(rma) == 1: + action.update( + res_id=rma.id, + view_mode="form", + view_id=False, + views=False, + ) + else: + action['domain'] = [('id', 'in', rma.ids)] + return action diff --git a/rma/models/stock_warehouse.py b/rma/models/stock_warehouse.py new file mode 100644 index 000000000..7c0716d23 --- /dev/null +++ b/rma/models/stock_warehouse.py @@ -0,0 +1,118 @@ +# Copyright 2020 Tecnativa - Ernesto Tejeda +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import fields, models, _ + + +class StockWarehouse(models.Model): + _inherit = 'stock.warehouse' + + # This is a strategic field used to create an rma location + # and rma operation types in existing warehouses when + # installing this module. + rma = fields.Boolean( + 'RMA', + default=True, + help="RMA related products can be stored in this warehouse.") + rma_in_type_id = fields.Many2one( + comodel_name='stock.picking.type', + string='RMA In Type', + ) + rma_out_type_id = fields.Many2one( + comodel_name='stock.picking.type', + string='RMA Out Type', + ) + rma_loc_id = fields.Many2one( + comodel_name='stock.location', + string='RMA Location', + ) + + def _get_locations_values(self, vals): + values = super()._get_locations_values(vals) + values.update({ + 'rma_loc_id': { + 'name': 'RMA', + 'active': True, + 'return_location': True, + 'usage': 'internal', + 'company_id': vals.get('company_id', self.company_id.id), + 'location_id': self.view_location_id.id, + }, + }) + return values + + def _get_sequence_values(self): + values = super()._get_sequence_values() + values.update({ + 'rma_in_type_id': { + 'name': self.name + ' ' + _('Sequence RMA in'), + 'prefix': self.code + '/RMA/IN/', 'padding': 5, + 'company_id': self.company_id.id, + }, + 'rma_out_type_id': { + 'name': self.name + ' ' + _('Sequence RMA out'), + 'prefix': self.code + '/RMA/OUT/', 'padding': 5, + 'company_id': self.company_id.id, + }, + }) + return values + + def _update_name_and_code(self, new_name=False, new_code=False): + for warehouse in self: + sequence_data = warehouse._get_sequence_values() + warehouse.rma_in_type_id.sequence_id.write( + sequence_data['rma_in_type_id']) + warehouse.rma_in_type_id.sequence_id.write( + sequence_data['rma_out_type_id']) + + def _get_picking_type_create_values(self, max_sequence): + data, next_sequence = super()._get_picking_type_create_values( + max_sequence) + data.update({ + 'rma_in_type_id': { + 'name': _('RMA Receipts'), + 'code': 'incoming', + 'use_create_lots': True, + 'use_existing_lots': False, + 'default_location_src_id': False, + 'default_location_dest_id': self.rma_loc_id.id, + 'sequence': max_sequence + 1, + }, + 'rma_out_type_id': { + 'name': _('RMA Delivery Orders'), + 'code': 'outgoing', + 'use_create_lots': False, + 'use_existing_lots': True, + 'default_location_src_id': self.rma_loc_id.id, + 'default_location_dest_id': False, + 'sequence': max_sequence + 2, + }, + }) + return data, max_sequence + 3 + + def _get_picking_type_update_values(self): + data = super()._get_picking_type_update_values() + data.update({ + 'rma_in_type_id': { + 'default_location_dest_id': self.rma_loc_id.id, + }, + 'rma_out_type_id': { + 'default_location_src_id': self.rma_loc_id.id, + }, + }) + return data + + def _create_or_update_sequences_and_picking_types(self): + data = super()._create_or_update_sequences_and_picking_types() + stock_picking_type = self.env['stock.picking.type'] + if 'out_type_id' in data: + rma_out_type = stock_picking_type.browse(data['rma_out_type_id']) + rma_out_type.write({ + 'return_picking_type_id': data.get('rma_in_type_id', False) + }) + if 'rma_in_type_id' in data: + rma_in_type = stock_picking_type.browse(data['rma_in_type_id']) + rma_in_type.write({ + 'return_picking_type_id': data.get('rma_out_type_id', False) + }) + return data diff --git a/rma/readme/CONFIGURE.rst b/rma/readme/CONFIGURE.rst new file mode 100644 index 000000000..c55130468 --- /dev/null +++ b/rma/readme/CONFIGURE.rst @@ -0,0 +1,8 @@ +If you want RMAs to be created from incoming emails, you need to: + +#. Go to *Settings > General Settings*. +#. Check 'External Email Servers' checkbox under *Discuss* section. +#. Set an 'alias domain' and an incoming server. +#. Go to *RMA > Configuration > RMA Team* and select a team or create a new + one. +#. Go to 'Email' tab and set an 'Email Alias'. diff --git a/rma/readme/CONTRIBUTORS.rst b/rma/readme/CONTRIBUTORS.rst new file mode 100644 index 000000000..d40522718 --- /dev/null +++ b/rma/readme/CONTRIBUTORS.rst @@ -0,0 +1,5 @@ +* `Tecnativa `_: + + * Ernesto Tejeda + * Pedro M. Baeza + * David Vidal diff --git a/rma/readme/DESCRIPTION.rst b/rma/readme/DESCRIPTION.rst new file mode 100644 index 000000000..0c0c6fd82 --- /dev/null +++ b/rma/readme/DESCRIPTION.rst @@ -0,0 +1,8 @@ +This module allows you to manage `Return Merchandise Authorization (RMA) +`_. +RMA documents can be created from scratch, from a delivery order or from +an incoming email. Product receptions and returning delivery operations +of the RMA module are fully integrated with the Receipts and Deliveries +Operations of Odoo inventory core module. It also allows you to generate +refunds in the same way as Odoo generates it. +Besides, you have full integration of the RMA documents in the customer portal. diff --git a/rma/readme/ROADMAP.rst b/rma/readme/ROADMAP.rst new file mode 100644 index 000000000..c6cb60298 --- /dev/null +++ b/rma/readme/ROADMAP.rst @@ -0,0 +1,3 @@ +* As soon as the picking is selected, the user should select the move, + but perhaps stock.move _rec_name could be improved to better show what + the product of that move is. diff --git a/rma/readme/USAGE.rst b/rma/readme/USAGE.rst new file mode 100644 index 000000000..6e6194a94 --- /dev/null +++ b/rma/readme/USAGE.rst @@ -0,0 +1,37 @@ +To use this module, you need to: + +#. Go to *RMA > Orders* and create a new RMA. +#. Select a partner, an invoice address, select a product + (or select a picking and a move instead), write a quantity, fill the rest + of the form and click on 'confirm' button in the status bar. +#. You will see an smart button labeled 'Receipt'. Click on that button to see + the reception operation form. +#. If everything is right, validate the operation and go back to the RMA to + see it in a 'received' state. +#. Now you are able to generate a refund, generate a delivery order to return + to the customer the same product or another product as a replacement, split + the RMA by extracting a part of the remaining quantity to another RMA, + preview the RMA in the website. All of these operations can be done by + clicking on the buttons in the status bar. + + * If you click on 'Refund' button, a refund will be created, and it will be + accessible via the smart button labeled Refund. The RMA will be set + automatically to 'Refunded' state when the refund is validated. + * If you click on 'Replace' or 'Return to customer' button instead, + a popup wizard will guide you to create a Delivery order to the client + and this order will be accessible via the smart button labeled Delivery. + The RMA will be set automatically to 'Replaced' or 'Returned' state when + the RMA quantity is equal or lower than the quantity in done delivery + orders linked to it. + +An RMA can also be created from a return of a delivery order: + +#. Select a delivery order and click on 'Return' button to create a return. +#. Check "Create RMAs" checkbox in the returning wizard, select the RMA + stock location and click on 'Return' button. +#. An RMA will be created for each product returned in the previous step. + Every RMA will be in confirmed state and they will + be linked to the returning operation generated previously. + +**Note: An RMA can also be created from an incoming email (See configuration +section).** diff --git a/rma/report/report.xml b/rma/report/report.xml new file mode 100644 index 000000000..5816ab294 --- /dev/null +++ b/rma/report/report.xml @@ -0,0 +1,14 @@ + + + + + + diff --git a/rma/security/ir.model.access.csv b/rma/security/ir.model.access.csv new file mode 100644 index 000000000..0afc4d55b --- /dev/null +++ b/rma/security/ir.model.access.csv @@ -0,0 +1,8 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_rma_team_user_own,rma.team.user.own,model_rma_team,rma_group_user_own,1,0,0,0 +access_rma_team_manager,rma.team.manager,model_rma_team,rma_group_manager,1,1,1,1 +access_rma_portal,rma.portal,model_rma,base.group_portal,1,0,0,0 +access_rma_user_own,rma.user.own,model_rma,rma_group_user_own,1,1,1,0 +access_rma_manager,rma.manager,model_rma,rma_group_manager,1,1,1,1 +access_rma_operation_user_own,rma.operation.user.own,model_rma_operation,rma_group_user_own,1,0,0,0 +access_rma_operation_manager,rma.operation.manager,model_rma_operation,rma_group_manager,1,1,1,1 diff --git a/rma/security/rma_security.xml b/rma/security/rma_security.xml new file mode 100644 index 000000000..5a8037c90 --- /dev/null +++ b/rma/security/rma_security.xml @@ -0,0 +1,60 @@ + + + + + + RMA + Manage Return Merchandise Authorizations (RMAs). + + + + User: Own Documents Only + + + the user will have access to his own data in the RMA application. + + + User: All Documents + + + the user will have access to all records of everyone in the RMA application. + + + Manager + the user will have an access to the RMA configuration as well as statistic reports. + + + + + + + Personal RMAs + + ['|',('user_id','=',user.id),('user_id','=',False)] + + + + All RMAs + + [(1,'=',1)] + + + + + RMA multi-company + + + ['|',('company_id','=',False),('company_id','child_of',[user.company_id.id])] + + + RMA team multi-company + + + ['|',('company_id','=',False),('company_id','child_of',[user.company_id.id])] + + + + + + diff --git a/rma/static/description/icon.png b/rma/static/description/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..2f8ea42a5706573c60ddab7451b99b07babf29e7 GIT binary patch literal 7450 zcmV+#9p&PQP)hA>a-^0)`p8xN5^J)w<{C`P`R^3;{hyG6p78uBr++fFElDDMQt*hzuZw*r&g;}^9q2myvg(J z1@HxyS(0cs7|`9mtxG5@{IY6uYU)dBL0C3dAJnL}*F1qGnCIo|xmm1MM4Qd14w0m< zC|!yof6vj?^7Oa2FHw)t7@yG~1Zr3+3i7$ja)x@g!=ivPb+U+JqNsE+iUN_Sz@jJ+ zsTEKZ%?e-?Ae1gikphg;x+t1vU=&ys8)m{39v{!*)vJk0Or*E9l^Z8dV(#ftYH2bV2Y03pokdYZ)M`;{wS?+)=#r9>8FWUus{LEmn=3LpBoJ1Y}gRIyMHb{JW*uv>eUo&-%jMBMG7$9u+-eV z2|#RSrs9zBP|E%Hv#h+F@c4KI7;jhr`Z_ukO1xT{JR2q4YuFvC z)6sqH8kWAk`2~#8Wa9iN^C%WcqRn6+!fq!zHFe$%E&WLo5rM;Eq03;HlOAE8!1#k@ z1T+o@(J3hkG_F2gttKQajP5VKU~mp55)gxd#cllB?R>g#ALsY%p{J!qp~b1F#6%V? zU5Ypd_i7l{!rbZW<5GYlAgd(N+SG)Op$rV6I-SzWs8q<3#O=$M=UiZx=m|?xXJ=3+ z>9gB8xMvR+l$OS==#&(q5)&nG@72TgeRgq%MlJe*LS&b=?A0?Zf;;lum) zBM1UT+qVbZJx$i5KHt^`ZEeulNYs%dSR{!@AAg+1t5=g!S~_lUL{S`SxkM4>h}T!O zIwRxt$-ID7mTA3ub;uA7&?D@y*|>S~Bv;1RC5`JrFcfxr@yZn%jvPVg>LMa0hVc0K zaRs*bwzSZB`LcU8rUJ}ZM^8;nL@|^xbF9w2sRYIjxT@3!T)TRe&e~ccBO-`SPDZ8C z1UCq{b&FdK4RZ#VDO(d_bU65A7CHCqz~#0!KKQ49;{1mnGSJl(oE;MuHnwHH3NXH* z`OB_dvj!R&xMQ*K{yXn*;iHc*b$18egwHX@-IT97Gs!Z4*|lrNPgn{b|B1le_I7%2 z-3oYFi9E+rmP`x&CRyg=UAvGS4hqZ5$K~9c=!c;d1nj`7<;zJeE+(b8n1rQEgLd>K z7Z>y3i!ahzUr%doErS#6D4iF;xS%<(XAiO@QMhee(0-cnHm@2$EeJS(N1uM0RK1?$ z!a~1UBLbO%5JZuL{CpDf^C|u6SFsKZ(0<_pZFP0DoIQ&?%d$jFYX@tRWe$Gy5wa}% zcJ57ZbFKnHJOFw4(ML%wE+$p4C(6&B2C^*E{>2v<&Yk0CVD_Oj9C4Bqa^xwHdTU{M(_4V97e_jD*EYN(icP}u~Biw!cdeEa2*Uf1J0l_)6 zU-#gHq?VMBs@Jn1E6Z;yakry`t2H%TJ$H_)=g!g9)rHAw#bmW|%V;F4xR}_53-4Km zx}!&FI&+4M!a}n3dg79jhJ7EoU;%j>Hhef>J^_4TyX*VA+Tx&n+}Xk?is zk32$jN(wzKEnM8cpVKup0d+AUK!_kb=Gc#HC)KfTYHf~?KL|a`Q?F|i>?%Yv;83xV1eH=V=h_!k>7Ne1x z#zqGGbW4HgXBws(gddib&8l&q%TQH0Tak^MH>dauSfB0c`yz~;`eiAnJ z_xAGEkAB33vuAyt6F{v}5w6h?uGOLo3nQzjh-|%{Y`vbOoE*O`n6aZ{sQYSY;P!Cz!6c)b4Q5H#kV&;2^hp zd%4ls$*HO;!nIoB;^N40!joN6GQ$3eN=P7T^JelkZ$_3Rx~^TLy}q9Ih6Z|W+?apB zggPs6(^tRB;?h#m3k%VNhWb6G<;oRKfBZ2`)z!2&H)FEd+<^FWqB$#DB?yFRH0U%M zRAU%2_jGn*?CvHmF>zF2+6@K{crsjg3RyS9LX3qj7En z6D|l8u3X8EH{V1Rz(y9Sd3k6fBZ*E=r~dS5E}S{T;K0C$u0Q~^8V%7J4VoY}$}kTM z(9_w;;!$iY$>E@-sR^su?DKnFNC-NoGf> zu2@0*7=p{y8VxCWJt=xUrO!Ug;h+7CyAv?#nvuY$fk9d3iPv5W2o{$XCMUCT>sB^y z-3man!9c_5(_E~srp3da7?)%jqo28@p3Y9%3QxNq&T+M_j`Jr@1byDVt}Y}=@@Yr4U%N(E04ZDb4?akiUQbqWF-h6k6P}4QRHtLZ zYp+rH=9_a1m@rY~ORu~_+9d7PCW<1dd3mJfC!2g)n1UDP06#*a_;^2=LRqlf zDz%!-!a_0&3)%GCb6Cx0u3xx-;oLb4b#)jT8vG8`ed*Zl-Bf+>0d}j^{aKFF)0?H& zlTlD`Pvd>El)GUAcdlNg`P8Y|4NM5IFeitn{_gLlwH%tzQ1b4-pFC%tm%;viuGQCb zwYC;RT^%<)0@5#@K8>Nio{k$gIQz*b+`4`po5cda;=DYvi;IW4nW7@X#<;=8RA}Wl zzscR}*MnNkFe4myB102`uT@kKGluz@>6G#AT@1Cgd{I}2p|+Mb=g1==HI>}bQg=6_ zo8jrpOx@k=d-c`X2~312^8BxUMUGz2Y}4Iaw~%Fdgi~!c=vHGRpS|@~z%Tc-JLfii z_q($a7J%5q#90XoKw^GA1y0w)tN{i=V9AOVv+6+`ySuq^_UtgHoMmv3%V*BeH;!Mj}<3<)vv#s#nuYSee|M(C0T#t9x z>ZvE5eU|56etBBUd%LZT)0LH+K5~RB_4UKbsuM-RRVp3`aOb?~1ID9uY3=L7>hXrU z0+qZc472m|-SDJjWK3y!5AE8;|NZGt0Z@w~@!{cg4h*N^qd@4Z`{b5O`FKb%bV2l)_wjtKYQ^-B!>f05J2!8hNyQJsjOjwy@S>{T8 zy&ICwwzlD2qbo$hLu!?ZxX@76jk6rHB1F?6NwoL(yQ9_oDQ?lCA$T@!gnp@$Im@SXL~zB0I;~!+}DQy#3v^5^MC($V#l#fj>B%}VoePvoi<62Ga%e&_w9rx zLaTL046X^#xSiF&jA-(Ed$Eln8-gx2mX!}Z#Oh6(C|b3OkN{RMTFhqt_qV@IlXI<5 zx(6&@df^3@ty{;NU;EmyWB%9s?{jM3K0f`={~QL5%l`U5zyCej045!6Rx1ssPI2<^ zVa|T`8U4=aJ%6@dTv!;PUNhel1(;Sm z(cNfPh<hPXmW3%Vfgn3^njUj=LFGtJM^(UOfa)Ss5n| z9_07`@DB)rfXZV;l$M*z_kZ&nLL(yFrx^_xfZI23^6M8~!0aqndw;)sxc&UgFSB;b z7OJbNIC=Om=g*u$vfJI=i@TfgYKgodDOk0NqV?-3c<@0YeArhN-}=^s*Tt*_W|#-o z;lNN|&-tn<&L2Ncw^y7;uh~qG+5FdBd9IO{SA_^3BMeU?H+In?Ui|HEiS=54?GG4$ ziziR=&X0fm*O8OaI4tPOb#)gY-mnNjvR53<(&fu3TDOj6>(&t!7w7l6Oa?G>BHN9q zQjuFyLT*V3Uw-jLZZtO1P*p|4v14?$v=AK{O0?5v>9<Eix;!$*=Ol)Z+F9EsINyQio;-8 z^TZQuc;=Z2xvnNOl&`=3I=}he_b@tF61ZSV$;zUrtc;?vG7_hFeoO%-0L3LGQMO|T zWjl7zd*==pjvuGKs*1~J&vM_&m2CUr4=4P3U;oKZ_~(~iA~8LkqV?-3Dk~$!Yssqu z%q&p!f(1PA)Kfh0)Ki!T2GE3t`d#E7ta3_A`T73+!-nRwO$snxn(WG)&9J~si0M_N z0Ha6&Mv)=~7)1&&iWFcJDZnUFfKj9Xqe#*01m@tM|BP)8Z5BS+a@g(s_lF-&=s9I) z{LrkJ8u*tt-r$eF{Vf}we3H%O<)mh1DOC7S_wCyp-o2ZnAAA74y_6LcOb95Rnl*Dt zecj#cdG9^;y!RgWm6oz)+cws2-i&qzXGxeUoj-Gi0~HmVIeHXPl89BQ#R$JtU|PIi9&ynX5q@m=ANG6JCC>`|D6v`qt&u@%NDk5+eUtA z>731T2h*AZHBe<%0oc)^$l~&g|g5 zW;5L;(_e4WxkSWjv2bMXUXJYDOG-uto6F02_{k?{RAQpBs-v})Lx27=hxhDphjx12 zE3%9V$k6GqGPF_qqKF8xgMekMnw4v-z17m&JCt9>Gi!mz-)gm*l@CA6=JIm%Yt~Fy zW~j+%VI)c7-0|ZasHmX!`0-KSdS7Wr5+WnX3Sv3TSX-A_YxH_6)fxHUZ?)3d-~ZPg zL7cMN?VLDth!cknu`n@_P0o!g7Ea=u7^r%?yQ%!}Lk|AwPjq_iQQ*0DNvGAi*H{gM zB`S)nMT;gCmO0bH_)=S6A7(%6tVKbfWbIlumzT4mtPFJ!ql$@WVJ_FzaiF4t6Ne6A z_i#?S)-1XH_LpiTF)E780AUGs1NJ!^)0^yUt{sv@b8qj6tX7gNQ+@0hHOGz-9T&%j zr=DWd)~!>L3e7w?$Y=ZabD*Mv8%<3<69CB!rAAA>$A~Xcrz11K)sXTyw!oS5LEtJ; zBqKI<$cQu=8Sr|GUERU%ci(0AAO1joX(^k_%Xwh)=74g!jLFve;tLK`RB&|PK1}`n ze_hM%r9}}DBXYd>Qj$(b#uP7|onH&tJl@uuJv}%iY54kPS*EeNn#P(Me*cSKu>SGK z*<4;uR)Co%J>{_5Id$X+2P!IPIDL9V0sz-~YSkEW=M8H?qoY|IL`Exlu5GY2RZ4db z&$~<}`h0A&H()gK@y?xm{9pe?c78saw{2tnqmPcrB{Q13Iy(4d*Den2+QnU0dNF5y z8uwy+Ste4eB|gCEjg;tU(#MhhOaW#biVqJbK0F+N=HA|+JbIpQp&(#rY{YQ!BLDWw zU-ICVEo?3?_X$q3!NA=+cldbcPEH>^isY4(&-HeTUJL#Mf+Z$~^dNmo<858$^eAau zMybulEu(R0lrl9WDd@CuT;Pl~v8x~mBt%Az$%ysqYl?}Pc32c(#%M3VP;$d|XToMYd?fxxXK6up zJKictPDU4G7{^4yq5uRRPGQr#S`FDwURYeqKc-5e0E0?Z%|2MB8WcA$R-4VFRM+f-Wx6Ex^!GO_RW>_d znXXpFrT@Ng;iPD`TCd*qT*9GfifjQPU|6PGMyvI*h#(y6?&+b&r1VTvC6yqsYK+T$ zr@K{gXBN!T3@?2?2=t+;9?N?MhQ<(Zi?9uk163m0f;Zl<}rdnig! z5We;QPM)k50Cp4psM5&5-L~&%Zq*(imG9fiTMT z6w?HVM3yyvZ_r0&`@FEP<9!v@3q&E12jt^H4Ex*@*3ll&Xyx&> z5A%e6}5DrJh#_s-?qem56MNx42 Y|6GCN!Hxm}hX4Qo07*qoM6N<$f{|u$(*OVf literal 0 HcmV?d00001 diff --git a/rma/static/description/index.html b/rma/static/description/index.html new file mode 100644 index 000000000..a45186e63 --- /dev/null +++ b/rma/static/description/index.html @@ -0,0 +1,496 @@ + + + + + + +Return Merchandise Authorization Management + + + +
+

Return Merchandise Authorization Management

+ + +

Beta License: AGPL-3 OCA/rma Translate me on Weblate Try me on Runbot

+

This module allows you to manage Return Merchandise Authorization (RMA). +RMA documents can be created from scratch, from a delivery order or from +an incoming email. Product receptions and returning delivery operations +of the RMA module are fully integrated with the Receipts and Deliveries +Operations of Odoo inventory core module. It also allows you to generate +refunds in the same way as Odoo generates it. +Besides, you have full integration of the RMA documents in the customer portal.

+

Table of contents

+ +
+

Configuration

+

If you want RMAs to be created from incoming emails, you need to:

+
    +
  1. Go to Settings > General Settings.
  2. +
  3. Check ‘External Email Servers’ checkbox under Discuss section.
  4. +
  5. Set an ‘alias domain’ and an incoming server.
  6. +
  7. Go to RMA > Configuration > RMA Team and select a team or create a new +one.
  8. +
  9. Go to ‘Email’ tab and set an ‘Email Alias’.
  10. +
+
+
+

Usage

+

To use this module, you need to:

+
    +
  1. Go to RMA > Orders and create a new RMA.
  2. +
  3. Select a partner, an invoice address, select a product +(or select a picking and a move instead), write a quantity, fill the rest +of the form and click on ‘confirm’ button in the status bar.
  4. +
  5. You will see an smart button labeled ‘Receipt’. Click on that button to see +the reception operation form.
  6. +
  7. If everything is right, validate the operation and go back to the RMA to +see it in a ‘received’ state.
  8. +
  9. Now you are able to generate a refund, generate a delivery order to return +to the customer the same product or another product as a replacement, split +the RMA by extracting a part of the remaining quantity to another RMA, +preview the RMA in the website. All of these operations can be done by +clicking on the buttons in the status bar.
      +
    • If you click on ‘Refund’ button, a refund will be created, and it will be +accessible via the smart button labeled Refund. The RMA will be set +automatically to ‘Refunded’ state when the refund is validated.
    • +
    • If you click on ‘Replace’ or ‘Return to customer’ button instead, +a popup wizard will guide you to create a Delivery order to the client +and this order will be accessible via the smart button labeled Delivery. +The RMA will be set automatically to ‘Replaced’ or ‘Returned’ state when +the RMA quantity is equal or lower than the quantity in done delivery +orders linked to it.
    • +
    +
  10. +
+

An RMA can also be created from a return of a delivery order:

+
    +
  1. Select a delivery order and click on ‘Return’ button to create a return.
  2. +
  3. Check “Create RMAs” checkbox in the returning wizard, select the RMA +stock location and click on ‘Return’ button.
  4. +
  5. An RMA will be created for each product returned in the previous step. +Every RMA will be in confirmed state and they will +be linked to the returning operation generated previously.
  6. +
+

Note: An RMA can also be created from an incoming email (See configuration +section).

+
+
+

Known issues / Roadmap

+
    +
  • As soon as the picking is selected, the user should select the move, +but perhaps stock.move _rec_name could be improved to better show what +the product of that move is.
  • +
+
+
+

Bug Tracker

+

Bugs are tracked on GitHub Issues. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us smashing it by providing a detailed and welcomed +feedback.

+

Do not contact contributors directly about support or help with technical issues.

+
+
+

Credits

+
+

Authors

+
    +
  • Tecnativa
  • +
+
+
+

Contributors

+
    +
  • Tecnativa:
      +
    • Ernesto Tejeda
    • +
    • Pedro M. Baeza
    • +
    • David Vidal
    • +
    +
  • +
+
+
+

Maintainers

+

This module is maintained by the OCA.

+Odoo Community Association +

OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use.

+

Current maintainer:

+

ernestotejeda

+

This module is part of the OCA/rma project on GitHub.

+

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

+
+
+
+ + diff --git a/rma/tests/__init__.py b/rma/tests/__init__.py new file mode 100644 index 000000000..5f9ab818b --- /dev/null +++ b/rma/tests/__init__.py @@ -0,0 +1,3 @@ +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from . import test_rma diff --git a/rma/tests/test_rma.py b/rma/tests/test_rma.py new file mode 100644 index 000000000..197276ad4 --- /dev/null +++ b/rma/tests/test_rma.py @@ -0,0 +1,638 @@ +# Copyright 2020 Tecnativa - Ernesto Tejeda +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo.tests import Form, SavepointCase +from odoo.exceptions import UserError, ValidationError + + +class TestRma(SavepointCase): + + @classmethod + def setUpClass(cls): + super(TestRma, cls).setUpClass() + cls.res_partner = cls.env['res.partner'] + cls.product_product = cls.env['product.product'] + cls.company = cls.env.user.company_id + cls.warehouse_company = cls.env['stock.warehouse'].search( + [('company_id', '=', cls.company.id)], limit=1) + cls.rma_loc = cls.warehouse_company.rma_loc_id + account_pay = cls.env['account.account'].create({ + 'code': 'X1111', + 'name': 'Creditors - (test)', + 'user_type_id': cls.env.ref( + 'account.data_account_type_payable').id, + 'reconcile': True, + }) + cls.journal = cls.env['account.journal'].create({ + 'name': 'sale_0', + 'code': 'SALE0', + 'type': 'sale', + 'default_debit_account_id': account_pay.id, + }) + cls.product = cls.product_product.create({ + 'name': 'Product test 1', + 'type': 'product', + }) + account_type = cls.env['account.account.type'].create({ + 'name': 'RCV type', + 'type': 'receivable', + }) + cls.account_receiv = cls.env['account.account'].create({ + 'name': 'Receivable', + 'code': 'RCV00', + 'user_type_id': account_type.id, + 'reconcile': True, + }) + cls.partner = cls.res_partner.create({ + 'name': 'Partner test', + 'property_account_receivable_id': cls.account_receiv.id, + }) + cls.partner_invoice = cls.res_partner.create({ + 'name': 'Partner invoice test', + 'parent_id': cls.partner.id, + 'type': 'invoice', + }) + + def _create_rma(self, partner=None, product=None, qty=None, location=None): + rma_form = Form(self.env['rma']) + if partner: + rma_form.partner_id = partner + if product: + rma_form.product_id = product + if qty: + rma_form.product_uom_qty = qty + if location: + rma_form.location_id = location + return rma_form.save() + + def _create_confirm_receive(self, partner=None, product=None, qty=None, + location=None): + rma = self._create_rma(partner, product, qty, location) + rma.action_confirm() + rma.reception_move_id.quantity_done = rma.product_uom_qty + rma.reception_move_id.picking_id.action_done() + return rma + + def _test_readonly_fields(self, rma): + with Form(rma) as rma_form: + with self.assertRaises(AssertionError): + rma_form.partner_id = self.env['res.partner'] + with self.assertRaises(AssertionError): + rma_form.partner_invoice_id = self.env['res.partner'] + with self.assertRaises(AssertionError): + rma_form.picking_id = self.env['stock.picking'] + with self.assertRaises(AssertionError): + rma_form.move_id = self.env['stock.move'] + with self.assertRaises(AssertionError): + rma_form.product_id = self.env['product.product'] + with self.assertRaises(AssertionError): + rma_form.product_uom_qty = 0 + with self.assertRaises(AssertionError): + rma_form.product_uom = self.env['uom.uom'] + with self.assertRaises(AssertionError): + rma_form.location_id = self.env['stock.location'] + + def _create_delivery(self): + picking_type = self.env['stock.picking.type'].search( + [ + ('code', '=', 'outgoing'), + '|', + ('warehouse_id.company_id', '=', self.company.id), + ('warehouse_id', '=', False) + ], + limit=1, + ) + picking_form = Form( + recordp=self.env['stock.picking'].with_context( + default_picking_type_id=picking_type.id), + view="stock.view_picking_form", + ) + picking_form.company_id = self.company + picking_form.partner_id = self.partner + with picking_form.move_ids_without_package.new() as move: + move.product_id = self.product + move.product_uom_qty = 10 + with picking_form.move_ids_without_package.new() as move: + move.product_id = self.product_product.create( + {'name': 'Product 2 test', 'type': 'product'}) + move.product_uom_qty = 20 + picking = picking_form.save() + picking.action_confirm() + for move in picking.move_lines: + move.quantity_done = move.product_uom_qty + picking.button_validate() + return picking + + def test_onchange(self): + rma_form = Form(self.env['rma']) + # If partner changes, the invoice address is set + rma_form.partner_id = self.partner + self.assertEqual(rma_form.partner_invoice_id, self.partner_invoice) + # If origin move changes, the product is set + uom_ten = self.env['uom.uom'].create({ + 'name': "Ten", + 'category_id': self.env.ref('uom.product_uom_unit').id, + 'factor_inv': 10, + 'uom_type': 'bigger', + }) + product_2 = self.product_product.create({ + 'name': 'Product test 2', + 'type': 'product', + 'uom_id': uom_ten.id, + }) + outgoing_picking_type = self.env['stock.picking.type'].search( + [ + ('code', '=', 'outgoing'), + '|', + ('warehouse_id.company_id', '=', self.company.id), + ('warehouse_id', '=', False) + ], + limit=1, + ) + picking_form = Form( + recordp=self.env['stock.picking'].with_context( + default_picking_type_id=outgoing_picking_type.id), + view="stock.view_picking_form", + ) + picking_form.company_id = self.company + picking_form.partner_id = self.partner + picking_form.partner_id = self.partner + with picking_form.move_ids_without_package.new() as move: + move.product_id = product_2 + move.product_uom_qty = 15 + move.product_uom = uom_ten + picking = picking_form.save() + picking.action_done() + rma_form.picking_id = picking + rma_form.move_id = picking.move_lines + self.assertEqual(rma_form.product_id, product_2) + self.assertEqual(rma_form.product_uom_qty, 15) + self.assertEqual(rma_form.product_uom, uom_ten) + # If product changes, unit of measure changes + rma_form.picking_id = self.env['stock.picking'] + rma_form.product_id = self.product + self.assertEqual(rma_form.product_id, self.product) + self.assertEqual(rma_form.product_uom_qty, 15) + self.assertNotEqual(rma_form.product_uom, uom_ten) + self.assertEqual(rma_form.product_uom, self.product.uom_id) + rma = rma_form.save() + # If product changes, unit of measure domain should also change + domain = rma._onchange_product_id()['domain']['product_uom'] + self.assertListEqual( + domain, [('category_id', '=', self.product.uom_id.category_id.id)]) + + def test_ensure_required_fields_on_confirm(self): + rma = self._create_rma() + with self.assertRaises(ValidationError) as e: + rma.action_confirm() + self.assertEqual( + e.exception.name, + "Required field(s):\nCustomer\nInvoice Address\nProduct\nLocation" + ) + with Form(rma) as rma_form: + rma_form.partner_id = self.partner + with self.assertRaises(ValidationError) as e: + rma.action_confirm() + self.assertEqual( + e.exception.name, "Required field(s):\nProduct\nLocation") + with Form(rma) as rma_form: + rma_form.product_id = self.product + rma_form.location_id = self.rma_loc + rma.action_confirm() + self.assertEqual(rma.state, 'confirmed') + + def test_confirm_and_receive(self): + rma = self._create_rma(self.partner, self.product, 10, self.rma_loc) + rma.action_confirm() + self.assertEqual(rma.reception_move_id.picking_id.state, 'assigned') + self.assertEqual(rma.reception_move_id.product_id, rma.product_id) + self.assertEqual(rma.reception_move_id.product_uom_qty, 10) + self.assertEqual(rma.reception_move_id.product_uom, rma.product_uom) + self.assertEqual(rma.state, 'confirmed') + self._test_readonly_fields(rma) + rma.reception_move_id.quantity_done = 9 + with self.assertRaises(ValidationError): + rma.reception_move_id.picking_id.action_done() + rma.reception_move_id.quantity_done = 10 + rma.reception_move_id.picking_id.action_done() + self.assertEqual(rma.reception_move_id.picking_id.state, 'done') + self.assertEqual(rma.reception_move_id.quantity_done, 10) + self.assertEqual(rma.state, 'received') + self._test_readonly_fields(rma) + + def test_cancel(self): + # cancel a draft RMA + rma = self._create_rma(self.partner, self.product) + rma.action_cancel() + self.assertEqual(rma.state, 'cancelled') + self._test_readonly_fields(rma) + # cancel a confirmed RMA + rma = self._create_rma(self.partner, self.product, 10, self.rma_loc) + rma.action_confirm() + rma.action_cancel() + self.assertEqual(rma.state, 'cancelled') + # A RMA is only cancelled from draft and confirmed states + rma = self._create_confirm_receive(self.partner, self.product, 10, + self.rma_loc) + with self.assertRaises(UserError): + rma.action_cancel() + + def test_lock_unlock(self): + # A RMA is only locked from 'received' state + rma_1 = self._create_rma(self.partner, self.product, 10, self.rma_loc) + rma_2 = self._create_confirm_receive(self.partner, self.product, 10, + self.rma_loc) + self.assertEqual(rma_1.state, 'draft') + self.assertEqual(rma_2.state, 'received') + (rma_1 | rma_2).action_lock() + self.assertEqual(rma_1.state, 'draft') + self.assertEqual(rma_2.state, 'locked') + # A RMA is only unlocked from 'lock' state and it will be set + # to 'received' state + (rma_1 | rma_2).action_unlock() + self.assertEqual(rma_1.state, 'draft') + self.assertEqual(rma_2.state, 'received') + + def test_action_refund(self): + rma = self._create_confirm_receive(self.partner, self.product, 10, + self.rma_loc) + self.assertEqual(rma.state, 'received') + self.assertTrue(rma.can_be_refunded) + self.assertTrue(rma.can_be_returned) + self.assertTrue(rma.can_be_replaced) + rma.action_refund() + self.assertEqual(rma.refund_id.type, 'out_refund') + self.assertEqual(rma.refund_id.state, 'draft') + self.assertEqual(rma.refund_line_id.product_id, rma.product_id) + self.assertEqual(rma.refund_line_id.quantity, 10) + self.assertEqual(rma.refund_line_id.uom_id, rma.product_uom) + self.assertEqual(rma.state, 'waiting_refund') + self.assertFalse(rma.can_be_refunded) + self.assertFalse(rma.can_be_returned) + self.assertFalse(rma.can_be_replaced) + rma.refund_line_id.quantity = 9 + with self.assertRaises(ValidationError): + rma.refund_id.action_invoice_open() + rma.refund_line_id.quantity = 10 + rma.refund_id.action_invoice_open() + self.assertEqual(rma.state, 'refunded') + self.assertFalse(rma.can_be_refunded) + self.assertFalse(rma.can_be_returned) + self.assertFalse(rma.can_be_replaced) + self._test_readonly_fields(rma) + + def test_mass_refund(self): + # Create, confirm and receive rma_1 + rma_1 = self._create_confirm_receive(self.partner, self.product, 10, + self.rma_loc) + # create, confirm and receive 3 more RMAs + # rma_2: Same partner and same product as rma_1 + rma_2 = self._create_confirm_receive(self.partner, self.product, 15, + self.rma_loc) + # rma_3: Same partner and different product than rma_1 + product = self.product_product.create( + {'name': 'Product 2 test', 'type': 'product'}) + rma_3 = self._create_confirm_receive(self.partner, product, 20, + self.rma_loc) + # rma_4: Different partner and same product as rma_1 + partner = self.res_partner.create({ + 'name': 'Partner 2 test', + 'property_account_receivable_id': self.account_receiv.id, + 'company_id': self.company.id, + }) + rma_4 = self._create_confirm_receive(partner, product, 25, + self.rma_loc) + # all rmas are ready to refund + all_rmas = (rma_1 | rma_2 | rma_3 | rma_4) + self.assertEqual(all_rmas.mapped('state'), ['received']*4) + self.assertEqual(all_rmas.mapped('can_be_refunded'), [True]*4) + # Mass refund of those four RMAs + action = self.env.ref('rma.rma_refund_action_server') + ctx = dict(self.env.context) + ctx.update(active_ids=all_rmas.ids, active_model='rma') + action.with_context(ctx).run() + # Two refunds were created + refund_1 = (rma_1 | rma_2 | rma_3).mapped('refund_id') + refund_2 = rma_4.refund_id + self.assertEqual(len(refund_1), 1) + self.assertEqual(len(refund_2), 1) + self.assertEqual((refund_1 | refund_2).mapped('state'), ['draft']*2) + # One refund per partner + self.assertNotEqual(refund_1.partner_id, refund_2.partner_id) + self.assertEqual( + refund_1.partner_id, + (rma_1 | rma_2 | rma_3).mapped('partner_invoice_id'), + ) + self.assertEqual(refund_2.partner_id, rma_4.partner_invoice_id) + # Each RMA (rma_1, rma_2 and rma_3) is linked with a different + # line of refund_1 + self.assertEqual(len(refund_1.invoice_line_ids), 3) + self.assertEqual( + refund_1.invoice_line_ids.mapped('rma_id'), + (rma_1 | rma_2 | rma_3), + ) + self.assertEqual( + (rma_1 | rma_2 | rma_3).mapped('refund_line_id'), + refund_1.invoice_line_ids, + ) + # rma_4 is linked with the unique line of refund_2 + self.assertEqual(len(refund_2.invoice_line_ids), 1) + self.assertEqual(refund_2.invoice_line_ids.rma_id, rma_4) + self.assertEqual(rma_4.refund_line_id, refund_2.invoice_line_ids) + # Assert product and quantities are propagated correctly + for rma in all_rmas: + self.assertEqual(rma.product_id, rma.refund_line_id.product_id) + self.assertEqual(rma.product_uom_qty, rma.refund_line_id.quantity) + self.assertEqual(rma.product_uom, rma.refund_line_id.uom_id) + # Less quantity -> error on confirm + rma_2.refund_line_id.quantity = 14 + with self.assertRaises(ValidationError): + refund_1.action_invoice_open() + rma_2.refund_line_id.quantity = 15 + refund_1.action_invoice_open() + refund_2.action_invoice_open() + self.assertEqual(all_rmas.mapped('state'), ['refunded']*4) + + def test_replace(self): + # Create, confirm and receive an RMA + rma = self._create_confirm_receive(self.partner, self.product, 10, + self.rma_loc) + # Replace with another product with quantity 2. + product_2 = self.product_product.create( + {'name': 'Product 2 test', 'type': 'product'}) + delivery_form = Form( + self.env['rma.delivery.wizard'].with_context( + active_ids=rma.ids, + rma_delivery_type='replace', + ) + ) + delivery_form.product_id = product_2 + delivery_form.product_uom_qty = 2 + delivery_wizard = delivery_form.save() + delivery_wizard.action_deliver() + self.assertEqual(len(rma.delivery_move_ids.picking_id.move_lines), 1) + self.assertEqual(rma.delivery_move_ids.product_id, product_2) + self.assertEqual(rma.delivery_move_ids.product_uom_qty, 2) + self.assertTrue(rma.delivery_move_ids.picking_id.state, 'waiting') + self.assertEqual(rma.state, 'waiting_replacement') + self.assertFalse(rma.can_be_refunded) + self.assertFalse(rma.can_be_returned) + self.assertTrue(rma.can_be_replaced) + self.assertEqual(rma.delivered_qty, 2) + self.assertEqual(rma.remaining_qty, 8) + self.assertEqual(rma.delivered_qty_done, 0) + self.assertEqual(rma.remaining_qty_to_done, 10) + first_move = rma.delivery_move_ids + picking = first_move.picking_id + # Replace again with another product with the remaining quantity + product_3 = self.product_product.create( + {'name': 'Product 3 test', 'type': 'product'}) + delivery_form = Form( + self.env['rma.delivery.wizard'].with_context( + active_ids=rma.ids, + rma_delivery_type='replace', + ) + ) + delivery_form.product_id = product_3 + delivery_wizard = delivery_form.save() + delivery_wizard.action_deliver() + second_move = rma.delivery_move_ids - first_move + self.assertEqual(len(rma.delivery_move_ids), 2) + self.assertEqual(rma.delivery_move_ids.mapped('picking_id'), picking) + self.assertEqual(first_move.product_id, product_2) + self.assertEqual(first_move.product_uom_qty, 2) + self.assertEqual(second_move.product_id, product_3) + self.assertEqual(second_move.product_uom_qty, 8) + self.assertTrue(picking.state, 'waiting') + self.assertEqual(rma.delivered_qty, 10) + self.assertEqual(rma.remaining_qty, 0) + self.assertEqual(rma.delivered_qty_done, 0) + self.assertEqual(rma.remaining_qty_to_done, 10) + # remaining_qty is 0 but rma is not set to 'replaced' until + # remaining_qty_to_done is less than or equal to 0 + first_move.quantity_done = 2 + second_move.quantity_done = 8 + picking.button_validate() + self.assertEqual(picking.state, 'done') + self.assertEqual(rma.delivered_qty, 10) + self.assertEqual(rma.remaining_qty, 0) + self.assertEqual(rma.delivered_qty_done, 10) + self.assertEqual(rma.remaining_qty_to_done, 0) + # The RMA is now in 'replaced' state + self.assertEqual(rma.state, 'replaced') + self.assertFalse(rma.can_be_refunded) + self.assertFalse(rma.can_be_returned) + # Despite being in 'replaced' state, + # RMAs can still perform replacements. + self.assertTrue(rma.can_be_replaced) + self._test_readonly_fields(rma) + + def test_return_to_customer(self): + # Create, confirm and receive an RMA + rma = self._create_confirm_receive(self.partner, self.product, 10, + self.rma_loc) + # Return the same product with quantity 2 to the customer. + delivery_form = Form( + self.env['rma.delivery.wizard'].with_context( + active_ids=rma.ids, + rma_delivery_type='return', + ) + ) + delivery_form.product_uom_qty = 2 + delivery_wizard = delivery_form.save() + delivery_wizard.action_deliver() + picking = rma.delivery_move_ids.picking_id + self.assertEqual(len(picking.move_lines), 1) + self.assertEqual(rma.delivery_move_ids.product_id, self.product) + self.assertEqual(rma.delivery_move_ids.product_uom_qty, 2) + self.assertTrue(picking.state, 'waiting') + self.assertEqual(rma.state, 'waiting_return') + self.assertFalse(rma.can_be_refunded) + self.assertFalse(rma.can_be_replaced) + self.assertTrue(rma.can_be_returned) + self.assertEqual(rma.delivered_qty, 2) + self.assertEqual(rma.remaining_qty, 8) + self.assertEqual(rma.delivered_qty_done, 0) + self.assertEqual(rma.remaining_qty_to_done, 10) + first_move = rma.delivery_move_ids + picking = first_move.picking_id + # Validate the picking + first_move.quantity_done = 2 + picking.button_validate() + self.assertEqual(picking.state, 'done') + self.assertEqual(rma.delivered_qty, 2) + self.assertEqual(rma.remaining_qty, 8) + self.assertEqual(rma.delivered_qty_done, 2) + self.assertEqual(rma.remaining_qty_to_done, 8) + # Return the remaining quantity to the customer + delivery_form = Form( + self.env['rma.delivery.wizard'].with_context( + active_ids=rma.ids, + rma_delivery_type='return', + ) + ) + delivery_wizard = delivery_form.save() + delivery_wizard.action_deliver() + second_move = rma.delivery_move_ids - first_move + second_move.quantity_done = 8 + self.assertEqual(rma.delivered_qty, 10) + self.assertEqual(rma.remaining_qty, 0) + self.assertEqual(rma.delivered_qty_done, 2) + self.assertEqual(rma.remaining_qty_to_done, 8) + self.assertEqual(rma.state, 'waiting_return') + # remaining_qty is 0 but rma is not set to 'returned' until + # remaining_qty_to_done is less than or equal to 0 + picking_2 = second_move.picking_id + picking_2.button_validate() + self.assertEqual(picking_2.state, 'done') + self.assertEqual(rma.delivered_qty, 10) + self.assertEqual(rma.remaining_qty, 0) + self.assertEqual(rma.delivered_qty_done, 10) + self.assertEqual(rma.remaining_qty_to_done, 0) + # The RMA is now in 'returned' state + self.assertEqual(rma.state, 'returned') + self.assertFalse(rma.can_be_refunded) + self.assertFalse(rma.can_be_returned) + self.assertFalse(rma.can_be_replaced) + self._test_readonly_fields(rma) + + def test_mass_return_to_customer(self): + # Create, confirm and receive rma_1 + rma_1 = self._create_confirm_receive(self.partner, self.product, 10, + self.rma_loc) + # create, confirm and receive 3 more RMAs + # rma_2: Same partner and same product as rma_1 + rma_2 = self._create_confirm_receive(self.partner, self.product, 15, + self.rma_loc) + # rma_3: Same partner and different product than rma_1 + product = self.product_product.create( + {'name': 'Product 2 test', 'type': 'product'}) + rma_3 = self._create_confirm_receive(self.partner, product, 20, + self.rma_loc) + # rma_4: Different partner and same product as rma_1 + partner = self.res_partner.create({'name': 'Partner 2 test'}) + rma_4 = self._create_confirm_receive(partner, product, 25, + self.rma_loc) + # all rmas are ready to be returned to the customer + all_rmas = (rma_1 | rma_2 | rma_3 | rma_4) + self.assertEqual(all_rmas.mapped('state'), ['received'] * 4) + self.assertEqual(all_rmas.mapped('can_be_returned'), [True] * 4) + # Mass return of those four RMAs + delivery_wizard = self.env['rma.delivery.wizard'].with_context( + active_ids=all_rmas.ids, rma_delivery_type='return').create({}) + delivery_wizard.action_deliver() + # Two pickings were created + pick_1 = (rma_1 | rma_2 | rma_3).mapped('delivery_move_ids.picking_id') + pick_2 = rma_4.delivery_move_ids.picking_id + self.assertEqual(len(pick_1), 1) + self.assertEqual(len(pick_2), 1) + self.assertNotEqual(pick_1, pick_2) + self.assertEqual((pick_1 | pick_2).mapped('state'), ['assigned'] * 2) + # One picking per partner + self.assertNotEqual(pick_1.partner_id, pick_2.partner_id) + self.assertEqual( + pick_1.partner_id, + (rma_1 | rma_2 | rma_3).mapped('partner_id'), + ) + self.assertEqual(pick_2.partner_id, rma_4.partner_id) + # Each RMA of (rma_1, rma_2 and rma_3) is linked to a different + # line of picking_1 + self.assertEqual(len(pick_1.move_lines), 3) + self.assertEqual( + pick_1.move_lines.mapped('rma_id'), + (rma_1 | rma_2 | rma_3), + ) + self.assertEqual( + (rma_1 | rma_2 | rma_3).mapped('delivery_move_ids'), + pick_1.move_lines, + ) + # rma_4 is linked with the unique move of pick_2 + self.assertEqual(len(pick_2.move_lines), 1) + self.assertEqual(pick_2.move_lines.rma_id, rma_4) + self.assertEqual(rma_4.delivery_move_ids, pick_2.move_lines) + # Assert product and quantities are propagated correctly + for rma in all_rmas: + self.assertEqual(rma.product_id, rma.delivery_move_ids.product_id) + self.assertEqual(rma.product_uom_qty, + rma.delivery_move_ids.product_uom_qty) + self.assertEqual(rma.product_uom, + rma.delivery_move_ids.product_uom) + rma.delivery_move_ids.quantity_done = rma.product_uom_qty + pick_1.button_validate() + pick_2.button_validate() + self.assertEqual(all_rmas.mapped('state'), ['returned'] * 4) + + def test_rma_from_picking_return(self): + # Create a return from a delivery picking + origin_delivery = self._create_delivery() + return_wizard = self.env['stock.return.picking'].with_context( + active_id=origin_delivery.id, + active_ids=origin_delivery.ids, + ).create({'create_rma': True}) + picking_action = return_wizard.create_returns() + # Each origin move is linked to a different RMA + origin_moves = origin_delivery.move_lines + self.assertTrue(origin_moves[0].rma_ids) + self.assertTrue(origin_moves[1].rma_ids) + rmas = origin_moves.mapped('rma_ids') + self.assertEqual(rmas.mapped('state'), ['confirmed']*2) + # Each reception move is linked one of the generated RMAs + reception = self.env['stock.picking'].browse(picking_action['res_id']) + reception_moves = reception.move_lines + self.assertTrue(reception_moves[0].rma_receiver_ids) + self.assertTrue(reception_moves[1].rma_receiver_ids) + self.assertEqual(reception_moves.mapped('rma_receiver_ids'), rmas) + # Validate the reception picking to set rmas to 'received' state + reception_moves[0].quantity_done = reception_moves[0].product_uom_qty + reception_moves[1].quantity_done = reception_moves[1].product_uom_qty + reception.button_validate() + self.assertEqual(rmas.mapped('state'), ['received'] * 2) + + def test_split(self): + origin_delivery = self._create_delivery() + rma_form = Form(self.env['rma']) + rma_form.partner_id = self.partner + rma_form.picking_id = origin_delivery + rma_form.move_id = origin_delivery.move_lines.filtered( + lambda r: r.product_id == self.product) + rma = rma_form.save() + rma.action_confirm() + rma.reception_move_id.quantity_done = 10 + rma.reception_move_id.picking_id.action_done() + # Return quantity 4 of the same product to the customer + delivery_form = Form( + self.env['rma.delivery.wizard'].with_context( + active_ids=rma.ids, + rma_delivery_type='return', + ) + ) + delivery_form.product_uom_qty = 4 + delivery_wizard = delivery_form.save() + delivery_wizard.action_deliver() + rma.delivery_move_ids.quantity_done = 4 + rma.delivery_move_ids.picking_id.button_validate() + self.assertEqual(rma.state, 'waiting_return') + # Extract the remaining quantity to another RMA + self.assertTrue(rma.can_be_split) + split_wizard = self.env['rma.split.wizard'].with_context( + active_id=rma.id, + active_ids=rma.ids, + ).create({}) + action = split_wizard.action_split() + # Check rma is set to 'returned' after split. Check new_rma values + self.assertEqual(rma.state, 'returned') + new_rma = self.env['rma'].browse(action['res_id']) + self.assertEqual(new_rma.origin_split_rma_id, rma) + self.assertEqual(new_rma.delivered_qty, 0) + self.assertEqual(new_rma.remaining_qty, 6) + self.assertEqual(new_rma.delivered_qty_done, 0) + self.assertEqual(new_rma.remaining_qty_to_done, 6) + self.assertEqual(new_rma.state, 'received') + self.assertTrue(new_rma.can_be_refunded) + self.assertTrue(new_rma.can_be_returned) + self.assertTrue(new_rma.can_be_replaced) + self.assertEqual(new_rma.move_id, rma.move_id) + self.assertEqual(new_rma.reception_move_id, rma.reception_move_id) + self.assertEqual(new_rma.product_uom_qty + rma.product_uom_qty, 10) + self.assertEqual(new_rma.move_id.quantity_done, 10) + self.assertEqual(new_rma.reception_move_id.quantity_done, 10) diff --git a/rma/views/menus.xml b/rma/views/menus.xml new file mode 100644 index 000000000..568c906d4 --- /dev/null +++ b/rma/views/menus.xml @@ -0,0 +1,24 @@ + + + + + + + + diff --git a/rma/views/report_rma.xml b/rma/views/report_rma.xml new file mode 100644 index 000000000..c6320d0d7 --- /dev/null +++ b/rma/views/report_rma.xml @@ -0,0 +1,96 @@ + + + + + diff --git a/rma/views/res_partner_views.xml b/rma/views/res_partner_views.xml new file mode 100644 index 000000000..d783f1f69 --- /dev/null +++ b/rma/views/res_partner_views.xml @@ -0,0 +1,23 @@ + + + + + res.partner.form + res.partner + + +
+ +
+
+
+
diff --git a/rma/views/rma_portal_templates.xml b/rma/views/rma_portal_templates.xml new file mode 100644 index 000000000..16cff15b0 --- /dev/null +++ b/rma/views/rma_portal_templates.xml @@ -0,0 +1,269 @@ + + + + + + + + + + + diff --git a/rma/views/rma_team_views.xml b/rma/views/rma_team_views.xml new file mode 100644 index 000000000..328e1ee6c --- /dev/null +++ b/rma/views/rma_team_views.xml @@ -0,0 +1,90 @@ + + + + + rma.team.view.form + rma.team + +
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+
+ Avatar +
+ +
+
+
+
+
+
+
+
+ + + + +
+
+
+ + +
+
+
+
+ + + RMA team + rma.team + form + tree,form + +

+ Click to add a new RMA. +

+
+
+ + +
diff --git a/rma/views/rma_views.xml b/rma/views/rma_views.xml new file mode 100644 index 000000000..c86d14c28 --- /dev/null +++ b/rma/views/rma_views.xml @@ -0,0 +1,285 @@ + + + + + rma.view.search + rma + + + + + + + + + + + + + + + + + + + + + + rma.view.tree + rma + + + + + + + + + + + + + + + + + + rma.view.form + rma + +
+
+
+ +
+ + + +
+
+

+ +

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+
+
+
+ + + rma.pivot + rma + + + + + + + + + + + rma.calendar + rma + + + + + + + + + + + + To Refund + + + code + records.action_refund() + + + + RMA + rma + form + tree,form,pivot,calendar + {"search_default_user_id": uid} + +

+ Click to add a new RMA. +

+
+
+ + + + +
diff --git a/rma/views/stock_picking_views.xml b/rma/views/stock_picking_views.xml new file mode 100644 index 000000000..d1f754001 --- /dev/null +++ b/rma/views/stock_picking_views.xml @@ -0,0 +1,23 @@ + + + + + stock.picking.form + stock.picking + + +
+ +
+
+
+
diff --git a/rma/views/stock_warehouse_views.xml b/rma/views/stock_warehouse_views.xml new file mode 100644 index 000000000..2371182e5 --- /dev/null +++ b/rma/views/stock_warehouse_views.xml @@ -0,0 +1,17 @@ + + + + Stock Warehouse Inherit MRP + stock.warehouse + + + + + + + + + + + + diff --git a/rma/wizard/__init__.py b/rma/wizard/__init__.py new file mode 100644 index 000000000..b81a7402c --- /dev/null +++ b/rma/wizard/__init__.py @@ -0,0 +1,5 @@ +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from . import rma_delivery +from . import rma_split +from . import stock_picking_return diff --git a/rma/wizard/rma_delivery.py b/rma/wizard/rma_delivery.py new file mode 100644 index 000000000..ef72053be --- /dev/null +++ b/rma/wizard/rma_delivery.py @@ -0,0 +1,102 @@ +# Copyright 2020 Tecnativa - Ernesto Tejeda +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import _, api, fields, models +from odoo.exceptions import ValidationError +import odoo.addons.decimal_precision as dp + + +class RmaReDeliveryWizard(models.TransientModel): + _name = 'rma.delivery.wizard' + _description = 'RMA Delivery Wizard' + + rma_count = fields.Integer() + type = fields.Selection( + selection=[ + ('replace', 'Replace'), + ('return', 'Return to customer'), + ], + string="Type", + required=True, + ) + product_id = fields.Many2one( + comodel_name="product.product", + string="Replace Product", + ) + product_uom_qty = fields.Float( + string='Product qty', + digits=dp.get_precision('Product Unit of Measure'), + ) + product_uom = fields.Many2one( + comodel_name="uom.uom", + string="Unit of measure", + ) + scheduled_date = fields.Datetime( + required=True, + default=fields.Datetime.now(), + ) + warehouse_id = fields.Many2one( + comodel_name="stock.warehouse", + string='Warehouse', + required=True, + ) + + @api.constrains('product_uom_qty') + def _check_product_uom_qty(self): + self.ensure_one() + rma_ids = self.env.context.get('active_ids') + if len(rma_ids) == 1 and self.product_uom_qty <= 0: + raise ValidationError(_('Quantity must be greater than 0.')) + + @api.model + def default_get(self, fields_list): + res = super().default_get(fields_list) + rma_ids = self.env.context.get('active_ids') + rma = self.env['rma'].browse(rma_ids) + warehouse_id = self.env['stock.warehouse'].search( + [('company_id', '=', rma[0].company_id.id)], limit=1).id + delivery_type = self.env.context.get('rma_delivery_type') + product_id = False + if len(rma) == 1 and delivery_type == 'return': + product_id = rma.product_id.id + product_uom_qty = 0.0 + if len(rma) == 1 and rma.remaining_qty > 0.0: + product_uom_qty = rma.remaining_qty + res.update( + rma_count=len(rma), + warehouse_id=warehouse_id, + type=delivery_type, + product_id=product_id, + product_uom_qty=product_uom_qty, + ) + return res + + @api.onchange("product_id") + def _onchange_product_id(self): + domain_product_uom = [] + if self.product_id: + domain_product_uom = [ + ('category_id', '=', self.product_id.uom_id.category_id.id) + ] + if (not self.product_uom + or self.product_id.uom_id.id != self.product_uom.id): + self.product_uom = self.product_id.uom_id + return {'domain': {'product_uom': domain_product_uom}} + + def action_deliver(self): + self.ensure_one() + rma_ids = self.env.context.get('active_ids') + rma = self.env['rma'].browse(rma_ids) + if self.type == 'replace': + rma.create_replace( + self.scheduled_date, + self.warehouse_id, + self.product_id, + self.product_uom_qty, + self.product_uom, + ) + elif self.type == 'return': + qty = uom = None + if self.rma_count == 1: + qty, uom = self.product_uom_qty, self.product_uom + rma.create_return(self.scheduled_date, qty, uom) diff --git a/rma/wizard/rma_delivery_views.xml b/rma/wizard/rma_delivery_views.xml new file mode 100644 index 000000000..2efcbc05e --- /dev/null +++ b/rma/wizard/rma_delivery_views.xml @@ -0,0 +1,50 @@ + + + + + rma.delivery.wizard.form + rma.delivery.wizard + +
+ + + + + + + + + + + +
+
+ +
+
+ +
diff --git a/rma/wizard/rma_split.py b/rma/wizard/rma_split.py new file mode 100644 index 000000000..fc8ea8500 --- /dev/null +++ b/rma/wizard/rma_split.py @@ -0,0 +1,70 @@ +# Copyright 2020 Tecnativa - Ernesto Tejeda +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import _, api, fields, models +import odoo.addons.decimal_precision as dp + + +class RmaReSplitWizard(models.TransientModel): + _name = 'rma.split.wizard' + _description = 'RMA Split Wizard' + + rma_id = fields.Many2one( + comodel_name='rma', + string='RMA', + ) + product_uom_qty = fields.Float( + string='Quantity to extract', + digits=dp.get_precision('Product Unit of Measure'), + required=True, + help="Quantity to extract to a new RMA." + ) + product_uom = fields.Many2one( + comodel_name='uom.uom', + string='Unit of measure', + required=True, + ) + + _sql_constraints = [ + ( + 'check_product_uom_qty_positive', + 'CHECK(product_uom_qty > 0)', + 'Quantity must be greater than 0.' + ), + ] + + @api.model + def fields_get(self, allfields=None, attributes=None): + res = super().fields_get(allfields, attributes=attributes) + rma_id = self.env.context.get('active_id') + rma = self.env['rma'].browse(rma_id) + res['product_uom']['domain'] = [ + ('category_id', '=', rma.product_uom.category_id.id) + ] + return res + + @api.model + def default_get(self, fields_list): + res = super().default_get(fields_list) + rma_id = self.env.context.get('active_id') + rma = self.env['rma'].browse(rma_id) + res.update( + rma_id=rma.id, + product_uom_qty=rma.remaining_qty, + product_uom=rma.product_uom.id, + ) + return res + + def action_split(self): + self.ensure_one() + extracted_rma = self.rma_id.extract_quantity( + self.product_uom_qty, self.product_uom) + return { + 'name': _('Extracted RMA'), + 'type': 'ir.actions.act_window', + 'view_type': 'form', + 'view_mode': 'form', + 'res_model': 'rma', + 'views': [(self.env.ref('rma.rma_view_form').id, 'form')], + 'res_id': extracted_rma.id, + } diff --git a/rma/wizard/rma_split_views.xml b/rma/wizard/rma_split_views.xml new file mode 100644 index 000000000..d33ba877b --- /dev/null +++ b/rma/wizard/rma_split_views.xml @@ -0,0 +1,33 @@ + + + + + rma.split.wizard.form + rma.split.wizard + +
+ + + + +
+
+
+
+
+ + Split RMA + rma.split.wizard + form + form + new + +
diff --git a/rma/wizard/stock_picking_return.py b/rma/wizard/stock_picking_return.py new file mode 100644 index 000000000..0608c3410 --- /dev/null +++ b/rma/wizard/stock_picking_return.py @@ -0,0 +1,83 @@ +# Copyright 2020 Tecnativa - Ernesto Tejeda +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import _, api, fields, models +from odoo.exceptions import ValidationError + + +class ReturnPicking(models.TransientModel): + _inherit = 'stock.return.picking' + + create_rma = fields.Boolean( + string="Create RMAs" + ) + picking_type_code = fields.Selection( + selection=[ + ('incoming', 'Vendors'), + ('outgoing', 'Customers'), + ('internal', 'Internal'), + ], + related='picking_id.picking_type_id.code', + store=True, + readonly=True, + ) + + @api.onchange("create_rma") + def _onchange_create_rma(self): + if self.create_rma: + warehouse = self.picking_id.picking_type_id.warehouse_id + self.location_id = warehouse.rma_loc_id.id + rma_loc = warehouse.search([]).mapped('rma_loc_id') + rma_loc_domain = [('id', 'child_of', rma_loc.ids)] + else: + self.location_id = self.default_get(['location_id'])['location_id'] + rma_loc_domain = [ + '|', + ('id', '=', self.picking_id.location_id.id), + ('return_location', '=', True), + ] + return {'domain': {'location_id': rma_loc_domain}} + + def create_returns(self): + """ Override create_returns method for creating one or more + 'confirmed' RMAs after return a delivery picking in case + 'Create RMAs' checkbox is checked in this wizard. + New RMAs will be linked to the delivery picking as the origin + delivery and also RMAs will be linked to the returned picking + as the 'Receipt'. + """ + if self.create_rma: + # set_rma_picking_type is to override the copy() method of stock + # picking and change the default picking type to rma picking type + self_with_context = self.with_context(set_rma_picking_type=True) + res = super(ReturnPicking, self_with_context).create_returns() + partner = self.picking_id.partner_id + if not partner: + raise ValidationError(_( + "You must specify the 'Customer' in the " + "'Stock Picking' from which RMAs will be created")) + picking = self.picking_id + returned_picking = self.env['stock.picking'].browse(res['res_id']) + if hasattr(picking, 'sale_id') and picking.sale_id: + partner_invoice_id = picking.sale_id.partner_invoice_id.id + else: + partner_invoice_id = partner.address_get( + ['invoice']).get('invoice', False), + for move in returned_picking.move_lines: + self.env['rma'].create({ + 'partner_id': partner.id, + 'partner_invoice_id': partner_invoice_id, + 'origin': picking.name, + 'picking_id': picking.id, + 'move_id': move.origin_returned_move_id.id, + 'product_id': move.origin_returned_move_id.product_id.id, + 'product_uom_qty': move.product_uom_qty, + 'product_uom': move.product_uom.id, + 'reception_move_id': move.id, + 'company_id': move.company_id.id, + 'location_id': move.location_dest_id.id, + 'state': 'confirmed', + }) + return res + else: + return super().create_returns() diff --git a/rma/wizard/stock_picking_return_views.xml b/rma/wizard/stock_picking_return_views.xml new file mode 100644 index 000000000..87b8f8ed2 --- /dev/null +++ b/rma/wizard/stock_picking_return_views.xml @@ -0,0 +1,20 @@ + + + + + Return lines inherit RMA + stock.return.picking + + + + + + + + + + + + From 051bad1e1cf627d890b7c0efd15ee8e42e43e78c Mon Sep 17 00:00:00 2001 From: Fernando Colus Date: Wed, 22 Jul 2020 16:44:50 +0000 Subject: [PATCH 002/108] Added translation using Weblate (Portuguese (Brazil)) --- rma/i18n/pt_BR.po | 1570 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1570 insertions(+) create mode 100644 rma/i18n/pt_BR.po diff --git a/rma/i18n/pt_BR.po b/rma/i18n/pt_BR.po new file mode 100644 index 000000000..34cfc5f76 --- /dev/null +++ b/rma/i18n/pt_BR.po @@ -0,0 +1,1570 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * rma +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 12.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2020-07-27 20:19+0000\n" +"Last-Translator: Fernando Colus \n" +"Language-Team: none\n" +"Language: pt_BR\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: nplurals=2; plural=n > 1;\n" +"X-Generator: Weblate 3.10\n" + +#. module: rma +#: model:mail.template,report_name:rma.mail_template_rma_notification +msgid "${(object.name or '')}" +msgstr "${(object.name or '')}" + +#. module: rma +#: model:mail.template,subject:rma.mail_template_rma_notification +msgid "${object.company_id.name} RMA (Ref ${object.name or 'n/a' })" +msgstr "${object.company_id.name} RMA (Ref ${object.name or 'n/a' })" + +#. module: rma +#: code:addons/rma/models/rma_team.py:43 +#, python-format +msgid "%s (copy)" +msgstr "%s (Cópia)" + +#. module: rma +#: model:mail.template,body_html:rma.mail_template_rma_notification +msgid "
\n" +"

\n" +" Dear ${object.partner_id.name}\n" +" % if object.partner_id.parent_id:\n" +" (${object.partner_id.parent_id.name})\n" +" % endif\n" +"

\n" +" Here is the RMA ${object.name} from ${object.company_id.name}.\n" +"

\n" +" Do not hesitate to contact us if you have any question.\n" +"

\n" +"
\n" +" " +msgstr "" +"
\n" +"

\n" +" Dear ${object.partner_id.name}\n" +" % if object.partner_id.parent_id:\n" +" (${object.partner_id.parent_id.name})\n" +" % endif\n" +"

\n" +" Aqui está o RMA ${object.name} from " +"${object.company_id.name}.\n" +"

\n" +" Não deixe de nos contactar se houver qualquer dúvida ou outra questão.\n" +"

\n" +"
\n" +" " + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.portal_rma_page +msgid "" +msgstr "" +"" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.portal_rma_page +msgid " Paid" +msgstr " Pago" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.portal_rma_page +msgid " Waiting Payment" +msgstr " Aguardando Pagamento" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.portal_rma_page +msgid "" +msgstr "" +"" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.portal_rma_page +msgid "" +msgstr "" +"" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.portal_rma_page +msgid " Cancelled" +msgstr "" +" Cancelado" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.portal_rma_page +msgid " Preparation" +msgstr "" +" Preparo" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.portal_rma_page +msgid " Shipped" +msgstr "" +" Embarcado" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.portal_rma_page +msgid " Partially Available" +msgstr "" +" Parcialmente Disponível" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.portal_rma_page +msgid "Delivery" +msgstr "Entrega" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.portal_rma_page +msgid "Reception" +msgstr "Recebimento" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.portal_rma_page +msgid "Refund" +msgstr "Restituição" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.portal_rma_page +msgid "Customer:" +msgstr "Cliente:" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.report_rma_document +msgid "Date:" +msgstr "Data:" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.report_rma_document +msgid "Deadline:" +msgstr "Prazo Final:" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.portal_rma_page +msgid "Deadline" +msgstr "Prazo Final" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.report_rma_document +msgid "Delivered qty:" +msgstr "Quantidade Enviada:" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.portal_rma_page +msgid "Delivered quantity" +msgstr "Quantidade Enviada" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.report_rma_document +msgid "Invoicing address:" +msgstr "Endereço de Cobrança:" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.report_rma_document +msgid "Move:" +msgstr "Movimentação:" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.report_rma_document +msgid "Origin delivery:" +msgstr " Origem da Entrega: " + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.portal_rma_page +msgid "Origin delivery" +msgstr "Origem da Entrega" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.report_rma_document +msgid "Origin:" +msgstr "Origem:" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.portal_rma_page +msgid "Origin" +msgstr "Origem" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.report_rma_document +msgid "Product:" +msgstr "Produto:" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.portal_rma_page +msgid "Product" +msgstr "Produto" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.report_rma_document +msgid "Quantity:" +msgstr "Quantidade:" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.portal_rma_page +msgid "Quantity" +msgstr "Quantidade" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.portal_rma_page +msgid "RMA Date" +msgstr "Data de RMA" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.report_rma_document +msgid "RMA Note:" +msgstr "Comentário do RMA:" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.portal_rma_page +#: model_terms:ir.ui.view,arch_db:rma.report_rma_document +msgid "Responsible:" +msgstr "Responsável:" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.report_rma_document +msgid "State:" +msgstr "Situação:" + +#. module: rma +#: model:ir.model.fields,help:rma.field_rma_team__alias_defaults +msgid "A Python dictionary that will be evaluated to provide default values when creating new records for this alias." +msgstr "" +"Um dicionário Python que será avaliado para fornecer valores padrão ao criar " +"novos registros para esse alias." + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.rma_team_view_form +msgid "Accept Emails From" +msgstr "Aceitar e-mails de" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__access_warning +msgid "Access warning" +msgstr "Aviso de acesso" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__message_needaction +#: model:ir.model.fields,field_description:rma.field_rma_team__message_needaction +msgid "Action Needed" +msgstr "Ação Necessária" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma_team__active +msgid "Active" +msgstr "Ativar/Ativo(a)" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__activity_ids +msgid "Activities" +msgstr "Atividades" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__activity_state +msgid "Activity State" +msgstr "Situação da Atividade" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma_team__alias_id +msgid "Alias" +msgstr "Alias" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma_team__alias_contact +msgid "Alias Contact Security" +msgstr "Segurança do Contato de Alias" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma_team__alias_name +msgid "Alias Name" +msgstr "Nome do Alias" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma_team__alias_domain +msgid "Alias domain" +msgstr "Domínio do Alias" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma_team__alias_model_id +msgid "Aliased Model" +msgstr "Modelo do Alias" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.rma_view_form +msgid "Are you sure you want to cancel this RMA" +msgstr "tem certeza que quer cancelar este RMA" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__message_attachment_count +#: model:ir.model.fields,field_description:rma.field_rma_team__message_attachment_count +msgid "Attachment Count" +msgstr "Contagem de Anexos" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.rma_team_view_form +msgid "Avatar" +msgstr "Avatar" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__can_be_locked +msgid "Can Be Locked" +msgstr "Pode Ser Travado" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__can_be_refunded +msgid "Can Be Refunded" +msgstr "Pode Ser Reembolsado" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__can_be_replaced +msgid "Can Be Replaced" +msgstr "Pode Ser Substituído" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__can_be_returned +msgid "Can Be Returned" +msgstr "Pode Ser Devolvido" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__can_be_split +msgid "Can Be Split" +msgstr "Pode Ser Dividido" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.rma_redelivery_wizard_view_form +#: model_terms:ir.ui.view,arch_db:rma.rma_split_wizard_view_form2 +#: model_terms:ir.ui.view,arch_db:rma.rma_view_form +msgid "Cancel" +msgstr "Cancelar" + +#. module: rma +#: selection:rma,state:0 +msgid "Canceled" +msgstr "Cancelado(a)" + +#. module: rma +#: model_terms:ir.actions.act_window,help:rma.rma_action +#: model_terms:ir.actions.act_window,help:rma.rma_team_action +msgid "Click to add a new RMA." +msgstr "Clique para adicionar um novo RMA." + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__commercial_partner_id +msgid "Commercial Entity" +msgstr "Entidade Comercial" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.portal_rma_page +msgid "Communication" +msgstr "Comunicação" + +#. module: rma +#: model:ir.model,name:rma.model_res_company +msgid "Companies" +msgstr "Empresas" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__company_id +#: model:ir.model.fields,field_description:rma.field_rma_team__company_id +msgid "Company" +msgstr "Empresa" + +#. module: rma +#: model:ir.ui.menu,name:rma.rma_configuration_menu +msgid "Configuration" +msgstr "Configuração" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.rma_view_form +msgid "Confirm" +msgstr "Confirmar" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.rma_view_search +#: selection:rma,state:0 +msgid "Confirmed" +msgstr "Confirmado(a)" + +#. module: rma +#: model:ir.model,name:rma.model_res_partner +#: model_terms:ir.ui.view,arch_db:rma.portal_rma_page +msgid "Contact" +msgstr "Contato" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_stock_return_picking__create_rma +msgid "Create RMAs" +msgstr "Criar RMAs" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__create_uid +#: model:ir.model.fields,field_description:rma.field_rma_delivery_wizard__create_uid +#: model:ir.model.fields,field_description:rma.field_rma_operation__create_uid +#: model:ir.model.fields,field_description:rma.field_rma_split_wizard__create_uid +#: model:ir.model.fields,field_description:rma.field_rma_team__create_uid +msgid "Created by" +msgstr "Criado Por" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__create_date +#: model:ir.model.fields,field_description:rma.field_rma_delivery_wizard__create_date +#: model:ir.model.fields,field_description:rma.field_rma_operation__create_date +#: model:ir.model.fields,field_description:rma.field_rma_split_wizard__create_date +#: model:ir.model.fields,field_description:rma.field_rma_team__create_date +msgid "Created on" +msgstr "Criado em" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__partner_id +msgid "Customer" +msgstr "Cliente" + +#. module: rma +#: model:ir.model.fields,help:rma.field_rma__access_url +msgid "Customer Portal URL" +msgstr "URL do portal do cliente" + +#. module: rma +#: code:addons/rma/controllers/main.py:38 +#: model:ir.model.fields,field_description:rma.field_rma__date +#: model_terms:ir.ui.view,arch_db:rma.portal_my_rmas +#: model_terms:ir.ui.view,arch_db:rma.rma_view_search +#, python-format +msgid "Date" +msgstr "Data" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.portal_rma_page +msgid "Date:" +msgstr "Data:" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__deadline +#: model_terms:ir.ui.view,arch_db:rma.rma_view_search +msgid "Deadline" +msgstr "Prazo Final" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma_team__alias_defaults +msgid "Default Values" +msgstr "Valores Padrão" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.rma_redelivery_wizard_view_form +msgid "Deliver" +msgstr "Entrega" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__delivered_qty +msgid "Delivered qty" +msgstr "Quantidade Enviada" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__delivered_qty_done +msgid "Delivered qty done" +msgstr "Quantidade Envidada Concluída" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.rma_view_form +msgid "Delivery" +msgstr "Entrega" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__delivery_picking_count +msgid "Delivery count" +msgstr "Contagem de Entregas" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__delivery_move_ids +msgid "Delivery reservation" +msgstr "Reserva de Entrega" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__description +#: model_terms:ir.ui.view,arch_db:rma.portal_rma_page +msgid "Description" +msgstr "Descrição" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__display_name +#: model:ir.model.fields,field_description:rma.field_rma_delivery_wizard__display_name +#: model:ir.model.fields,field_description:rma.field_rma_operation__display_name +#: model:ir.model.fields,field_description:rma.field_rma_split_wizard__display_name +#: model:ir.model.fields,field_description:rma.field_rma_team__display_name +msgid "Display Name" +msgstr "Exibir Nome" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.rma_view_search +#: selection:rma,state:0 +msgid "Draft" +msgstr "Rascunho" + +#. module: rma +#: model:mail.message.subtype,name:rma.mt_rma_draft +#: model:mail.message.subtype,name:rma.mt_rma_team_rma_draft +msgid "Draft RMA" +msgstr "Rascunho do RMA" + +#. module: rma +#: code:addons/rma/models/rma.py:1076 +#, python-format +msgid "E-mail subject: %s\n" +"\n" +"E-mail body:\n" +"%s" +msgstr "" +"Assunto do E-mail: %s\n" +"\n" +"Corpo do E-mail:\n" +"%s" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.rma_team_view_form +msgid "Email" +msgstr "Email" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.rma_team_view_form +msgid "Email Alias" +msgstr "Alias do Email" + +#. module: rma +#: code:addons/rma/wizard/rma_split.py:63 +#, python-format +msgid "Extracted RMA" +msgstr "RMA Extraído" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__origin_split_rma_id +msgid "Extracted from" +msgstr "Extraído de" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__message_follower_ids +#: model:ir.model.fields,field_description:rma.field_rma_team__message_follower_ids +msgid "Followers" +msgstr "Seguidores" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__message_channel_ids +#: model:ir.model.fields,field_description:rma.field_rma_team__message_channel_ids +msgid "Followers (Channels)" +msgstr "Seguidores (Canais)" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__message_partner_ids +#: model:ir.model.fields,field_description:rma.field_rma_team__message_partner_ids +msgid "Followers (Partners)" +msgstr "Seguidores (Parceiros)" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.rma_view_search +msgid "Group By" +msgstr "Agrupado Por" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__id +#: model:ir.model.fields,field_description:rma.field_rma_delivery_wizard__id +#: model:ir.model.fields,field_description:rma.field_rma_operation__id +#: model:ir.model.fields,field_description:rma.field_rma_split_wizard__id +#: model:ir.model.fields,field_description:rma.field_rma_team__id +msgid "ID" +msgstr "ID" + +#. module: rma +#: model:ir.model.fields,help:rma.field_rma_team__alias_parent_thread_id +msgid "ID of the parent record holding the alias (example: project holding the task creation alias)" +msgstr "" +"ID do registro pai que contém o alias (exemplo: projeto que contém o alias " +"de criação da tarefa)" + +#. module: rma +#: model:ir.model.fields,help:rma.field_rma__message_unread +#: model:ir.model.fields,help:rma.field_rma_team__message_unread +msgid "If checked new messages require your attention." +msgstr "Se marcada, novas mensagens requerem sua atenção." + +#. module: rma +#: model:ir.model.fields,help:rma.field_rma__message_needaction +#: model:ir.model.fields,help:rma.field_rma_team__message_needaction +msgid "If checked, new messages require your attention." +msgstr "Se marcada, novas mensagens requerem sua atenção." + +#. module: rma +#: model:ir.model.fields,help:rma.field_rma__message_has_error +#: model:ir.model.fields,help:rma.field_rma_team__message_has_error +msgid "If checked, some messages have a delivery error." +msgstr "Se marcada, algumas mensagens apresentam um erro de entrega." + +#. module: rma +#: model:ir.model.fields,help:rma.field_rma_team__active +msgid "If the active field is set to false, it will allow you to hide the RMA Team without removing it." +msgstr "" +"Se o campo ativo estiver definido como falso, permitirá ocultar a equipe RMA " +"sem removê-lo." + +#. module: rma +#: code:addons/rma/models/rma.py:1080 +#, python-format +msgid "Incoming e-mail" +msgstr "E-mail recebido" + +#. module: rma +#: model:ir.model,name:rma.model_account_invoice +msgid "Invoice" +msgstr "Fatura" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__partner_invoice_id +msgid "Invoice Address" +msgstr "Endereço de Faturamento" + +#. module: rma +#: model:ir.model,name:rma.model_account_invoice_line +msgid "Invoice Line" +msgstr "Linha da Fatura" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__message_is_follower +#: model:ir.model.fields,field_description:rma.field_rma_team__message_is_follower +msgid "Is Follower" +msgstr "É Seguidor" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma____last_update +#: model:ir.model.fields,field_description:rma.field_rma_delivery_wizard____last_update +#: model:ir.model.fields,field_description:rma.field_rma_operation____last_update +#: model:ir.model.fields,field_description:rma.field_rma_split_wizard____last_update +#: model:ir.model.fields,field_description:rma.field_rma_team____last_update +msgid "Last Modified on" +msgstr "Última Modificação Feita em" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__write_uid +#: model:ir.model.fields,field_description:rma.field_rma_delivery_wizard__write_uid +#: model:ir.model.fields,field_description:rma.field_rma_operation__write_uid +#: model:ir.model.fields,field_description:rma.field_rma_split_wizard__write_uid +#: model:ir.model.fields,field_description:rma.field_rma_team__write_uid +msgid "Last Updated by" +msgstr "Última Atualização Feita por" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__write_date +#: model:ir.model.fields,field_description:rma.field_rma_delivery_wizard__write_date +#: model:ir.model.fields,field_description:rma.field_rma_operation__write_date +#: model:ir.model.fields,field_description:rma.field_rma_split_wizard__write_date +#: model:ir.model.fields,field_description:rma.field_rma_team__write_date +msgid "Last Updated on" +msgstr "Última Atualização Feita em" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__location_id +msgid "Location" +msgstr "Localização" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.rma_view_form +msgid "Lock" +msgstr "Travar" + +#. module: rma +#: selection:rma,state:0 +msgid "Locked" +msgstr "Travado" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__message_main_attachment_id +#: model:ir.model.fields,field_description:rma.field_rma_team__message_main_attachment_id +msgid "Main Attachment" +msgstr "Anexo Principal" + +#. module: rma +#: model:ir.module.category,description:rma.rma_module_category +msgid "Manage Return Merchandise Authorizations (RMAs)." +msgstr "Gerenciar Autorizações de Devolução de Mercadoria (RMAs)." + +#. module: rma +#: model:res.groups,name:rma.rma_group_manager +msgid "Manager" +msgstr "Gerente" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__message_has_error +#: model:ir.model.fields,field_description:rma.field_rma_team__message_has_error +msgid "Message Delivery error" +msgstr "Erro de Entrega de Mensagem" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__message_ids +#: model:ir.model.fields,field_description:rma.field_rma_team__message_ids +msgid "Messages" +msgstr "Mensagens" + +#. module: rma +#: code:addons/rma/controllers/main.py:39 +#: model:ir.model.fields,field_description:rma.field_rma__name +#: model:ir.model.fields,field_description:rma.field_rma_operation__name +#: model:ir.model.fields,field_description:rma.field_rma_team__name +#, python-format +msgid "Name" +msgstr "Nome" + +#. module: rma +#: code:addons/rma/models/rma.py:31 +#: code:addons/rma/models/rma.py:496 +#: code:addons/rma/models/rma.py:1079 +#, python-format +msgid "New" +msgstr "Novo(a)" + +#. module: rma +#: model:mail.message.subtype,description:rma.mt_rma_draft +msgid "New RMA in draft state" +msgstr "Novo Rascunho de RMA" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__activity_date_deadline +msgid "Next Activity Deadline" +msgstr "Prazo Final da Próxima Atividade" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__activity_summary +msgid "Next Activity Summary" +msgstr "Resumo da Próxima Atividade" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__activity_type_id +msgid "Next Activity Type" +msgstr "Tipo da Próxima Atividade" + +#. module: rma +#: code:addons/rma/models/rma.py:757 +#, python-format +msgid "None of the selected RMAs can perform a replacement." +msgstr "Nenhum dos RMAs selecionados pode executar uma substituição." + +#. module: rma +#: code:addons/rma/models/rma.py:740 +#, python-format +msgid "None of the selected RMAs can perform a return." +msgstr "Nenhum dos RMAs selecionados pode executar uma devolução." + +#. module: rma +#: selection:rma,priority:0 +msgid "Normal" +msgstr "Normal" + +#. module: rma +#: selection:rma,priority:0 +msgid "Not urgent" +msgstr "Não é urgente" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__message_needaction_counter +#: model:ir.model.fields,field_description:rma.field_rma_team__message_needaction_counter +msgid "Number of Actions" +msgstr "Número de Ações" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__message_has_error_counter +#: model:ir.model.fields,field_description:rma.field_rma_team__message_has_error_counter +msgid "Number of error" +msgstr "Número de Erros" + +#. module: rma +#: model:ir.model.fields,help:rma.field_rma__message_needaction_counter +#: model:ir.model.fields,help:rma.field_rma_team__message_needaction_counter +msgid "Number of messages which requires an action" +msgstr "Número de Mensagens que Requerem sua Atenção" + +#. module: rma +#: model:ir.model.fields,help:rma.field_rma__message_has_error_counter +#: model:ir.model.fields,help:rma.field_rma_team__message_has_error_counter +msgid "Number of messages with delivery error" +msgstr "Número de Mensagens com Erro de Entrega" + +#. module: rma +#: model:ir.model.fields,help:rma.field_rma__message_unread_counter +#: model:ir.model.fields,help:rma.field_rma_team__message_unread_counter +msgid "Number of unread messages" +msgstr "Número de Mensagens Não Lidas" + +#. module: rma +#: model:ir.model.fields,help:rma.field_rma_team__alias_force_thread_id +msgid "Optional ID of a thread (record) to which all incoming messages will be attached, even if they did not reply to it. If set, this will disable the creation of new records completely." +msgstr "" +"ID opcional de um encadeamento (registro) ao qual todas as mensagens " +"recebidas serão anexadas, mesmo que não tenham respondido. Se definido, isso " +"desativará a criação de novos registros completamente." + +#. module: rma +#: model:ir.ui.menu,name:rma.rma_orders_menu +msgid "Orders" +msgstr "Pedidos" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__picking_id +msgid "Origin Delivery" +msgstr "Origem da Entrega" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__move_id +msgid "Origin move" +msgstr "Origem da Movimentação" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.rma_view_form +msgid "Other Information" +msgstr "Outra Informação" + +#. module: rma +#: selection:rma,activity_state:0 +msgid "Overdue" +msgstr "Vencidos(as)" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma_team__alias_user_id +msgid "Owner" +msgstr "Proprietário" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma_team__alias_parent_model_id +msgid "Parent Model" +msgstr "Modelo Pai" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma_team__alias_parent_thread_id +msgid "Parent Record Thread ID" +msgstr "ID do Encadeamento do Registro Pai" + +#. module: rma +#: model:ir.model.fields,help:rma.field_rma_team__alias_parent_model_id +msgid "Parent model holding the alias. The model holding the alias reference is not necessarily the model given by alias_model_id (example: project (parent_model) and task (model))" +msgstr "" +"Modelo pai que contém o alias. O modelo que contém a referência de alias não " +"é necessariamente o modelo fornecido pelo alias_model_id (exemplo: projeto " +"(pai_modelo) e tarefa (modelo))" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.rma_view_search +msgid "Partner" +msgstr "Parceiro" + +#. module: rma +#: selection:rma,activity_state:0 +msgid "Planned" +msgstr "Planejado)a)" + +#. module: rma +#: model:ir.model.fields,help:rma.field_rma_team__alias_contact +msgid "Policy to post a message on the document using the mailgateway.\n" +"- everyone: everyone can post\n" +"- partners: only authenticated partners\n" +"- followers: only followers of the related document or members of following channels\n" +"" +msgstr "" +"Política para postar uma mensagem no documento usando o mail gateway.\n" +"- todos: todos podem postar\n" +"- parceiros: apenas parceiros autenticados\n" +"- seguidores: apenas seguidores do documento relacionado ou membros dos " +"seguintes canais\n" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__access_url +msgid "Portal Access URL" +msgstr "URL de acesso ao portal" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.rma_view_form +msgid "Preview" +msgstr "Pré-visualização" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__priority +msgid "Priority" +msgstr "Prioridade" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__procurement_group_id +msgid "Procurement group" +msgstr "Agrupamento de Aquisição" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__product_id +#: model_terms:ir.ui.view,arch_db:rma.portal_my_rmas +msgid "Product" +msgstr "Produto" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma_delivery_wizard__product_uom_qty +msgid "Product qty" +msgstr "Quantidade de Produto" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__product_uom_qty +#: model_terms:ir.ui.view,arch_db:rma.portal_my_rmas +msgid "Quantity" +msgstr "Quantidade" + +#. module: rma +#: code:addons/rma/wizard/rma_delivery.py:49 +#: sql_constraint:rma.split.wizard:0 +#, python-format +msgid "Quantity must be greater than 0." +msgstr "Quantidade precisa ser maior que zero." + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma_split_wizard__product_uom_qty +msgid "Quantity to extract" +msgstr "Quantidade a extrair" + +#. module: rma +#: code:addons/rma/models/rma.py:790 +#, python-format +msgid "Quantity to extract cannot be greater than remaining delivery quantity (%s %s)" +msgstr "" +"A quantidade a extrair não pode ser maior que a quantidade de entrega " +"restante (%s %s)" + +#. module: rma +#: model:ir.model.fields,help:rma.field_rma_split_wizard__product_uom_qty +msgid "Quantity to extract to a new RMA." +msgstr "Quantidade a extrair para um novo RMA." + +#. module: rma +#: model:ir.actions.act_window,name:rma.rma_action +#: model:ir.model,name:rma.model_rma +#: model:ir.model.fields,field_description:rma.field_account_invoice_line__rma_id +#: model:ir.model.fields,field_description:rma.field_rma_split_wizard__rma_id +#: model:ir.model.fields,field_description:rma.field_stock_warehouse__rma +#: model:ir.module.category,name:rma.rma_module_category +#: model:ir.ui.menu,name:rma.rma_menu +#: model_terms:ir.ui.view,arch_db:rma.view_partner_form +#: model_terms:ir.ui.view,arch_db:rma.view_picking_form +msgid "RMA" +msgstr "RMA" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.portal_my_rmas +#: model_terms:ir.ui.view,arch_db:rma.report_rma_document +msgid "RMA #" +msgstr "RMA #" + +#. module: rma +#: code:addons/rma/models/res_company.py:18 +#, python-format +msgid "RMA Code" +msgstr "Código RMA" + +#. module: rma +#: code:addons/rma/models/stock_warehouse.py:82 +#, python-format +msgid "RMA Delivery Orders" +msgstr "Pedidos de Entrega RMA" + +#. module: rma +#: model:ir.model,name:rma.model_rma_delivery_wizard +msgid "RMA Delivery Wizard" +msgstr "Assistente de Entrega RMA" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_stock_warehouse__rma_in_type_id +msgid "RMA In Type" +msgstr "Tipo Entrada RMA" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_stock_warehouse__rma_loc_id +msgid "RMA Location" +msgstr "Localização RMA" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.portal_rma_page +msgid "RMA Order -" +msgstr "Pedido RMA -" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.portal_my_home_menu_rma +#: model_terms:ir.ui.view,arch_db:rma.portal_my_home_rma +#: model_terms:ir.ui.view,arch_db:rma.portal_my_rmas +msgid "RMA Orders" +msgstr "Pedidos RMA" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_stock_warehouse__rma_out_type_id +msgid "RMA Out Type" +msgstr "Tipo Saída RMA" + +#. module: rma +#: code:addons/rma/models/stock_warehouse.py:73 +#, python-format +msgid "RMA Receipts" +msgstr "Recebimentos RMA" + +#. module: rma +#: model:ir.actions.report,name:rma.report_rma_action +msgid "RMA Report" +msgstr "Relatório RMA" + +#. module: rma +#: model:ir.model,name:rma.model_rma_split_wizard +msgid "RMA Split Wizard" +msgstr "Assistente para divisão de RMA" + +#. module: rma +#: model:ir.model,name:rma.model_rma_team +#: model:ir.model.fields,field_description:rma.field_res_users__rma_team_id +#: model:ir.ui.menu,name:rma.rma_configuration_rma_team_menu +#: model_terms:ir.ui.view,arch_db:rma.rma_team_view_form +msgid "RMA Team" +msgstr "Equipe RMA" + +#. module: rma +#: model:ir.model.fields,help:rma.field_res_users__rma_team_id +msgid "RMA Team the user is member of." +msgstr "Equipe de RMA da qual o usuário é membro." + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_res_partner__rma_count +#: model:ir.model.fields,field_description:rma.field_res_users__rma_count +#: model:ir.model.fields,field_description:rma.field_stock_picking__rma_count +msgid "RMA count" +msgstr "Contagem de RMA" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_stock_move__rma_receiver_ids +msgid "RMA receivers" +msgstr "Destinatários RMA" + +#. module: rma +#: model:ir.model.fields,help:rma.field_stock_warehouse__rma +msgid "RMA related products can be stored in this warehouse." +msgstr "Os produtos relacionados ao RMA podem ser armazenados neste armazém." + +#. module: rma +#: model:ir.model,name:rma.model_rma_operation +msgid "RMA requested operation" +msgstr "Operação solicitada pelo RMA" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_stock_move__rma_id +msgid "RMA return" +msgstr "Devolução RMA" + +#. module: rma +#: model:ir.actions.act_window,name:rma.rma_team_action +#: model:ir.model.fields,field_description:rma.field_rma__team_id +msgid "RMA team" +msgstr "Equipe RMA" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_res_partner__rma_ids +#: model:ir.model.fields,field_description:rma.field_res_users__rma_ids +#: model:ir.model.fields,field_description:rma.field_stock_move__rma_ids +msgid "RMAs" +msgstr "RMAs" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.rma_view_form +msgid "Receipt" +msgstr "Recebimento" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.rma_view_search +#: selection:rma,state:0 +msgid "Received" +msgstr "Recebido" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__reception_move_id +msgid "Reception move" +msgstr "Recebimento de Movimentação" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma_team__alias_force_thread_id +msgid "Record Thread ID" +msgstr "ID do segmento de registro" + +#. module: rma +#: model:ir.model.fields,help:rma.field_rma__origin +msgid "Reference of the document that generated this RMA." +msgstr "Referência do documento que gerou este RMA." + +#. module: rma +#: code:addons/rma/models/rma.py:679 +#: model:ir.model.fields,field_description:rma.field_rma__refund_id +#: model_terms:ir.ui.view,arch_db:rma.rma_view_form +#: model:rma.operation,name:rma.rma_operation_refund +#, python-format +msgid "Refund" +msgstr "Restituição" + +#. module: rma +#: model:ir.model.fields,help:rma.field_rma__partner_invoice_id +msgid "Refund address for current RMA." +msgstr "Endereço de reembolso para o RMA atual." + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__refund_line_id +msgid "Refund line" +msgstr "Linha de Restituição" + +#. module: rma +#: selection:rma,state:0 +msgid "Refunded" +msgstr "Restituído" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__remaining_qty +msgid "Remaining delivered qty" +msgstr "Quantidade Entregue Restante" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__remaining_qty_to_done +msgid "Remaining delivered qty to done" +msgstr "Quantidade restante entregue a concluir" + +#. module: rma +#: model:rma.operation,name:rma.rma_operation_return +msgid "Repair" +msgstr "Reparo" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.rma_view_form +#: selection:rma.delivery.wizard,type:0 +#: model:rma.operation,name:rma.rma_operation_replace +msgid "Replace" +msgstr "Substituir" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma_delivery_wizard__product_id +msgid "Replace Product" +msgstr "Substituir Produto" + +#. module: rma +#: selection:rma,state:0 +msgid "Replaced" +msgstr "Substituído" + +#. module: rma +#: code:addons/rma/models/rma.py:995 +#, python-format +msgid "Replacement: Move %s (Picking %s) has been created." +msgstr "" +"Substituição: Movimentação %s (A Coleta %s foi criada." + +#. module: rma +#: code:addons/rma/models/rma.py:1006 +#, python-format +msgid "Replacement:
Product %s
Quantity %f %s
This replacement did not create a new move, but one of the previously created moves was updated with this data." +msgstr "" +"Substituição:
Produto %s
Quantidade %f%s
Essa substituição não " +"criou uma nova movimentação, mas uma das movimentos criadas anteriormente " +"foi atualizada com esses dados." + +#. module: rma +#: model:ir.ui.menu,name:rma.rma_reporting_menu +msgid "Reporting" +msgstr "Comunicando" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__operation_id +msgid "Requested operation" +msgstr "Operação Solicitada" + +#. module: rma +#: code:addons/rma/models/rma.py:721 +#, python-format +msgid "Required field(s):%s" +msgstr "Campo(s) Solicitado(s):%s" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__user_id +#: model_terms:ir.ui.view,arch_db:rma.rma_view_search +msgid "Responsible" +msgstr "Responsável" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__activity_user_id +msgid "Responsible User" +msgstr "Usuário Responsável" + +#. module: rma +#: model:ir.model,name:rma.model_stock_return_picking +msgid "Return Picking" +msgstr "Retorno de Coleta" + +#. module: rma +#: model:ir.actions.act_window,name:rma.rma_delivery_wizard_action +#: model_terms:ir.ui.view,arch_db:rma.rma_view_form +#: selection:rma.delivery.wizard,type:0 +msgid "Return to customer" +msgstr "Retornar ao cliente" + +#. module: rma +#: code:addons/rma/models/rma.py:956 +#, python-format +msgid "Return: %s has been created." +msgstr "" +"Retorno: %s foi criado." + +#. module: rma +#: selection:rma,state:0 +msgid "Returned" +msgstr "Retornado(a)" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma_delivery_wizard__rma_count +msgid "Rma Count" +msgstr "Contagem de RMA" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma_delivery_wizard__scheduled_date +msgid "Scheduled Date" +msgstr "Data Programada" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__access_token +msgid "Security Token" +msgstr "Token de Segurança" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.rma_view_form +msgid "Send by Email" +msgstr "Enviado por Email" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.rma_view_form +msgid "Send by Mail" +msgstr "Enviado(a) por Correio" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__sent +msgid "Sent" +msgstr "Enviado(a)" + +#. module: rma +#: code:addons/rma/models/stock_warehouse.py:48 +#, python-format +msgid "Sequence RMA in" +msgstr "Sequência de Entrada RMA" + +#. module: rma +#: code:addons/rma/models/stock_warehouse.py:53 +#, python-format +msgid "Sequence RMA out" +msgstr "Sequência de Saída RMA" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.rma_view_form +msgid "Set to draft" +msgstr "Marcado como Rascunho" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.rma_view_form +msgid "Share" +msgstr "Compartilhar" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__origin +msgid "Source Document" +msgstr "Documento Fonte" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.rma_split_wizard_view_form2 +#: model_terms:ir.ui.view,arch_db:rma.rma_view_form +msgid "Split" +msgstr "Dividir" + +#. module: rma +#: model:ir.actions.act_window,name:rma.rma_split_wizard_action +msgid "Split RMA" +msgstr "Dividir RMA" + +#. module: rma +#: code:addons/rma/models/rma.py:874 +#, python-format +msgid "Split: %s has been created." +msgstr "" +"Divisão: %s foi " +"criada." + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__state +#: model_terms:ir.ui.view,arch_db:rma.rma_view_search +msgid "State" +msgstr "Situação" + +#. module: rma +#: code:addons/rma/controllers/main.py:40 +#: model_terms:ir.ui.view,arch_db:rma.portal_my_rmas +#, python-format +msgid "Status" +msgstr "Status" + +#. module: rma +#: model:ir.model.fields,help:rma.field_rma__activity_state +msgid "Status based on activities\n" +"Overdue: Due date is already passed\n" +"Today: Activity date is today\n" +"Planned: Future activities." +msgstr "" +"Status baseado em atividades\n" +"Vencido: a data de vencimento já passou\n" +"Hoje: a data da atividade é hoje\n" +"Planejado: Atividades futuras." + +#. module: rma +#: model:ir.model,name:rma.model_stock_move +msgid "Stock Move" +msgstr "Movimentação de Estoque" + +#. module: rma +#: model:ir.model,name:rma.model_stock_rule +msgid "Stock Rule" +msgstr "Regra de Estoque" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma_team__user_id +msgid "Team Leader" +msgstr "Líder de Equipe" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma_team__member_ids +#: model_terms:ir.ui.view,arch_db:rma.rma_team_view_form +msgid "Team Members" +msgstr "Membros da Equipe" + +#. module: rma +#: sql_constraint:rma.operation:0 +msgid "That operation name already exists !" +msgstr "Esse nome de operação já existe!" + +#. module: rma +#: model:ir.model.fields,help:rma.field_rma_team__alias_model_id +msgid "The model (Odoo Document Kind) to which this alias corresponds. Any incoming email that does not reply to an existing record will cause the creation of a new record of this model (e.g. a Project Task)" +msgstr "" +"Modelo (tipo de documento Odoo) ao qual esse alias corresponde. Qualquer e-" +"mail recebido que não responda a um registro existente causará a criação de " +"um novo registro desse modelo (por exemplo, uma tarefa do projeto)" + +#. module: rma +#: model:ir.model.fields,help:rma.field_rma_team__alias_name +msgid "The name of the email alias, e.g. 'jobs' if you want to catch emails for " +msgstr "" +"O nome do alias do email, por exemplo 'jobs' se você deseja receber e-mails " +"de " + +#. module: rma +#: model:ir.model.fields,help:rma.field_rma_team__alias_user_id +msgid "The owner of records created upon receiving emails on this alias. If this field is not set the system will attempt to find the right owner based on the sender (From) address, or will use the Administrator account if no system user is found for that address." +msgstr "" +"O proprietário dos registros criados ao receber emails nesse alias. Se esse " +"campo não estiver definido, o sistema tentará encontrar o proprietário certo " +"com base no endereço do remetente (De) ou usará a conta de Administrador se " +"nenhum usuário do sistema for encontrado para esse endereço." + +#. module: rma +#: code:addons/rma/models/stock_move.py:60 +#, python-format +msgid "The quantity done for the product '%s' must be equal to its initial demand because the stock move is linked to an RMA (%s)." +msgstr "" +"A quantidade realizada para o produto '%s' deve ser igual à sua demanda " +"inicial, porque a movimentação do estoque está vinculada a uma RMA (%s)." + +#. module: rma +#: code:addons/rma/models/rma.py:778 +#, python-format +msgid "The quantity to return is greater than remaining quantity." +msgstr "A quantidade a retornar é maior que a quantidade restante." + +#. module: rma +#: code:addons/rma/models/account_invoice.py:22 +#, python-format +msgid "There is at least one invoice lines whose quantity is less than the quantity specified in its linked RMA." +msgstr "" +"Há pelo menos uma linha de fatura cuja quantidade é menor que a quantidade " +"especificada na sua RMA vinculada." + +#. module: rma +#: code:addons/rma/models/rma.py:767 +#, python-format +msgid "This RMA cannot be split." +msgstr "Esta RMA não pode ser dividida." + +#. module: rma +#: code:addons/rma/models/rma.py:754 +#, python-format +msgid "This RMA cannot perform a replacement." +msgstr "Esta RMA não pode executar uma substituição." + +#. module: rma +#: code:addons/rma/models/rma.py:737 +#, python-format +msgid "This RMA cannot perform a return." +msgstr "Este RMA não pode executar uma devolução." + +#. module: rma +#: model:ir.actions.server,name:rma.rma_refund_action_server +#: model_terms:ir.ui.view,arch_db:rma.rma_view_form +msgid "To Refund" +msgstr "A Ser Restituído" + +#. module: rma +#: selection:rma,activity_state:0 +msgid "Today" +msgstr "Hoje" + +#. module: rma +#: model:ir.model,name:rma.model_stock_picking +msgid "Transfer" +msgstr "Transferir" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma_delivery_wizard__type +msgid "Type" +msgstr "Tipo" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_stock_return_picking__picking_type_code +msgid "Type of Operation" +msgstr "Tipo de Operação" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.rma_view_search +msgid "Unassigned RMAs" +msgstr "RMAs não atribuídos" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma_delivery_wizard__product_uom +#: model:ir.model.fields,field_description:rma.field_rma_split_wizard__product_uom +msgid "Unit of measure" +msgstr "Unidade de Medida" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.rma_view_form +msgid "Unlock" +msgstr "Destravar" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__message_unread +#: model:ir.model.fields,field_description:rma.field_rma_team__message_unread +msgid "Unread Messages" +msgstr "Mensagens não lidas" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__message_unread_counter +#: model:ir.model.fields,field_description:rma.field_rma_team__message_unread_counter +msgid "Unread Messages Counter" +msgstr "Contador de Mensagens Não Lidas" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__product_uom +msgid "UoM" +msgstr "Unidade de Medida" + +#. module: rma +#: selection:rma,priority:0 +msgid "Urgent" +msgstr "Urgente" + +#. module: rma +#: model:res.groups,name:rma.rma_group_user_all +msgid "User: All Documents" +msgstr "Usuário: Todos os Documentos" + +#. module: rma +#: model:res.groups,name:rma.rma_group_user_own +msgid "User: Own Documents Only" +msgstr "Usuário: Somente Seus Documentos" + +#. module: rma +#: model:ir.model,name:rma.model_res_users +msgid "Users" +msgstr "Usuários" + +#. module: rma +#: selection:rma,priority:0 +msgid "Very Urgent" +msgstr "Muito Urgente" + +#. module: rma +#: selection:rma,state:0 +msgid "Waiting for refund" +msgstr "Aguardando Restituição" + +#. module: rma +#: selection:rma,state:0 +msgid "Waiting for replacement" +msgstr "Aguardando Substituição" + +#. module: rma +#: selection:rma,state:0 +msgid "Waiting for return" +msgstr "Aguardando Devolução" + +#. module: rma +#: model:ir.model,name:rma.model_stock_warehouse +#: model:ir.model.fields,field_description:rma.field_rma__warehouse_id +#: model:ir.model.fields,field_description:rma.field_rma_delivery_wizard__warehouse_id +msgid "Warehouse" +msgstr "Armazém" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__website_message_ids +#: model:ir.model.fields,field_description:rma.field_rma_team__website_message_ids +msgid "Website Messages" +msgstr "Mensagens do Site" + +#. module: rma +#: model:ir.model.fields,help:rma.field_rma__website_message_ids +#: model:ir.model.fields,help:rma.field_rma_team__website_message_ids +msgid "Website communication history" +msgstr "Histórico de Comunicação Através do Site" + +#. module: rma +#: code:addons/rma/models/rma.py:514 +#, python-format +msgid "You cannot delete RMAs that are not in draft state" +msgstr "Você não pode apagar RMAs que não estejam na situação \"Rascunho\"" + +#. module: rma +#: code:addons/rma/wizard/stock_picking_return.py:56 +#, python-format +msgid "You must specify the 'Customer' in the 'Stock Picking' from which RMAs will be created" +msgstr "" +"Você deve especificar o 'Cliente' na 'Seleção de estoque' a partir do qual " +"as RMAs serão criadas" + +#. module: rma +#: model:res.groups,comment:rma.rma_group_user_all +msgid "the user will have access to all records of everyone in the RMA application." +msgstr "o usuário terá acesso a todos os registros de todos na aplicação RMA." + +#. module: rma +#: model:res.groups,comment:rma.rma_group_user_own +msgid "the user will have access to his own data in the RMA application." +msgstr "o usuário terá acesso aos seus próprios dados na aplicação RMA." + +#. module: rma +#: model:res.groups,comment:rma.rma_group_manager +msgid "the user will have an access to the RMA configuration as well as statistic reports." +msgstr "" +"o usuário terá acesso à configuração RMA, bem como aos relatórios " +"estatísticos." From f94eadee13dcd45f88bf2fe3a721c3cd49ad3f4b Mon Sep 17 00:00:00 2001 From: david Date: Fri, 14 Aug 2020 12:15:42 +0200 Subject: [PATCH 003/108] [FIX+IMP] rma: view permissions + portal views access errors + teams flow + Translated using Weblate (Spanish) [FIX] rma: views permissions Regular users don't have permissions to rma models, so we should avoid loading views that lead to permission errors. TT24986 rma 12.0.1.1.0 Update translation files Updated by "Update PO files to match POT (msgmerge)" hook in Weblate. Translation: rma-12.0/rma-12.0-rma Translate-URL: https://translation.odoo-community.org/projects/rma-12-0/rma-12-0-rma/ [FIX] rma: portal views access errors - Portal mail thread needs token config. - Unpublished products will raise AccessError on RMAs portal views for portal users due to record rules. - Ensure active_id when getting actions in rma, since we could come from a context that pollutes the expected active rma id. [IMP] rma: teams flow - If no RMA Team is set, we'll assign a default one to the new RMA. - A sequence is now used to search for the top team and assign it. - No default user is assigned when it's not in the context (i.e. portal rmas). [UPD] Update rma.pot rma 12.0.1.2.0 Update translation files Updated by "Update PO files to match POT (msgmerge)" hook in Weblate. Translation: rma-12.0/rma-12.0-rma Translate-URL: https://translation.odoo-community.org/projects/rma-12-0/rma-12-0-rma/ Translated using Weblate (Spanish) Currently translated at 96.2% (253 of 263 strings) Translation: rma-12.0/rma-12.0-rma Translate-URL: https://translation.odoo-community.org/projects/rma-12-0/rma-12-0-rma/es/ --- rma/README.rst | 19 ++- rma/__manifest__.py | 2 +- rma/i18n/es.po | 66 ++++----- rma/i18n/pt_BR.po | 200 ++++++++++++++++++---------- rma/i18n/rma.pot | 43 +++--- rma/models/rma.py | 29 +++- rma/models/rma_team.py | 3 +- rma/readme/USAGE.rst | 19 ++- rma/static/description/index.html | 25 +++- rma/views/res_partner_views.xml | 1 + rma/views/rma_portal_templates.xml | 12 +- rma/views/rma_team_views.xml | 11 ++ rma/views/stock_picking_views.xml | 1 + rma/views/stock_warehouse_views.xml | 1 + 14 files changed, 294 insertions(+), 138 deletions(-) diff --git a/rma/README.rst b/rma/README.rst index 788f6684a..dea2df0f2 100644 --- a/rma/README.rst +++ b/rma/README.rst @@ -89,8 +89,23 @@ An RMA can also be created from a return of a delivery order: Every RMA will be in confirmed state and they will be linked to the returning operation generated previously. -**Note: An RMA can also be created from an incoming email (See configuration -section).** +There are Optional RMA Teams that can be used for: + + - Organize RMAs in sections. + - Subscribe users to notifications. + - Create RMAs from incoming mail to special aliases (See configuration + section). + +To create an RMA Team (RMA Responsible user level required): + + #. Go to *RMA > Configuration > RMA Teams* + #. Create a new team and assign a name, a responsible and members. + #. Subscribe users to notifications, that can be of these subtypes: + + - RMA draft. When a new RMA is created. + - Notes, Debates, Activities. As in standard Odoo. + #. In the list view, use the cross handle to sort RMA Teams. The top team + will be the default one if no team is set. Known issues / Roadmap ====================== diff --git a/rma/__manifest__.py b/rma/__manifest__.py index c06a9a0ad..469596dad 100644 --- a/rma/__manifest__.py +++ b/rma/__manifest__.py @@ -3,7 +3,7 @@ { "name": "Return Merchandise Authorization Management", "summary": "Return Merchandise Authorization (RMA)", - "version": "12.0.1.0.0", + "version": "12.0.1.2.0", "development_status": "Beta", "category": "RMA", "website": "https://github.com/OCA/rma", diff --git a/rma/i18n/es.po b/rma/i18n/es.po index 242403e8d..e778d696c 100644 --- a/rma/i18n/es.po +++ b/rma/i18n/es.po @@ -7,15 +7,15 @@ msgstr "" "Project-Id-Version: Odoo Server 12.0\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2020-06-21 05:13+0000\n" -"PO-Revision-Date: 2020-06-21 01:16-0400\n" -"Last-Translator: <>\n" +"PO-Revision-Date: 2020-08-18 13:59+0000\n" +"Last-Translator: David Vidal \n" "Language-Team: \n" "Language: es\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: \n" -"X-Generator: Poedit 2.0.6\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 3.10\n" #. module: rma #: model:mail.template,report_name:rma.mail_template_rma_notification @@ -28,7 +28,7 @@ msgid "${object.company_id.name} RMA (Ref ${object.name or 'n/a' })" msgstr "" #. module: rma -#: code:addons/rma/models/rma_team.py:43 +#: code:addons/rma/models/rma_team.py:44 #, python-format msgid "%s (copy)" msgstr "%s (copia)" @@ -202,7 +202,6 @@ msgstr "Orden de Entrega:" #. module: rma #: model_terms:ir.ui.view,arch_db:rma.portal_rma_page #, fuzzy -#| msgid "Origin delivery:" msgid "Origin delivery" msgstr "Orden de Entrega:" @@ -325,7 +324,7 @@ msgstr "Modelo con seudónimo" #. module: rma #: model_terms:ir.ui.view,arch_db:rma.rma_view_form msgid "Are you sure you want to cancel this RMA" -msgstr "" +msgstr "¿Está seguro de querer cancelar este RMA?" #. module: rma #: model:ir.model.fields,field_description:rma.field_rma__message_attachment_count @@ -538,7 +537,7 @@ msgid "Draft RMA" msgstr "RMA en estado Borrador" #. module: rma -#: code:addons/rma/models/rma.py:1076 +#: code:addons/rma/models/rma.py:1091 #, python-format msgid "" "E-mail subject: %s\n" @@ -641,7 +640,7 @@ msgstr "" "sin eliminarlo." #. module: rma -#: code:addons/rma/models/rma.py:1080 +#: code:addons/rma/models/rma.py:1095 #, python-format msgid "Incoming e-mail" msgstr "Correo electrónico entrante" @@ -747,8 +746,8 @@ msgid "Name" msgstr "Nombre" #. module: rma -#: code:addons/rma/models/rma.py:31 code:addons/rma/models/rma.py:496 -#: code:addons/rma/models/rma.py:1079 +#: code:addons/rma/models/rma.py:31 code:addons/rma/models/rma.py:495 +#: code:addons/rma/models/rma.py:1094 #, python-format msgid "New" msgstr "Nuevo" @@ -774,13 +773,13 @@ msgid "Next Activity Type" msgstr "Siguiente tipo de actividad" #. module: rma -#: code:addons/rma/models/rma.py:757 +#: code:addons/rma/models/rma.py:772 #, python-format msgid "None of the selected RMAs can perform a replacement." msgstr "Ninguno de los RMAs seleccionados puede realizar un reemplazo." #. module: rma -#: code:addons/rma/models/rma.py:740 +#: code:addons/rma/models/rma.py:755 #, python-format msgid "None of the selected RMAs can perform a return." msgstr "Ninguno de los RMAs seleccionados puede realizar una devolución." @@ -961,7 +960,7 @@ msgid "Quantity to extract" msgstr "Cantidad a extraer" #. module: rma -#: code:addons/rma/models/rma.py:790 +#: code:addons/rma/models/rma.py:805 #, python-format msgid "" "Quantity to extract cannot be greater than remaining delivery quantity (%s " @@ -1087,7 +1086,7 @@ msgstr "" #. module: rma #: model:ir.model,name:rma.model_rma_operation msgid "RMA requested operation" -msgstr "" +msgstr "Operación de RMA solicitada" #. module: rma #: model:ir.model.fields,field_description:rma.field_stock_move__rma_id @@ -1133,7 +1132,7 @@ msgid "Reference of the document that generated this RMA." msgstr "Referencia al documento que generó este RMA." #. module: rma -#: code:addons/rma/models/rma.py:679 +#: code:addons/rma/models/rma.py:693 #: model:ir.model.fields,field_description:rma.field_rma__refund_id #: model_terms:ir.ui.view,arch_db:rma.rma_view_form #: model:rma.operation,name:rma.rma_operation_refund @@ -1169,7 +1168,7 @@ msgstr "Ctd. entregada restante por realizar" #. module: rma #: model:rma.operation,name:rma.rma_operation_return msgid "Repair" -msgstr "" +msgstr "Reparar" #. module: rma #: model_terms:ir.ui.view,arch_db:rma.rma_view_form @@ -1189,7 +1188,7 @@ msgid "Replaced" msgstr "Reemplazado" #. module: rma -#: code:addons/rma/models/rma.py:995 +#: code:addons/rma/models/rma.py:1010 #, python-format msgid "" "Replacement: Move %s) ha sido creado." #. module: rma -#: code:addons/rma/models/rma.py:1006 +#: code:addons/rma/models/rma.py:1021 #, python-format msgid "" "Replacement:
Product %s%s has been " @@ -1352,7 +1354,7 @@ msgstr "" #: model:ir.model.fields,field_description:rma.field_rma__state #: model_terms:ir.ui.view,arch_db:rma.rma_view_search msgid "State" -msgstr "Provincia" +msgstr "Estado" #. module: rma #: code:addons/rma/controllers/main.py:40 @@ -1398,7 +1400,7 @@ msgstr "Miembros del equipo" #. module: rma #: sql_constraint:rma.operation:0 msgid "That operation name already exists !" -msgstr "" +msgstr "¡El nombre de operación ya existe!" #. module: rma #: model:ir.model.fields,help:rma.field_rma_team__alias_model_id @@ -1446,7 +1448,7 @@ msgstr "" "inicial porque el movimiento está enlazado a un RMA (%s)." #. module: rma -#: code:addons/rma/models/rma.py:778 +#: code:addons/rma/models/rma.py:793 #, python-format msgid "The quantity to return is greater than remaining quantity." msgstr "La cantidad a devolver es mayor que la cantidad restante del RMA." @@ -1462,19 +1464,19 @@ msgstr "" "cantidad especificada en el RMA asociado." #. module: rma -#: code:addons/rma/models/rma.py:767 +#: code:addons/rma/models/rma.py:782 #, python-format msgid "This RMA cannot be split." msgstr "Este RMA no puede ser dividido." #. module: rma -#: code:addons/rma/models/rma.py:754 +#: code:addons/rma/models/rma.py:769 #, python-format msgid "This RMA cannot perform a replacement." msgstr "Este RMA no puede realizar un reemplazo." #. module: rma -#: code:addons/rma/models/rma.py:737 +#: code:addons/rma/models/rma.py:752 #, python-format msgid "This RMA cannot perform a return." msgstr "Este RMA no puede realizar una devolución." @@ -1598,7 +1600,7 @@ msgid "Website communication history" msgstr "Historial de comunicaciones del sitio web" #. module: rma -#: code:addons/rma/models/rma.py:514 +#: code:addons/rma/models/rma.py:516 #, python-format msgid "You cannot delete RMAs that are not in draft state" msgstr "No puede " diff --git a/rma/i18n/pt_BR.po b/rma/i18n/pt_BR.po index 34cfc5f76..a64ce4790 100644 --- a/rma/i18n/pt_BR.po +++ b/rma/i18n/pt_BR.po @@ -1,6 +1,6 @@ # Translation of Odoo Server. # This file contains the translation of the following modules: -# * rma +# * rma # msgid "" msgstr "" @@ -27,21 +27,23 @@ msgid "${object.company_id.name} RMA (Ref ${object.name or 'n/a' })" msgstr "${object.company_id.name} RMA (Ref ${object.name or 'n/a' })" #. module: rma -#: code:addons/rma/models/rma_team.py:43 +#: code:addons/rma/models/rma_team.py:44 #, python-format msgid "%s (copy)" msgstr "%s (Cópia)" #. module: rma #: model:mail.template,body_html:rma.mail_template_rma_notification -msgid "
\n" +msgid "" +"
\n" "

\n" " Dear ${object.partner_id.name}\n" " % if object.partner_id.parent_id:\n" " (${object.partner_id.parent_id.name})\n" " % endif\n" "

\n" -" Here is the RMA ${object.name} from ${object.company_id.name}.\n" +" Here is the RMA ${object.name} from ${object.company_id." +"name}.\n" "

\n" " Do not hesitate to contact us if you have any question.\n" "

\n" @@ -55,8 +57,8 @@ msgstr "" " (${object.partner_id.parent_id.name})\n" " % endif\n" "

\n" -" Aqui está o RMA ${object.name} from " -"${object.company_id.name}.\n" +" Aqui está o RMA ${object.name} from ${object.company_id." +"name}.\n" "

\n" " Não deixe de nos contactar se houver qualquer dúvida ou outra questão.\n" "

\n" @@ -65,10 +67,12 @@ msgstr "" #. module: rma #: model_terms:ir.ui.view,arch_db:rma.portal_rma_page -msgid "" +msgid "" +"" msgstr "" -"" +"" #. module: rma #: model_terms:ir.ui.view,arch_db:rma.portal_rma_page @@ -82,42 +86,54 @@ msgstr " Aguardando Pagamento" #. module: rma #: model_terms:ir.ui.view,arch_db:rma.portal_rma_page -msgid "" +msgid "" +"" msgstr "" "" #. module: rma #: model_terms:ir.ui.view,arch_db:rma.portal_rma_page -msgid "" +msgid "" +"" msgstr "" -"" +"" #. module: rma #: model_terms:ir.ui.view,arch_db:rma.portal_rma_page -msgid " Cancelled" +msgid "" +" Cancelled" msgstr "" " Cancelado" #. module: rma #: model_terms:ir.ui.view,arch_db:rma.portal_rma_page -msgid " Preparation" +msgid "" +" Preparation" msgstr "" " Preparo" #. module: rma #: model_terms:ir.ui.view,arch_db:rma.portal_rma_page -msgid " Shipped" +msgid "" +" Shipped" msgstr "" " Embarcado" #. module: rma #: model_terms:ir.ui.view,arch_db:rma.portal_rma_page -msgid " Partially Available" +msgid "" +" Partially Available" msgstr "" " Parcialmente Disponível" @@ -240,7 +256,9 @@ msgstr "Situação:" #. module: rma #: model:ir.model.fields,help:rma.field_rma_team__alias_defaults -msgid "A Python dictionary that will be evaluated to provide default values when creating new records for this alias." +msgid "" +"A Python dictionary that will be evaluated to provide default values when " +"creating new records for this alias." msgstr "" "Um dicionário Python que será avaliado para fornecer valores padrão ao criar " "novos registros para esse alias." @@ -392,8 +410,7 @@ msgid "Confirm" msgstr "Confirmar" #. module: rma -#: model_terms:ir.ui.view,arch_db:rma.rma_view_search -#: selection:rma,state:0 +#: model_terms:ir.ui.view,arch_db:rma.rma_view_search selection:rma,state:0 msgid "Confirmed" msgstr "Confirmado(a)" @@ -507,8 +524,7 @@ msgid "Display Name" msgstr "Exibir Nome" #. module: rma -#: model_terms:ir.ui.view,arch_db:rma.rma_view_search -#: selection:rma,state:0 +#: model_terms:ir.ui.view,arch_db:rma.rma_view_search selection:rma,state:0 msgid "Draft" msgstr "Rascunho" @@ -519,9 +535,10 @@ msgid "Draft RMA" msgstr "Rascunho do RMA" #. module: rma -#: code:addons/rma/models/rma.py:1076 +#: code:addons/rma/models/rma.py:1091 #, python-format -msgid "E-mail subject: %s\n" +msgid "" +"E-mail subject: %s\n" "\n" "E-mail body:\n" "%s" @@ -586,7 +603,9 @@ msgstr "ID" #. module: rma #: model:ir.model.fields,help:rma.field_rma_team__alias_parent_thread_id -msgid "ID of the parent record holding the alias (example: project holding the task creation alias)" +msgid "" +"ID of the parent record holding the alias (example: project holding the task " +"creation alias)" msgstr "" "ID do registro pai que contém o alias (exemplo: projeto que contém o alias " "de criação da tarefa)" @@ -611,13 +630,15 @@ msgstr "Se marcada, algumas mensagens apresentam um erro de entrega." #. module: rma #: model:ir.model.fields,help:rma.field_rma_team__active -msgid "If the active field is set to false, it will allow you to hide the RMA Team without removing it." +msgid "" +"If the active field is set to false, it will allow you to hide the RMA Team " +"without removing it." msgstr "" "Se o campo ativo estiver definido como falso, permitirá ocultar a equipe RMA " "sem removê-lo." #. module: rma -#: code:addons/rma/models/rma.py:1080 +#: code:addons/rma/models/rma.py:1095 #, python-format msgid "Incoming e-mail" msgstr "E-mail recebido" @@ -723,9 +744,8 @@ msgid "Name" msgstr "Nome" #. module: rma -#: code:addons/rma/models/rma.py:31 -#: code:addons/rma/models/rma.py:496 -#: code:addons/rma/models/rma.py:1079 +#: code:addons/rma/models/rma.py:31 code:addons/rma/models/rma.py:495 +#: code:addons/rma/models/rma.py:1094 #, python-format msgid "New" msgstr "Novo(a)" @@ -751,13 +771,13 @@ msgid "Next Activity Type" msgstr "Tipo da Próxima Atividade" #. module: rma -#: code:addons/rma/models/rma.py:757 +#: code:addons/rma/models/rma.py:772 #, python-format msgid "None of the selected RMAs can perform a replacement." msgstr "Nenhum dos RMAs selecionados pode executar uma substituição." #. module: rma -#: code:addons/rma/models/rma.py:740 +#: code:addons/rma/models/rma.py:755 #, python-format msgid "None of the selected RMAs can perform a return." msgstr "Nenhum dos RMAs selecionados pode executar uma devolução." @@ -804,7 +824,10 @@ msgstr "Número de Mensagens Não Lidas" #. module: rma #: model:ir.model.fields,help:rma.field_rma_team__alias_force_thread_id -msgid "Optional ID of a thread (record) to which all incoming messages will be attached, even if they did not reply to it. If set, this will disable the creation of new records completely." +msgid "" +"Optional ID of a thread (record) to which all incoming messages will be " +"attached, even if they did not reply to it. If set, this will disable the " +"creation of new records completely." msgstr "" "ID opcional de um encadeamento (registro) ao qual todas as mensagens " "recebidas serão anexadas, mesmo que não tenham respondido. Se definido, isso " @@ -852,7 +875,10 @@ msgstr "ID do Encadeamento do Registro Pai" #. module: rma #: model:ir.model.fields,help:rma.field_rma_team__alias_parent_model_id -msgid "Parent model holding the alias. The model holding the alias reference is not necessarily the model given by alias_model_id (example: project (parent_model) and task (model))" +msgid "" +"Parent model holding the alias. The model holding the alias reference is not " +"necessarily the model given by alias_model_id (example: project " +"(parent_model) and task (model))" msgstr "" "Modelo pai que contém o alias. O modelo que contém a referência de alias não " "é necessariamente o modelo fornecido pelo alias_model_id (exemplo: projeto " @@ -870,11 +896,12 @@ msgstr "Planejado)a)" #. module: rma #: model:ir.model.fields,help:rma.field_rma_team__alias_contact -msgid "Policy to post a message on the document using the mailgateway.\n" +msgid "" +"Policy to post a message on the document using the mailgateway.\n" "- everyone: everyone can post\n" "- partners: only authenticated partners\n" -"- followers: only followers of the related document or members of following channels\n" -"" +"- followers: only followers of the related document or members of following " +"channels\n" msgstr "" "Política para postar uma mensagem no documento usando o mail gateway.\n" "- todos: todos podem postar\n" @@ -920,8 +947,7 @@ msgid "Quantity" msgstr "Quantidade" #. module: rma -#: code:addons/rma/wizard/rma_delivery.py:49 -#: sql_constraint:rma.split.wizard:0 +#: code:addons/rma/wizard/rma_delivery.py:49 sql_constraint:rma.split.wizard:0 #, python-format msgid "Quantity must be greater than 0." msgstr "Quantidade precisa ser maior que zero." @@ -932,9 +958,11 @@ msgid "Quantity to extract" msgstr "Quantidade a extrair" #. module: rma -#: code:addons/rma/models/rma.py:790 +#: code:addons/rma/models/rma.py:805 #, python-format -msgid "Quantity to extract cannot be greater than remaining delivery quantity (%s %s)" +msgid "" +"Quantity to extract cannot be greater than remaining delivery quantity (%s " +"%s)" msgstr "" "A quantidade a extrair não pode ser maior que a quantidade de entrega " "restante (%s %s)" @@ -1082,8 +1110,7 @@ msgid "Receipt" msgstr "Recebimento" #. module: rma -#: model_terms:ir.ui.view,arch_db:rma.rma_view_search -#: selection:rma,state:0 +#: model_terms:ir.ui.view,arch_db:rma.rma_view_search selection:rma,state:0 msgid "Received" msgstr "Recebido" @@ -1103,7 +1130,7 @@ msgid "Reference of the document that generated this RMA." msgstr "Referência do documento que gerou este RMA." #. module: rma -#: code:addons/rma/models/rma.py:679 +#: code:addons/rma/models/rma.py:693 #: model:ir.model.fields,field_description:rma.field_rma__refund_id #: model_terms:ir.ui.view,arch_db:rma.rma_view_form #: model:rma.operation,name:rma.rma_operation_refund @@ -1159,18 +1186,25 @@ msgid "Replaced" msgstr "Substituído" #. module: rma -#: code:addons/rma/models/rma.py:995 +#: code:addons/rma/models/rma.py:1010 #, python-format -msgid "Replacement: Move %s (Picking %s) has been created." +msgid "" +"Replacement: Move %s (Picking %s) has been created." msgstr "" "Substituição: Movimentação %s (A Coleta %s foi criada." #. module: rma -#: code:addons/rma/models/rma.py:1006 +#: code:addons/rma/models/rma.py:1021 #, python-format -msgid "Replacement:
Product %s
Quantity %f %s
This replacement did not create a new move, but one of the previously created moves was updated with this data." +msgid "" +"Replacement:
Product %s
Quantity %f %s
This replacement did not " +"create a new move, but one of the previously created moves was updated with " +"this data." msgstr "" "Substituição:
Produto %s
Quantidade %f%s
Essa substituição não " @@ -1188,7 +1222,7 @@ msgid "Requested operation" msgstr "Operação Solicitada" #. module: rma -#: code:addons/rma/models/rma.py:721 +#: code:addons/rma/models/rma.py:736 #, python-format msgid "Required field(s):%s" msgstr "Campo(s) Solicitado(s):%s" @@ -1217,12 +1251,14 @@ msgid "Return to customer" msgstr "Retornar ao cliente" #. module: rma -#: code:addons/rma/models/rma.py:956 +#: code:addons/rma/models/rma.py:971 #, python-format -msgid "Return: %s has been created." +msgid "" +"Return: %s has been created." msgstr "" -"Retorno: %s foi criado." +"Retorno: %s foi criado." #. module: rma #: selection:rma,state:0 @@ -1259,6 +1295,11 @@ msgstr "Enviado(a) por Correio" msgid "Sent" msgstr "Enviado(a)" +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma_team__sequence +msgid "Sequence" +msgstr "" + #. module: rma #: code:addons/rma/models/stock_warehouse.py:48 #, python-format @@ -1298,9 +1339,11 @@ msgid "Split RMA" msgstr "Dividir RMA" #. module: rma -#: code:addons/rma/models/rma.py:874 +#: code:addons/rma/models/rma.py:889 #, python-format -msgid "Split: %s has been created." +msgid "" +"Split: %s has been " +"created." msgstr "" "Divisão: %s foi " "criada." @@ -1320,7 +1363,8 @@ msgstr "Status" #. module: rma #: model:ir.model.fields,help:rma.field_rma__activity_state -msgid "Status based on activities\n" +msgid "" +"Status based on activities\n" "Overdue: Due date is already passed\n" "Today: Activity date is today\n" "Planned: Future activities." @@ -1358,7 +1402,10 @@ msgstr "Esse nome de operação já existe!" #. module: rma #: model:ir.model.fields,help:rma.field_rma_team__alias_model_id -msgid "The model (Odoo Document Kind) to which this alias corresponds. Any incoming email that does not reply to an existing record will cause the creation of a new record of this model (e.g. a Project Task)" +msgid "" +"The model (Odoo Document Kind) to which this alias corresponds. Any incoming " +"email that does not reply to an existing record will cause the creation of a " +"new record of this model (e.g. a Project Task)" msgstr "" "Modelo (tipo de documento Odoo) ao qual esse alias corresponde. Qualquer e-" "mail recebido que não responda a um registro existente causará a criação de " @@ -1366,14 +1413,20 @@ msgstr "" #. module: rma #: model:ir.model.fields,help:rma.field_rma_team__alias_name -msgid "The name of the email alias, e.g. 'jobs' if you want to catch emails for " +msgid "" +"The name of the email alias, e.g. 'jobs' if you want to catch emails for " +"" msgstr "" "O nome do alias do email, por exemplo 'jobs' se você deseja receber e-mails " "de " #. module: rma #: model:ir.model.fields,help:rma.field_rma_team__alias_user_id -msgid "The owner of records created upon receiving emails on this alias. If this field is not set the system will attempt to find the right owner based on the sender (From) address, or will use the Administrator account if no system user is found for that address." +msgid "" +"The owner of records created upon receiving emails on this alias. If this " +"field is not set the system will attempt to find the right owner based on " +"the sender (From) address, or will use the Administrator account if no " +"system user is found for that address." msgstr "" "O proprietário dos registros criados ao receber emails nesse alias. Se esse " "campo não estiver definido, o sistema tentará encontrar o proprietário certo " @@ -1383,13 +1436,15 @@ msgstr "" #. module: rma #: code:addons/rma/models/stock_move.py:60 #, python-format -msgid "The quantity done for the product '%s' must be equal to its initial demand because the stock move is linked to an RMA (%s)." +msgid "" +"The quantity done for the product '%s' must be equal to its initial demand " +"because the stock move is linked to an RMA (%s)." msgstr "" "A quantidade realizada para o produto '%s' deve ser igual à sua demanda " "inicial, porque a movimentação do estoque está vinculada a uma RMA (%s)." #. module: rma -#: code:addons/rma/models/rma.py:778 +#: code:addons/rma/models/rma.py:793 #, python-format msgid "The quantity to return is greater than remaining quantity." msgstr "A quantidade a retornar é maior que a quantidade restante." @@ -1397,25 +1452,27 @@ msgstr "A quantidade a retornar é maior que a quantidade restante." #. module: rma #: code:addons/rma/models/account_invoice.py:22 #, python-format -msgid "There is at least one invoice lines whose quantity is less than the quantity specified in its linked RMA." +msgid "" +"There is at least one invoice lines whose quantity is less than the quantity " +"specified in its linked RMA." msgstr "" "Há pelo menos uma linha de fatura cuja quantidade é menor que a quantidade " "especificada na sua RMA vinculada." #. module: rma -#: code:addons/rma/models/rma.py:767 +#: code:addons/rma/models/rma.py:782 #, python-format msgid "This RMA cannot be split." msgstr "Esta RMA não pode ser dividida." #. module: rma -#: code:addons/rma/models/rma.py:754 +#: code:addons/rma/models/rma.py:769 #, python-format msgid "This RMA cannot perform a replacement." msgstr "Esta RMA não pode executar uma substituição." #. module: rma -#: code:addons/rma/models/rma.py:737 +#: code:addons/rma/models/rma.py:752 #, python-format msgid "This RMA cannot perform a return." msgstr "Este RMA não pode executar uma devolução." @@ -1539,7 +1596,7 @@ msgid "Website communication history" msgstr "Histórico de Comunicação Através do Site" #. module: rma -#: code:addons/rma/models/rma.py:514 +#: code:addons/rma/models/rma.py:516 #, python-format msgid "You cannot delete RMAs that are not in draft state" msgstr "Você não pode apagar RMAs que não estejam na situação \"Rascunho\"" @@ -1547,14 +1604,17 @@ msgstr "Você não pode apagar RMAs que não estejam na situação \"Rascunho\"" #. module: rma #: code:addons/rma/wizard/stock_picking_return.py:56 #, python-format -msgid "You must specify the 'Customer' in the 'Stock Picking' from which RMAs will be created" +msgid "" +"You must specify the 'Customer' in the 'Stock Picking' from which RMAs will " +"be created" msgstr "" "Você deve especificar o 'Cliente' na 'Seleção de estoque' a partir do qual " "as RMAs serão criadas" #. module: rma #: model:res.groups,comment:rma.rma_group_user_all -msgid "the user will have access to all records of everyone in the RMA application." +msgid "" +"the user will have access to all records of everyone in the RMA application." msgstr "o usuário terá acesso a todos os registros de todos na aplicação RMA." #. module: rma @@ -1564,7 +1624,9 @@ msgstr "o usuário terá acesso aos seus próprios dados na aplicação RMA." #. module: rma #: model:res.groups,comment:rma.rma_group_manager -msgid "the user will have an access to the RMA configuration as well as statistic reports." +msgid "" +"the user will have an access to the RMA configuration as well as statistic " +"reports." msgstr "" "o usuário terá acesso à configuração RMA, bem como aos relatórios " "estatísticos." diff --git a/rma/i18n/rma.pot b/rma/i18n/rma.pot index 7b327b2b5..6eac770ea 100644 --- a/rma/i18n/rma.pot +++ b/rma/i18n/rma.pot @@ -24,7 +24,7 @@ msgid "${object.company_id.name} RMA (Ref ${object.name or 'n/a' })" msgstr "" #. module: rma -#: code:addons/rma/models/rma_team.py:43 +#: code:addons/rma/models/rma_team.py:44 #, python-format msgid "%s (copy)" msgstr "" @@ -486,7 +486,7 @@ msgid "Draft RMA" msgstr "" #. module: rma -#: code:addons/rma/models/rma.py:1076 +#: code:addons/rma/models/rma.py:1091 #, python-format msgid "E-mail subject: %s\n" "\n" @@ -576,7 +576,7 @@ msgid "If the active field is set to false, it will allow you to hide the RMA Te msgstr "" #. module: rma -#: code:addons/rma/models/rma.py:1080 +#: code:addons/rma/models/rma.py:1095 #, python-format msgid "Incoming e-mail" msgstr "" @@ -683,8 +683,8 @@ msgstr "" #. module: rma #: code:addons/rma/models/rma.py:31 -#: code:addons/rma/models/rma.py:496 -#: code:addons/rma/models/rma.py:1079 +#: code:addons/rma/models/rma.py:495 +#: code:addons/rma/models/rma.py:1094 #, python-format msgid "New" msgstr "" @@ -710,13 +710,13 @@ msgid "Next Activity Type" msgstr "" #. module: rma -#: code:addons/rma/models/rma.py:757 +#: code:addons/rma/models/rma.py:772 #, python-format msgid "None of the selected RMAs can perform a replacement." msgstr "" #. module: rma -#: code:addons/rma/models/rma.py:740 +#: code:addons/rma/models/rma.py:755 #, python-format msgid "None of the selected RMAs can perform a return." msgstr "" @@ -880,7 +880,7 @@ msgid "Quantity to extract" msgstr "" #. module: rma -#: code:addons/rma/models/rma.py:790 +#: code:addons/rma/models/rma.py:805 #, python-format msgid "Quantity to extract cannot be greater than remaining delivery quantity (%s %s)" msgstr "" @@ -1049,7 +1049,7 @@ msgid "Reference of the document that generated this RMA." msgstr "" #. module: rma -#: code:addons/rma/models/rma.py:679 +#: code:addons/rma/models/rma.py:693 #: model:ir.model.fields,field_description:rma.field_rma__refund_id #: model_terms:ir.ui.view,arch_db:rma.rma_view_form #: model:rma.operation,name:rma.rma_operation_refund @@ -1105,13 +1105,13 @@ msgid "Replaced" msgstr "" #. module: rma -#: code:addons/rma/models/rma.py:995 +#: code:addons/rma/models/rma.py:1010 #, python-format msgid "Replacement: Move %s (Picking %s) has been created." msgstr "" #. module: rma -#: code:addons/rma/models/rma.py:1006 +#: code:addons/rma/models/rma.py:1021 #, python-format msgid "Replacement:
Product %s
Quantity %f %s
This replacement did not create a new move, but one of the previously created moves was updated with this data." msgstr "" @@ -1127,7 +1127,7 @@ msgid "Requested operation" msgstr "" #. module: rma -#: code:addons/rma/models/rma.py:721 +#: code:addons/rma/models/rma.py:736 #, python-format msgid "Required field(s):%s" msgstr "" @@ -1156,7 +1156,7 @@ msgid "Return to customer" msgstr "" #. module: rma -#: code:addons/rma/models/rma.py:956 +#: code:addons/rma/models/rma.py:971 #, python-format msgid "Return: %s has been created." msgstr "" @@ -1196,6 +1196,11 @@ msgstr "" msgid "Sent" msgstr "" +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma_team__sequence +msgid "Sequence" +msgstr "" + #. module: rma #: code:addons/rma/models/stock_warehouse.py:48 #, python-format @@ -1235,7 +1240,7 @@ msgid "Split RMA" msgstr "" #. module: rma -#: code:addons/rma/models/rma.py:874 +#: code:addons/rma/models/rma.py:889 #, python-format msgid "Split: %s has been created." msgstr "" @@ -1309,7 +1314,7 @@ msgid "The quantity done for the product '%s' must be equal to its initial deman msgstr "" #. module: rma -#: code:addons/rma/models/rma.py:778 +#: code:addons/rma/models/rma.py:793 #, python-format msgid "The quantity to return is greater than remaining quantity." msgstr "" @@ -1321,19 +1326,19 @@ msgid "There is at least one invoice lines whose quantity is less than the quant msgstr "" #. module: rma -#: code:addons/rma/models/rma.py:767 +#: code:addons/rma/models/rma.py:782 #, python-format msgid "This RMA cannot be split." msgstr "" #. module: rma -#: code:addons/rma/models/rma.py:754 +#: code:addons/rma/models/rma.py:769 #, python-format msgid "This RMA cannot perform a replacement." msgstr "" #. module: rma -#: code:addons/rma/models/rma.py:737 +#: code:addons/rma/models/rma.py:752 #, python-format msgid "This RMA cannot perform a return." msgstr "" @@ -1457,7 +1462,7 @@ msgid "Website communication history" msgstr "" #. module: rma -#: code:addons/rma/models/rma.py:514 +#: code:addons/rma/models/rma.py:516 #, python-format msgid "You cannot delete RMAs that are not in draft state" msgstr "" diff --git a/rma/models/rma.py b/rma/models/rma.py index 8367c5b36..8a9ddcd85 100644 --- a/rma/models/rma.py +++ b/rma/models/rma.py @@ -55,7 +55,6 @@ def _domain_location_id(self): comodel_name="res.users", string="Responsible", track_visibility="always", - default=lambda self: self.env.user, states={ 'locked': [('readonly', True)], 'cancelled': [('readonly', True)], @@ -499,6 +498,9 @@ def create(self, vals): ir_sequence = ir_sequence.with_context( force_company=vals['company_id']) vals['name'] = ir_sequence.next_by_code('rma') + # Assign a default team_id which will be the first in the sequence + if "team_id" not in vals: + vals["team_id"] = self.env["rma.team"].search([], limit=1).id return super().create(vals) def copy(self, default=None): @@ -599,7 +601,10 @@ def action_replace(self): """Invoked when 'Replace' button in rma form view is clicked.""" self.ensure_one() self._ensure_can_be_replaced() - action = self.env.ref("rma.rma_delivery_wizard_action").read()[0] + # Force active_id to avoid issues when coming from smart buttons + # in other models + action = self.env.ref("rma.rma_delivery_wizard_action").with_context( + active_id=self.id).read()[0] action['name'] = 'Replace product(s)' action['context'] = dict(self.env.context) action['context'].update( @@ -615,7 +620,10 @@ def action_return(self): """ self.ensure_one() self._ensure_can_be_returned() - action = self.env.ref("rma.rma_delivery_wizard_action").read()[0] + # Force active_id to avoid issues when coming from smart buttons + # in other models + action = self.env.ref("rma.rma_delivery_wizard_action").with_context( + active_id=self.id).read()[0] action['context'] = dict(self.env.context) action['context'].update( active_id=self.id, @@ -628,9 +636,12 @@ def action_split(self): """Invoked when 'Split' button in rma form view is clicked.""" self.ensure_one() self._ensure_can_be_split() - action = self.env.ref("rma.rma_split_wizard_action").read()[0] + # Force active_id to avoid issues when coming from smart buttons + # in other models + action = self.env.ref("rma.rma_split_wizard_action").with_context( + active_id=self.id).read()[0] action['context'] = dict(self.env.context) - action['context'].update(active_ids=self.ids) + action['context'].update(active_id=self.id, active_ids=self.ids) return action def action_cancel(self): @@ -663,7 +674,10 @@ def action_preview(self): def action_view_receipt(self): """Invoked when 'Receipt' smart button in rma form view is clicked.""" self.ensure_one() - action = self.env.ref('stock.action_picking_tree_all').read()[0] + # Force active_id to avoid issues when coming from smart buttons + # in other models + action = self.env.ref('stock.action_picking_tree_all').with_context( + active_id=self.id).read()[0] action.update( res_id=self.reception_move_id.picking_id.id, view_mode="form", @@ -687,7 +701,8 @@ def action_view_refund(self): def action_view_delivery(self): """Invoked when 'Delivery' smart button in rma form view is clicked.""" - action = self.env.ref('stock.action_picking_tree_all').read()[0] + action = self.env.ref('stock.action_picking_tree_all').with_context( + active_id=self.id).read()[0] picking = self.delivery_move_ids.mapped('picking_id') if len(picking) > 1: action['domain'] = [('id', 'in', picking.ids)] diff --git a/rma/models/rma_team.py b/rma/models/rma_team.py index 31b600972..018fa8820 100644 --- a/rma/models/rma_team.py +++ b/rma/models/rma_team.py @@ -8,8 +8,9 @@ class RmaTeam(models.Model): _name = "rma.team" _inherit = ['mail.alias.mixin', 'mail.thread'] _description = "RMA Team" - _order = "name" + _order = "sequence, name" + sequence = fields.Integer() name = fields.Char( required=True, translate=True, diff --git a/rma/readme/USAGE.rst b/rma/readme/USAGE.rst index 6e6194a94..aa524e7a0 100644 --- a/rma/readme/USAGE.rst +++ b/rma/readme/USAGE.rst @@ -33,5 +33,20 @@ An RMA can also be created from a return of a delivery order: Every RMA will be in confirmed state and they will be linked to the returning operation generated previously. -**Note: An RMA can also be created from an incoming email (See configuration -section).** +There are Optional RMA Teams that can be used for: + + - Organize RMAs in sections. + - Subscribe users to notifications. + - Create RMAs from incoming mail to special aliases (See configuration + section). + +To create an RMA Team (RMA Responsible user level required): + + #. Go to *RMA > Configuration > RMA Teams* + #. Create a new team and assign a name, a responsible and members. + #. Subscribe users to notifications, that can be of these subtypes: + + - RMA draft. When a new RMA is created. + - Notes, Debates, Activities. As in standard Odoo. + #. In the list view, use the cross handle to sort RMA Teams. The top team + will be the default one if no team is set. diff --git a/rma/static/description/index.html b/rma/static/description/index.html index a45186e63..88523ce37 100644 --- a/rma/static/description/index.html +++ b/rma/static/description/index.html @@ -440,8 +440,29 @@

Usage

Every RMA will be in confirmed state and they will be linked to the returning operation generated previously. -

Note: An RMA can also be created from an incoming email (See configuration -section).

+

There are Optional RMA Teams that can be used for:

+
+
    +
  • Organize RMAs in sections.
  • +
  • Subscribe users to notifications.
  • +
  • Create RMAs from incoming mail to special aliases (See configuration +section).
  • +
+
+

To create an RMA Team (RMA Responsible user level required):

+
+
    +
  1. Go to RMA > Configuration > RMA Teams
  2. +
  3. Create a new team and assign a name, a responsible and members.
  4. +
  5. Subscribe users to notifications, that can be of these subtypes:
      +
    • RMA draft. When a new RMA is created.
    • +
    • Notes, Debates, Activities. As in standard Odoo.
    • +
    +
  6. +
  7. In the list view, use the cross handle to sort RMA Teams. The top team +will be the default one if no team is set.
  8. +
+

Known issues / Roadmap

diff --git a/rma/views/res_partner_views.xml b/rma/views/res_partner_views.xml index d783f1f69..bdf58616d 100644 --- a/rma/views/res_partner_views.xml +++ b/rma/views/res_partner_views.xml @@ -6,6 +6,7 @@ res.partner.form res.partner +
-
+ +
Product
- +
@@ -262,6 +265,9 @@

Communication

+ + +
diff --git a/rma/views/rma_team_views.xml b/rma/views/rma_team_views.xml index 328e1ee6c..9202da2dd 100644 --- a/rma/views/rma_team_views.xml +++ b/rma/views/rma_team_views.xml @@ -2,6 +2,17 @@ + + rma.team + + + + + + + + + rma.team.view.form rma.team diff --git a/rma/views/stock_picking_views.xml b/rma/views/stock_picking_views.xml index d1f754001..95734fb08 100644 --- a/rma/views/stock_picking_views.xml +++ b/rma/views/stock_picking_views.xml @@ -6,6 +6,7 @@ stock.picking.form stock.picking +