diff --git a/rma/README.rst b/rma/README.rst new file mode 100644 index 00000000..abdfda0a --- /dev/null +++ b/rma/README.rst @@ -0,0 +1,167 @@ +=========================================== +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-Production%2FStable-green.png + :target: https://odoo-community.org/page/development-status + :alt: Production/Stable +.. |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/13.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-13-0/rma-13-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/13.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. + +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 +====================== + +* 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 00000000..25d6cd09 --- /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 00000000..ecc7ce28 --- /dev/null +++ b/rma/__manifest__.py @@ -0,0 +1,35 @@ +# 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": "13.0.1.0.0", + "development_status": "Production/Stable", + "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", + "data/stock_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 00000000..f43232f0 --- /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 00000000..a262f83a --- /dev/null +++ b/rma/controllers/main.py @@ -0,0 +1,138 @@ +# 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.tools import consteq + +from odoo.addons.portal.controllers.portal import CustomerPortal, pager as portal_pager + + +class PortalRma(CustomerPortal): + def _prepare_portal_layout_values(self): + values = super()._prepare_portal_layout_values() + if request.env["rma"].check_access_rights("read", raise_exception=False): + values["rma_count"] = request.env["rma"].search_count([]) + else: + values["rma_count"] = 0 + 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, rma_id, picking_id, access_token=None, **kw): + try: + picking_sudo = self._picking_check_access( + rma_id, 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 _picking_check_access(self, rma_id, picking_id, access_token=None): + rma = request.env["rma"].browse([rma_id]) + 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(rma.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 00000000..3a0c32c2 --- /dev/null +++ b/rma/data/mail_data.xml @@ -0,0 +1,48 @@ + + + + + Draft RMA + rma + + 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 00000000..a194b87d --- /dev/null +++ b/rma/data/rma_operation_data.xml @@ -0,0 +1,12 @@ + + + + Replace + + + Repair + + + Refund + + diff --git a/rma/data/stock_data.xml b/rma/data/stock_data.xml new file mode 100644 index 00000000..8ee0cdda --- /dev/null +++ b/rma/data/stock_data.xml @@ -0,0 +1,9 @@ + + + + RMA + + view + + + diff --git a/rma/hooks.py b/rma/hooks.py new file mode 100644 index 00000000..dbdbf75a --- /dev/null +++ b/rma/hooks.py @@ -0,0 +1,74 @@ +# Copyright 2020 Tecnativa - Ernesto Tejeda +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import SUPERUSER_ID, api + + +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"] + if not warehouse.rma_loc_id: + rma_location_vals = warehouse._get_rma_location_values() + warehouse.rma_loc_id = ( + stock_location.with_context(active_test=False) + .create(rma_location_vals) + .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 00000000..15af6738 --- /dev/null +++ b/rma/i18n/es.po @@ -0,0 +1,1722 @@ +# 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-10-23 20:11+0000\n" +"PO-Revision-Date: 2020-10-23 16:21-0400\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: nplurals=2; plural=n != 1;\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 "${(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:0 +#, python-format +msgid "%s (copy)" +msgstr "%s (copia)" + +#. module: rma +#: code:addons/rma/models/rma.py:0 +#, python-format +msgid "E-mail subject: %s

E-mail body:
%s" +msgstr "" +"Asunto del correo electrónico: %s

Cuerpo del correo " +"electrónico:
%s" + +#. module: rma +#: model:mail.template,body_html:rma.mail_template_rma_notification +#, fuzzy +#| 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" +#| " " +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 +#, fuzzy +#| msgid " Paid" +msgid "" +"\n" +" Paid" +msgstr "Pagado" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.portal_rma_page +#, fuzzy +#| msgid " Waiting Payment" +msgid "" +"\n" +" 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 +msgid "Origin delivery" +msgstr "Entrega origen" + +#. 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_exception_decoration +msgid "Activity Exception Decoration" +msgstr "" + +#. 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 "¿Está seguro de querer 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 "Conteo de archivos adjuntos" + +#. 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 "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 +#: model:ir.model.fields.selection,name:rma.selection__rma__state__cancelled +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:ir.model.fields.selection,name:rma.selection__rma__state__confirmed +#: model_terms:ir.ui.view,arch_db:rma.rma_view_search +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:0 +#: 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:ir.model.fields.selection,name:rma.selection__rma__state__draft +#: model_terms:ir.ui.view,arch_db:rma.rma_view_search +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 +#: 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:0 +#, 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 "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 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,field_description:rma.field_rma__activity_exception_icon +msgid "Icon" +msgstr "" + +#. module: rma +#: model:ir.model.fields,help:rma.field_rma__activity_exception_icon +msgid "Icon to indicate an exception activity." +msgstr "" + +#. module: rma +#: model:ir.model.fields,help:rma.field_rma__message_needaction +#: model:ir.model.fields,help:rma.field_rma__message_unread +#: model:ir.model.fields,help:rma.field_rma_team__message_needaction +#: 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_has_error +#: model:ir.model.fields,help:rma.field_rma__message_has_sms_error +#: model:ir.model.fields,help:rma.field_rma_team__message_has_error +#: model:ir.model.fields,help:rma.field_rma_team__message_has_sms_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:0 +#, python-format +msgid "Incoming e-mail" +msgstr "Correo electrónico entrante" + +#. 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.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,name:rma.model_account_move +msgid "Journal Entries" +msgstr "" + +#. module: rma +#: model:ir.model,name:rma.model_account_move_line +msgid "Journal Item" +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 "Ú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_terms:ir.ui.view,arch_db:rma.rma_view_search +msgid "Late RMAs" +msgstr "RMAs retrasados" + +#. 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 +#: model:ir.model.fields.selection,name:rma.selection__rma__state__locked +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:0 +#: 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:0 +#, python-format +msgid "New" +msgstr "Nuevo" + +#. 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:0 +#, 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:0 +#, 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 +#: model:ir.model.fields.selection,name:rma.selection__rma__priority__1 +msgid "Normal" +msgstr "Normal" + +#. module: rma +#: model:ir.model.fields.selection,name: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 errors" +msgstr "Número de errores" + +#. 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 +#: 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 +#: 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:0 +#: model:ir.model.constraint,message:rma.constraint_rma_split_wizard_check_product_uom_qty_positive +#, 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:0 +#, 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_move_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 nº" + +#. module: rma +#: code:addons/rma/models/res_company.py:0 +#, python-format +msgid "RMA Code" +msgstr "Código de RMA" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.rma_view_search +msgid "RMA Date" +msgstr "Fecha de RMA" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.rma_view_search +msgid "RMA Deadline" +msgstr "RMA fecha límite" + +#. module: rma +#: code:addons/rma/models/stock_warehouse.py:0 +#, 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:0 +#, 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:mail.message.subtype,description:rma.mt_rma_draft +msgid "RMA in draft state" +msgstr "RMA en estado borrador" + +#. 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 "" +"Productos relacionados con el RMA pueden ser guardados en este almacén." + +#. module: rma +#: model:ir.model,name:rma.model_rma_operation +msgid "RMA requested operation" +msgstr "Operación de RMA solicitada" + +#. 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 "RMAs" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.rma_view_search +msgid "RMAs which deadline has passed" +msgstr "RMAs pasados de fecha límite" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.rma_view_search +msgid "RMAs yet to be fully processed" +msgstr "RMAs pendientes de ser procesados por completo" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.rma_view_form +msgid "Receipt" +msgstr "Recepción" + +#. module: rma +#: model:ir.model.fields.selection,name:rma.selection__rma__state__received +#: model_terms:ir.ui.view,arch_db:rma.rma_view_search +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:0 +#: 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 +#: model:ir.model.fields.selection,name:rma.selection__rma__state__refunded +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 "Reparar" + +#. module: rma +#: model:ir.model.fields.selection,name:rma.selection__rma_delivery_wizard__type__replace +#: model_terms:ir.ui.view,arch_db:rma.rma_view_form +#: 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 +#: model:ir.model.fields.selection,name:rma.selection__rma__state__replaced +msgid "Replaced" +msgstr "Reemplazado" + +#. module: rma +#: code:addons/rma/models/rma.py:0 +#, 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:0 +#, 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 +msgid "Requested operation" +msgstr "Operación solicitada" + +#. module: rma +#: code:addons/rma/models/rma.py:0 +#, 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:ir.model.fields.selection,name:rma.selection__rma_delivery_wizard__type__return +#: model_terms:ir.ui.view,arch_db:rma.rma_view_form +msgid "Return to customer" +msgstr "Devolver al cliente" + +#. module: rma +#: code:addons/rma/models/rma.py:0 +#, python-format +msgid "" +"Return: %s has been created." +msgstr "" +"Devolución: La orden de entrega %s ha sido creada." + +#. module: rma +#: model:ir.model.fields.selection,name:rma.selection__rma__state__returned +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__message_has_sms_error +#: model:ir.model.fields,field_description:rma.field_rma_team__message_has_sms_error +#, fuzzy +#| msgid "Message Delivery error" +msgid "SMS Delivery error" +msgstr "Error de Envío de Mensaje" + +#. 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 +#: model:ir.model.fields,field_description:rma.field_rma_team__sequence +msgid "Sequence" +msgstr "Secuencia" + +#. module: rma +#: code:addons/rma/models/stock_warehouse.py:0 +#, python-format +msgid "Sequence RMA in" +msgstr "Secuencia de recepción de RMA" + +#. module: rma +#: code:addons/rma/models/stock_warehouse.py:0 +#, 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:0 +#, 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 "Estado" + +#. module: rma +#: code:addons/rma/controllers/main.py:0 +#: 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 +#: model:ir.model.constraint,message:rma.constraint_rma_operation_name_uniq +msgid "That operation name already exists !" +msgstr "¡El nombre de operación ya 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 "" +"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:0 +#, 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:0 +#, 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_move.py:0 +#, 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:0 +#, python-format +msgid "This RMA cannot be split." +msgstr "Este RMA no puede ser dividido." + +#. module: rma +#: code:addons/rma/models/rma.py:0 +#, 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:0 +#, 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 +#: 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:ir.model.fields,help:rma.field_rma__activity_exception_decoration +msgid "Type of the exception activity on record." +msgstr "" + +#. 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_terms:ir.ui.view,arch_db:rma.rma_view_search +msgid "Unresolved RMAs" +msgstr "RMAs sin resolver" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__product_uom +msgid "UoM" +msgstr "UdM" + +#. module: rma +#: model:ir.model.fields.selection,name:rma.selection__rma__priority__2 +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 +#: model:ir.model.fields.selection,name:rma.selection__rma__priority__3 +msgid "Very Urgent" +msgstr "Muy Urgente" + +#. module: rma +#: model:ir.model.fields.selection,name:rma.selection__rma__state__waiting_replacement +msgid "Waiting for replacement" +msgstr "Esperando por reemplazo" + +#. module: rma +#: model:ir.model.fields.selection,name:rma.selection__rma__state__waiting_return +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:0 +#, python-format +msgid "You cannot delete RMAs that are not in draft state" +msgstr "No puede eliminar RMAs que no estén en estado borrador" + +#. module: rma +#: code:addons/rma/wizard/stock_picking_return.py:0 +#, 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:ir.actions.report,print_report_name:rma.report_rma_action +msgid "object._get_report_base_filename()" +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 "" +"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." + +#~ msgid "If checked new messages require your attention." +#~ msgstr "Si está marcado, hay nuevos mensajes que requieren su atención." + +#~ msgid "Invoice" +#~ msgstr "Factura" + +#~ msgid "Invoice Line" +#~ msgstr "Linea de factura" + +#~ msgid "Overdue" +#~ msgstr "Vencidas" + +#~ msgid "Planned" +#~ msgstr "Planeado" + +#~ msgid "Today" +#~ msgstr "Hoy" + +#~ msgid "Waiting for refund" +#~ msgstr "Esperando por reembolso" + +#~ msgid "New RMA in draft state" +#~ msgstr "Nuevo RMA en estado Borrador" diff --git a/rma/i18n/pt_BR.po b/rma/i18n/pt_BR.po new file mode 100644 index 00000000..eb48b82d --- /dev/null +++ b/rma/i18n/pt_BR.po @@ -0,0 +1,1663 @@ +# 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:46 +#, 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:1098 +#, 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:1102 +#, 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_terms:ir.ui.view,arch_db:rma.rma_view_search +msgid "Late RMAs" +msgstr "" + +#. 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:494 +#: code:addons/rma/models/rma.py:1101 +#, python-format +msgid "New" +msgstr "Novo(a)" + +#. 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: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:755 +#, 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:805 +#, 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 +#: model_terms:ir.ui.view,arch_db:rma.rma_view_search +msgid "RMA Date" +msgstr "" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.rma_view_search +msgid "RMA Deadline" +msgstr "" + +#. 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:mail.message.subtype,description:rma.mt_rma_draft +msgid "RMA in draft state" +msgstr "" + +#. 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_search +msgid "RMAs which deadline has passed" +msgstr "" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.rma_view_search +msgid "RMAs yet to be fully processed" +msgstr "" + +#. 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: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 +#, 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:1011 +#, 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:1022 +#, 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:736 +#, 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:972 +#, 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 +#: 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 +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:889 +#, 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:64 +#, 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:793 +#, 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: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: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:752 +#, 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_terms:ir.ui.view,arch_db:rma.rma_view_search +msgid "Unresolved RMAs" +msgstr "" + +#. 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 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: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\"" + +#. module: rma +#: code:addons/rma/wizard/stock_picking_return.py:55 +#, 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." + +#~ msgid "Waiting for refund" +#~ msgstr "Aguardando Restituição" + +#~ msgid "New RMA in draft state" +#~ msgstr "Novo Rascunho de RMA" diff --git a/rma/i18n/rma.pot b/rma/i18n/rma.pot new file mode 100644 index 00000000..0a936faa --- /dev/null +++ b/rma/i18n/rma.pot @@ -0,0 +1,1587 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * rma +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 13.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2020-10-23 20:11+0000\n" +"PO-Revision-Date: 2020-10-23 20:11+0000\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:0 +#, python-format +msgid "%s (copy)" +msgstr "" + +#. module: rma +#: code:addons/rma/models/rma.py:0 +#, python-format +msgid "E-mail subject: %s

E-mail body:
%s" +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 "" +"\n" +" Paid" +msgstr "" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.portal_rma_page +msgid "" +"\n" +" 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_exception_decoration +msgid "Activity Exception Decoration" +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 +#: model:ir.model.fields.selection,name:rma.selection__rma__state__cancelled +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:ir.model.fields.selection,name:rma.selection__rma__state__confirmed +#: model_terms:ir.ui.view,arch_db:rma.rma_view_search +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:0 +#: 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:ir.model.fields.selection,name:rma.selection__rma__state__draft +#: model_terms:ir.ui.view,arch_db:rma.rma_view_search +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 +#: 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:0 +#, 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,field_description:rma.field_rma__activity_exception_icon +msgid "Icon" +msgstr "" + +#. module: rma +#: model:ir.model.fields,help:rma.field_rma__activity_exception_icon +msgid "Icon to indicate an exception activity." +msgstr "" + +#. module: rma +#: model:ir.model.fields,help:rma.field_rma__message_needaction +#: model:ir.model.fields,help:rma.field_rma__message_unread +#: model:ir.model.fields,help:rma.field_rma_team__message_needaction +#: 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_has_error +#: model:ir.model.fields,help:rma.field_rma__message_has_sms_error +#: model:ir.model.fields,help:rma.field_rma_team__message_has_error +#: model:ir.model.fields,help:rma.field_rma_team__message_has_sms_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:0 +#, python-format +msgid "Incoming e-mail" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__partner_invoice_id +msgid "Invoice Address" +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,name:rma.model_account_move +msgid "Journal Entries" +msgstr "" + +#. module: rma +#: model:ir.model,name:rma.model_account_move_line +msgid "Journal Item" +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_terms:ir.ui.view,arch_db:rma.rma_view_search +msgid "Late RMAs" +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 +#: model:ir.model.fields.selection,name:rma.selection__rma__state__locked +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:0 +#: 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:0 code:addons/rma/models/rma.py:0 +#: code:addons/rma/models/rma.py:0 +#, python-format +msgid "New" +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:0 +#, python-format +msgid "None of the selected RMAs can perform a replacement." +msgstr "" + +#. module: rma +#: code:addons/rma/models/rma.py:0 +#, python-format +msgid "None of the selected RMAs can perform a return." +msgstr "" + +#. module: rma +#: model:ir.model.fields.selection,name:rma.selection__rma__priority__1 +msgid "Normal" +msgstr "" + +#. module: rma +#: model:ir.model.fields.selection,name: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 errors" +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 +#: 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 +#: 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:0 +#: model:ir.model.constraint,message:rma.constraint_rma_split_wizard_check_product_uom_qty_positive +#, 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:0 +#, 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_move_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:0 +#, python-format +msgid "RMA Code" +msgstr "" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.rma_view_search +msgid "RMA Date" +msgstr "" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.rma_view_search +msgid "RMA Deadline" +msgstr "" + +#. module: rma +#: code:addons/rma/models/stock_warehouse.py:0 +#, 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:0 +#, 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:mail.message.subtype,description:rma.mt_rma_draft +msgid "RMA in draft state" +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_search +msgid "RMAs which deadline has passed" +msgstr "" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.rma_view_search +msgid "RMAs yet to be fully processed" +msgstr "" + +#. module: rma +#: model_terms:ir.ui.view,arch_db:rma.rma_view_form +msgid "Receipt" +msgstr "" + +#. module: rma +#: model:ir.model.fields.selection,name:rma.selection__rma__state__received +#: model_terms:ir.ui.view,arch_db:rma.rma_view_search +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:0 +#: 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 +#: model:ir.model.fields.selection,name:rma.selection__rma__state__refunded +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:ir.model.fields.selection,name:rma.selection__rma_delivery_wizard__type__replace +#: model_terms:ir.ui.view,arch_db:rma.rma_view_form +#: 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 +#: model:ir.model.fields.selection,name:rma.selection__rma__state__replaced +msgid "Replaced" +msgstr "" + +#. module: rma +#: code:addons/rma/models/rma.py:0 +#, python-format +msgid "" +"Replacement: Move %s (Picking %s) has been created." +msgstr "" + +#. module: rma +#: code:addons/rma/models/rma.py:0 +#, 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:0 +#, 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:ir.model.fields.selection,name:rma.selection__rma_delivery_wizard__type__return +#: model_terms:ir.ui.view,arch_db:rma.rma_view_form +msgid "Return to customer" +msgstr "" + +#. module: rma +#: code:addons/rma/models/rma.py:0 +#, python-format +msgid "" +"Return: %s has been created." +msgstr "" + +#. module: rma +#: model:ir.model.fields.selection,name:rma.selection__rma__state__returned +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__message_has_sms_error +#: model:ir.model.fields,field_description:rma.field_rma_team__message_has_sms_error +msgid "SMS Delivery error" +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 +#: model:ir.model.fields,field_description:rma.field_rma_team__sequence +msgid "Sequence" +msgstr "" + +#. module: rma +#: code:addons/rma/models/stock_warehouse.py:0 +#, python-format +msgid "Sequence RMA in" +msgstr "" + +#. module: rma +#: code:addons/rma/models/stock_warehouse.py:0 +#, 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:0 +#, 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:0 +#: 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 +#: model:ir.model.constraint,message:rma.constraint_rma_operation_name_uniq +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:0 +#, 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:0 +#, python-format +msgid "The quantity to return is greater than remaining quantity." +msgstr "" + +#. module: rma +#: code:addons/rma/models/account_move.py:0 +#, 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:0 +#, python-format +msgid "This RMA cannot be split." +msgstr "" + +#. module: rma +#: code:addons/rma/models/rma.py:0 +#, python-format +msgid "This RMA cannot perform a replacement." +msgstr "" + +#. module: rma +#: code:addons/rma/models/rma.py:0 +#, 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 +#: 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:ir.model.fields,help:rma.field_rma__activity_exception_decoration +msgid "Type of the exception activity on record." +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_terms:ir.ui.view,arch_db:rma.rma_view_search +msgid "Unresolved RMAs" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma__product_uom +msgid "UoM" +msgstr "" + +#. module: rma +#: model:ir.model.fields.selection,name:rma.selection__rma__priority__2 +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 +#: model:ir.model.fields.selection,name:rma.selection__rma__priority__3 +msgid "Very Urgent" +msgstr "" + +#. module: rma +#: model:ir.model.fields.selection,name:rma.selection__rma__state__waiting_replacement +msgid "Waiting for replacement" +msgstr "" + +#. module: rma +#: model:ir.model.fields.selection,name:rma.selection__rma__state__waiting_return +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:0 +#, python-format +msgid "You cannot delete RMAs that are not in draft state" +msgstr "" + +#. module: rma +#: code:addons/rma/wizard/stock_picking_return.py:0 +#, python-format +msgid "" +"You must specify the 'Customer' in the 'Stock Picking' from which RMAs will " +"be created" +msgstr "" + +#. module: rma +#: model:ir.actions.report,print_report_name:rma.report_rma_action +msgid "object._get_report_base_filename()" +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 00000000..f3c5b7de --- /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_move +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_move.py b/rma/models/account_move.py new file mode 100644 index 00000000..3bd81e6c --- /dev/null +++ b/rma/models/account_move.py @@ -0,0 +1,47 @@ +# 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 AccountMove(models.Model): + _inherit = "account.move" + + def post(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.sudo() + .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." + ) + ) + return super().post() + + def unlink(self): + rma = self.mapped("invoice_line_ids.rma_id") + rma.write({"state": "received"}) + return super().unlink() + + +class AccountMoveLine(models.Model): + _inherit = "account.move.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 00000000..a7129391 --- /dev/null +++ b/rma/models/res_company.py @@ -0,0 +1,29 @@ +# 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): + return ( + 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 00000000..45ff8f1b --- /dev/null +++ b/rma/models/res_partner.py @@ -0,0 +1,33 @@ +# 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 = {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 00000000..47ebd213 --- /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 00000000..78b19acb --- /dev/null +++ b/rma/models/rma.py @@ -0,0 +1,1174 @@ +# Copyright 2020 Tecnativa - Ernesto Tejeda +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from collections import Counter + +from odoo import _, api, fields, models +from odoo.exceptions import ValidationError +from odoo.tests import Form +from odoo.tools import html2plaintext + +from odoo.addons.stock.models.stock_move import PROCUREMENT_PRIORITIES + + +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", + index=True, + default=lambda self: self.env.user, + tracking=True, + 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.company, + 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, + tracking=True, + ) + partner_invoice_id = fields.Many2one( + string="Invoice Address", + comodel_name="res.partner", + readonly=True, + states={"draft": [("readonly", False)]}, + domain="['|', ('company_id', '=', False), ('company_id', '=', company_id)]", + 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="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_return", "Waiting for return"), + ("waiting_replacement", "Waiting for replacement"), + ("refunded", "Refunded"), + ("returned", "Returned"), + ("replaced", "Replaced"), + ("locked", "Locked"), + ("cancelled", "Canceled"), + ], + default="draft", + copy=False, + tracking=True, + ) + description = fields.Html( + 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.move", string="Refund", readonly=True, copy=False, + ) + refund_line_id = fields.Many2one( + comodel_name="account.move.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="Product Unit of Measure", + compute="_compute_delivered_qty", + store=True, + ) + delivered_qty_done = fields.Float( + string="Delivered qty done", + digits="Product Unit of Measure", + compute="_compute_delivered_qty", + compute_sudo=True, + ) + 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="Product Unit of Measure", + compute="_compute_remaining_qty", + ) + remaining_qty_to_done = fields.Float( + string="Remaining delivered qty to done", + digits="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_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.company + 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_create_multi + def create(self, vals_list): + for vals in vals_list: + 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") + # 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_list) + + 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.move"].with_context( + default_type="out_refund", company_id=rmas[0].company_id.id, + ), + "account.view_move_form", + ) + rmas[0]._prepare_refund(invoice_form, origin) + refund = invoice_form.save() + for rma in rmas: + # For each iteration the Form is edited, a new invoice line + # is added and then saved. This is to generate the other + # lines of the accounting entry and to specify the associated + # RMA to that new invoice line. + invoice_form = Form(refund) + with invoice_form.invoice_line_ids.new() as line_form: + rma._prepare_refund_line(line_form) + refund = invoice_form.save() + line = refund.invoice_line_ids.filtered(lambda r: not r.rma_id) + line.rma_id = rma.id + rma.write( + { + "refund_line_id": line.id, + "refund_id": refund.id, + "state": "refunded", + } + ) + refund.invoice_origin = origin + 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() + # 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( + 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() + # 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, 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() + # 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_id=self.id, 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() + # 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", + 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.move", + "views": [(self.env.ref("account.view_move_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") + .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)] + 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.update( + location_id=self.location_id.id, picking_id=self.picking_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._onchange_picking_id() + 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 = "{} ({})".format(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.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.partner_id = self.partner_invoice_id + + 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.product_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 = {} + rmas_to_return = self.filtered("can_be_returned") + for record in rmas_to_return: + 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)], + company_id=picking.company_id.id, + ) + self.env["stock.move"].sudo().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, + ) + rmas_to_return.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.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 + ) + procurement = self.env["procurement.group"].Procurement( + product, + qty, + uom, + self.partner_id.property_stock_customer, + self.product_id.display_name, + self.procurement_group_id.name, + self.company_id, + values, + ) + self.env["procurement.group"].run([procurement]) + return True + + def _prepare_procurement_values( + self, group_id, scheduled_date, warehouse, + ): + self.ensure_one() + return { + "company_id": self.company_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 _creation_subtype(self): + if self.state in ("draft", "confirmed"): + return self.env.ref("rma.mt_rma_draft") + else: + return super()._creation_subtype() + + 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

E-mail body:
%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 00000000..616dc55e --- /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 00000000..4bb2e7d7 --- /dev/null +++ b/rma/models/rma_team.py @@ -0,0 +1,55 @@ +# 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 = "sequence, name" + + sequence = fields.Integer() + 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.company, + ) + 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): + self.ensure_one() + 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 00000000..a1b702c8 --- /dev/null +++ b/rma/models/stock_move.py @@ -0,0 +1,124 @@ +# 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): + # A stock user could have no RMA permissions, so the ids wouldn't + # be accessible due to record rules. + rma_receiver = self.sudo().mapped("rma_receiver_ids") + rma = self.sudo().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() + # A stock user could have no RMA permissions, so the ids wouldn't + # be accessible due to record rules. + cancelled_moves = self.filtered(lambda r: r.state == "cancel").sudo() + 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, cancel_backorder=False): + """ 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.sudo().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").sudo() + # Set RMAs as received. We sudo so we can grant the operation even + # if the stock user has no RMA permissions. + to_be_received = ( + move_done.sudo() + .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.sudo().rma_id.id + return res + + def _prepare_return_rma_vals(self, original_picking): + """ hook method for preparing an RMA from the 'return picking wizard'. + """ + self.ensure_one() + partner = original_picking.partner_id + if hasattr(original_picking, "sale_id") and original_picking.sale_id: + partner_invoice_id = original_picking.sale_id.partner_invoice_id.id + else: + partner_invoice_id = ( + partner.address_get(["invoice"]).get("invoice", False), + ) + return { + "user_id": self.env.user.id, + "partner_id": partner.id, + "partner_invoice_id": partner_invoice_id, + "origin": original_picking.name, + "picking_id": original_picking.id, + "move_id": self.origin_returned_move_id.id, + "product_id": self.origin_returned_move_id.product_id.id, + "product_uom_qty": self.product_uom_qty, + "product_uom": self.product_uom.id, + "reception_move_id": self.id, + "company_id": self.company_id.id, + "location_id": self.location_dest_id.id, + "state": "confirmed", + } + + +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 00000000..e343ae3a --- /dev/null +++ b/rma/models/stock_picking.py @@ -0,0 +1,37 @@ +# 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): + self.ensure_one() + 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 00000000..0b1c3091 --- /dev/null +++ b/rma/models/stock_warehouse.py @@ -0,0 +1,135 @@ +# Copyright 2020 Tecnativa - Ernesto Tejeda +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import _, api, 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",) + + @api.model_create_multi + def create(self, vals_list): + """ To create an RMA location and link it with a new warehouse, + this method is overridden instead of '_get_locations_values' + method because the locations that are created with the + values ​​returned by that method are forced to be children + of view_location_id, and we don't want that. + """ + res = super().create(vals_list) + stock_location = self.env["stock.location"] + for record in res: + rma_location_vals = record._get_rma_location_values() + record.rma_loc_id = stock_location.create(rma_location_vals).id + return res + + def _get_rma_location_values(self): + """ this method is intended to be used by 'create' method + to create a new RMA location to be linked to a new warehouse. + """ + return { + "name": self.view_location_id.name, + "active": True, + "return_location": True, + "usage": "internal", + "company_id": self.company_id.id, + "location_id": self.env.ref("rma.stock_location_rma").id, + } + + 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_out_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": False, + "use_existing_lots": True, + "default_location_src_id": False, + "default_location_dest_id": self.rma_loc_id.id, + "sequence": max_sequence + 1, + "sequence_code": "RMA/IN", + "company_id": self.company_id.id, + }, + "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, + "sequence_code": "RMA/OUT", + "company_id": self.company_id.id, + }, + } + ) + 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 00000000..c5513046 --- /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 00000000..d4052271 --- /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 00000000..0c0c6fd8 --- /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 00000000..c6cb6029 --- /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 00000000..aa524e7a --- /dev/null +++ b/rma/readme/USAGE.rst @@ -0,0 +1,52 @@ +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. + +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/report/report.xml b/rma/report/report.xml new file mode 100644 index 00000000..5816ab29 --- /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 00000000..0afc4d55 --- /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 00000000..e7c42289 --- /dev/null +++ b/rma/security/rma_security.xml @@ -0,0 +1,86 @@ + + + + + + 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 portal users + + [('message_partner_ids', 'child_of', [user.partner_id.commercial_partner_id.id])] + + + + + RMA multi-company + + + ['|',('company_id','=',False),('company_id','in',company_ids)] + + + RMA team multi-company + + + ['|',('company_id','=',False),('company_id','in',company_ids)] + + + + + + diff --git a/rma/static/description/icon.png b/rma/static/description/icon.png new file mode 100644 index 00000000..2f8ea42a Binary files /dev/null and b/rma/static/description/icon.png differ diff --git a/rma/static/description/index.html b/rma/static/description/index.html new file mode 100644 index 00000000..f99fdcde --- /dev/null +++ b/rma/static/description/index.html @@ -0,0 +1,517 @@ + + + + + + +Return Merchandise Authorization Management + + + +
+

Return Merchandise Authorization Management

+ + +

Production/Stable 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. +
+

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

+
    +
  • 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 00000000..5f9ab818 --- /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 00000000..b917f269 --- /dev/null +++ b/rma/tests/test_rma.py @@ -0,0 +1,658 @@ +# Copyright 2020 Tecnativa - Ernesto Tejeda +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo.exceptions import UserError, ValidationError +from odoo.tests import Form, SavepointCase, tagged + + +@tagged("post_install", "-at_install") +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 + 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", "internal_group": "income"} + ) + 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.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.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.product_uom_id, rma.product_uom) + self.assertEqual(rma.state, "refunded") + self.assertFalse(rma.can_be_refunded) + self.assertFalse(rma.can_be_returned) + self.assertFalse(rma.can_be_replaced) + with Form(rma.refund_line_id.move_id) as refund_form: + with refund_form.invoice_line_ids.edit(0) as refund_line: + refund_line.quantity = 9 + with self.assertRaises(ValidationError): + rma.refund_id.post() + with Form(rma.refund_line_id.move_id) as refund_form: + with refund_form.invoice_line_ids.edit(0) as refund_line: + refund_line.quantity = 10 + rma.refund_id.post() + 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() + # After that all RMAs are in 'refunded' state + self.assertEqual(all_rmas.mapped("state"), ["refunded"] * 4) + # 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.product_uom_id) + # Less quantity -> error on confirm + with Form(rma_2.refund_line_id.move_id) as refund_form: + with refund_form.invoice_line_ids.edit(1) as refund_line: + refund_line.quantity = 14 + with self.assertRaises(ValidationError): + refund_1.post() + with Form(rma_2.refund_line_id.move_id) as refund_form: + with refund_form.invoice_line_ids.edit(1) as refund_line: + refund_line.quantity = 15 + refund_1.post() + refund_2.post() + + 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_id": origin_delivery.id}) + ) + return_wizard._onchange_picking_id() + 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) + + def test_rma_to_receive_on_delete_invoice(self): + rma = self._create_confirm_receive(self.partner, self.product, 10, self.rma_loc) + rma.action_refund() + self.assertEqual(rma.state, "refunded") + rma.refund_id.unlink() + self.assertFalse(rma.refund_id) + self.assertEqual(rma.state, "received") + self.assertTrue(rma.can_be_refunded) + self.assertTrue(rma.can_be_returned) + self.assertTrue(rma.can_be_replaced) + + def test_rma_picking_type_default_values(self): + warehouse = self.env["stock.warehouse"].create( + {"name": "Stock - RMA Test", "code": "SRT"} + ) + self.assertFalse(warehouse.rma_in_type_id.use_create_lots) + self.assertTrue(warehouse.rma_in_type_id.use_existing_lots) + + def test_quantities_on_hand(self): + rma = self._create_confirm_receive(self.partner, self.product, 10, self.rma_loc) + self.assertEqual(rma.product_id.qty_available, 0) diff --git a/rma/views/menus.xml b/rma/views/menus.xml new file mode 100644 index 00000000..e5119894 --- /dev/null +++ b/rma/views/menus.xml @@ -0,0 +1,19 @@ + + + + + + + + diff --git a/rma/views/report_rma.xml b/rma/views/report_rma.xml new file mode 100644 index 00000000..2442589d --- /dev/null +++ b/rma/views/report_rma.xml @@ -0,0 +1,121 @@ + + + + + diff --git a/rma/views/res_partner_views.xml b/rma/views/res_partner_views.xml new file mode 100644 index 00000000..45d9474c --- /dev/null +++ b/rma/views/res_partner_views.xml @@ -0,0 +1,24 @@ + + + + + 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 00000000..ef0ea47d --- /dev/null +++ b/rma/views/rma_portal_templates.xml @@ -0,0 +1,469 @@ + + + + + + + + diff --git a/rma/views/rma_team_views.xml b/rma/views/rma_team_views.xml new file mode 100644 index 00000000..5ca01b82 --- /dev/null +++ b/rma/views/rma_team_views.xml @@ -0,0 +1,157 @@ + + + + + rma.team + + + + + + + + + + + rma.team.view.form + rma.team + +
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+
+ Avatar +
+ + + +
+
+
+
+
+
+
+
+ + + + +
+
+
+ + +
+
+
+
+ + RMA team + rma.team + 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 00000000..f270b8da --- /dev/null +++ b/rma/views/rma_views.xml @@ -0,0 +1,366 @@ + + + + + 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 + tree,form,pivot,calendar,activity + {"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 00000000..4f1e8aad --- /dev/null +++ b/rma/views/stock_picking_views.xml @@ -0,0 +1,24 @@ + + + + + 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 00000000..059efa05 --- /dev/null +++ b/rma/views/stock_warehouse_views.xml @@ -0,0 +1,18 @@ + + + + Stock Warehouse Inherit MRP + stock.warehouse + + + + + + + + + + + + + diff --git a/rma/wizard/__init__.py b/rma/wizard/__init__.py new file mode 100644 index 00000000..b81a7402 --- /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 00000000..ae7c0f42 --- /dev/null +++ b/rma/wizard/rma_delivery.py @@ -0,0 +1,90 @@ +# 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 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="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 00000000..ae3ace93 --- /dev/null +++ b/rma/wizard/rma_delivery_views.xml @@ -0,0 +1,67 @@ + + + + + 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 00000000..1ac646a9 --- /dev/null +++ b/rma/wizard/rma_split.py @@ -0,0 +1,65 @@ +# Copyright 2020 Tecnativa - Ernesto Tejeda +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import _, api, fields, models + + +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="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 00000000..68807503 --- /dev/null +++ b/rma/wizard/rma_split_views.xml @@ -0,0 +1,37 @@ + + + + + rma.split.wizard.form + rma.split.wizard + +
+ + + + +
+
+
+
+
+ + Split RMA + rma.split.wizard + form + new + +
diff --git a/rma/wizard/stock_picking_return.py b/rma/wizard/stock_picking_return.py new file mode 100644 index 00000000..b337e73a --- /dev/null +++ b/rma/wizard/stock_picking_return.py @@ -0,0 +1,81 @@ +# 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( + [("company_id", "=", self.picking_id.company_id.id)] + ).mapped("rma_loc_id") + rma_loc_domain = [("id", "child_of", rma_loc.ids)] + else: + # If self.create_rma is not True, the value of the location and + # the location domain will be the same as assigned by default. + location_id = self.picking_id.location_id.id + return_picking_type = self.picking_id.picking_type_id.return_picking_type_id + if return_picking_type.default_location_dest_id.return_location: + location_id = return_picking_type.default_location_dest_id.id + self.location_id = location_id + rma_loc_domain = [ + "|", + ("id", "=", self.picking_id.location_id.id), + "|", + "&", + ("return_location", "=", True), + ("company_id", "=", False), + "&", + ("return_location", "=", True), + ("company_id", "=", self.picking_id.company_id.id), + ] + 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() + if not self.picking_id.partner_id: + raise ValidationError( + _( + "You must specify the 'Customer' in the " + "'Stock Picking' from which RMAs will be created" + ) + ) + returned_picking = self.env["stock.picking"].browse(res["res_id"]) + vals_list = [ + move._prepare_return_rma_vals(self.picking_id) + for move in returned_picking.move_lines + ] + self.env["rma"].create(vals_list) + 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 00000000..d899ee3f --- /dev/null +++ b/rma/wizard/stock_picking_return_views.xml @@ -0,0 +1,25 @@ + + + + + Return lines inherit RMA + stock.return.picking + + + + + + + + + + + + diff --git a/setup/.setuptools-odoo-make-default-ignore b/setup/.setuptools-odoo-make-default-ignore new file mode 100644 index 00000000..207e6153 --- /dev/null +++ b/setup/.setuptools-odoo-make-default-ignore @@ -0,0 +1,2 @@ +# addons listed in this file are ignored by +# setuptools-odoo-make-default (one addon per line) diff --git a/setup/README b/setup/README new file mode 100644 index 00000000..a63d633e --- /dev/null +++ b/setup/README @@ -0,0 +1,2 @@ +To learn more about this directory, please visit +https://pypi.python.org/pypi/setuptools-odoo diff --git a/setup/rma/odoo/addons/rma b/setup/rma/odoo/addons/rma new file mode 120000 index 00000000..8475ad50 --- /dev/null +++ b/setup/rma/odoo/addons/rma @@ -0,0 +1 @@ +../../../../rma \ No newline at end of file diff --git a/setup/rma/setup.py b/setup/rma/setup.py new file mode 100644 index 00000000..28c57bb6 --- /dev/null +++ b/setup/rma/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +)