diff --git a/pos_order_return/README.rst b/pos_order_return/README.rst new file mode 100644 index 00000000..597163ff --- /dev/null +++ b/pos_order_return/README.rst @@ -0,0 +1,138 @@ +========================== +Point of Sale Order Return +========================== + +.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png + :target: https://odoo-community.org/page/development-status + :alt: Beta +.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fpos-lightgray.png?logo=github + :target: https://github.com/OCA/pos/tree/12.0/pos_order_return + :alt: OCA/pos +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/pos-12-0/pos-12-0-pos_order_return + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png + :target: https://runbot.odoo-community.org/runbot/184/12.0 + :alt: Try me on Runbot + +|badge1| |badge2| |badge3| |badge4| |badge5| + +This module extends the functionality of odoo Point Of Sale about POS Order +returns. + +With this module, it is now forbidden to return more quantity than the initial +one. + +A link is created between the returned Order and the initial Order. +A link is created between the returned Order Line and the initial Order Line. + +**Table of contents** + +.. contents:: + :local: + +Configuration +============= + +In some cases, you may want to let the possibility to allow negative quantity +in a PoS Order, without mentioning initial order. This can happen for special +products like returnable products, etc. + +In that case, a checkbox is possible on Product Form View to allow such case + +.. image:: https://raw.githubusercontent.com/pos_order_return/static/description/product_returnable_bottle.png + +.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas + :alt: Try me on Runbot + :target: https://runbot.odoo-community.org/runbot/184/10.0 + +Usage +===== + +Select an PoS Order an choose either *Return Products* (full return of the +order) or *Partial Return*. In this case, a wizard allows to select just some +products and quantities to return: + +.. image:: https://raw.githubusercontent.com/pos_order_return/static/description/partial_return_wizard.png + +Register the refund payment to finish the return. If the original order was +invoiced, a refund invoice will be made. + +**Implemented Constraints** + +* User can not return more products than the initial quantity: + +.. image:: https://raw.githubusercontent.com/pos_order_return/static/description/returned_qty_over_initial.png + +* If a line has been partially refund, only a reduced quantity can be returned: + +.. image:: https://raw.githubusercontent.com/pos_order_return/static/description/sum_returned_qty_over_initial.png + +* It is not possible to set a negative quantity if the initial Pos Order is + not indicated: + +.. image:: https://raw.githubusercontent.com/pos_order_return/static/description/initial_pos_order_required.png + +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 +~~~~~~~ + +* La Louve +* GRAP +* Tecnativa +* Lambda IS + +Contributors +~~~~~~~~~~~~ + +* Sylvain LE GAL +* David Vidal +* Kiril Vangelovski + +Other credits +~~~~~~~~~~~~~ + +Funders +~~~~~~~ + +The development of this module has been financially supported by: + +* La Louve (www.lalouve.net) +* GRAP, Groupement Régional Alimentaire de Proximité (www.grap.coop) + +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. + +This module is part of the `OCA/pos `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/pos_order_return/__init__.py b/pos_order_return/__init__.py new file mode 100644 index 00000000..0217b0f9 --- /dev/null +++ b/pos_order_return/__init__.py @@ -0,0 +1,3 @@ + +from . import models +from . import wizard diff --git a/pos_order_return/__manifest__.py b/pos_order_return/__manifest__.py new file mode 100644 index 00000000..fb210589 --- /dev/null +++ b/pos_order_return/__manifest__.py @@ -0,0 +1,29 @@ +# Copyright 2016-2018 Sylvain LE GAL (https://twitter.com/legalsylvain) +# Copyright 2018 David Vidal +# Copyright 2018 Lambda IS DOOEL +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +{ + 'name': 'Point of Sale Order Return', + 'version': '12.0.1.0.0', + 'category': 'Point Of Sale', + 'author': 'La Louve, ' + 'GRAP, ' + 'Tecnativa, ' + 'Lambda IS, ' + 'Odoo Community Association (OCA)', + 'license': 'AGPL-3', + 'website': 'https://www.github.com/OCA/pos', + 'depends': [ + 'point_of_sale', + ], + 'data': [ + 'wizard/pos_partial_return_wizard_view.xml', + 'views/pos_order_view.xml', + 'views/product_product_view.xml', + ], + 'demo': [ + 'demo/product_product.xml', + ], + 'installable': True, +} diff --git a/pos_order_return/demo/product_product.xml b/pos_order_return/demo/product_product.xml new file mode 100644 index 00000000..44f7a4a2 --- /dev/null +++ b/pos_order_return/demo/product_product.xml @@ -0,0 +1,16 @@ + + + + + Returnable Bottle + RET-BOTL + + + + + + + + + + diff --git a/pos_order_return/i18n/es.po b/pos_order_return/i18n/es.po new file mode 100644 index 00000000..8c286744 --- /dev/null +++ b/pos_order_return/i18n/es.po @@ -0,0 +1,230 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * pos_order_return +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 10.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2018-05-03 12:50+0000\n" +"PO-Revision-Date: 2019-03-01 15:53+0000\n" +"Last-Translator: Marta Vázquez Rodríguez \n" +"Language-Team: \n" +"Language: es\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.4\n" + +#. module: pos_order_return +#: model:ir.model.fields,field_description:pos_order_return.field_product_product_pos_allow_negative_qty +#: model:ir.model.fields,field_description:pos_order_return.field_product_template_pos_allow_negative_qty +msgid "Allow Negative Quantity on PoS" +msgstr "Permitir Cantidades Negativas en PoS" + +#. module: pos_order_return +#: model:ir.ui.view,arch_db:pos_order_return.view_partial_return_wizard_form +msgid "Cancel" +msgstr "Cancelar" + +#. module: pos_order_return +#: model:ir.model.fields,help:pos_order_return.field_pos_partial_return_wizard_line_max_returnable_qty +msgid "" +"Compute maximum quantity that can be returned for this line, depending of " +"the quantity of the line and other possible refunds." +msgstr "" +"Calcula la cantidad máxima que puede ser devuelta para esta línea, " +"dependiendo de la cantidad de la línea y otras devoluciones anteriores." + +#. module: pos_order_return +#: model:ir.ui.view,arch_db:pos_order_return.view_partial_return_wizard_form +msgid "Confirm" +msgstr "Confirmar" + +#. module: pos_order_return +#: model:ir.model.fields,field_description:pos_order_return.field_pos_partial_return_wizard_create_uid +#: model:ir.model.fields,field_description:pos_order_return.field_pos_partial_return_wizard_line_create_uid +msgid "Created by" +msgstr "Creado por" + +#. module: pos_order_return +#: model:ir.model.fields,field_description:pos_order_return.field_pos_partial_return_wizard_create_date +#: model:ir.model.fields,field_description:pos_order_return.field_pos_partial_return_wizard_line_create_date +msgid "Created on" +msgstr "Creado el" + +#. module: pos_order_return +#: model:ir.model.fields,field_description:pos_order_return.field_pos_partial_return_wizard_display_name +#: model:ir.model.fields,field_description:pos_order_return.field_pos_partial_return_wizard_line_display_name +msgid "Display Name" +msgstr "Nombre a mostrar" + +#. module: pos_order_return +#: code:addons/pos_order_return/models/pos_order.py:187 +#, python-format +msgid "" +"For legal and traceability reasons, you can not set a negative quantity (%d " +"%s of %s), without using return wizard." +msgstr "" +"Por razones legales y de trazabilidad, no puede establecer una cantidad " +"negativa (%d %s of %s), sin en el asistente de devoluciones." + +#. module: pos_order_return +#: model:ir.model.fields,field_description:pos_order_return.field_pos_partial_return_wizard_id +#: model:ir.model.fields,field_description:pos_order_return.field_pos_partial_return_wizard_line_id +msgid "ID" +msgstr "ID" + +#. module: pos_order_return +#: model:ir.model.fields,field_description:pos_order_return.field_pos_partial_return_wizard_line_initial_qty +msgid "Initial Quantity" +msgstr "Cantidad inicial" + +#. module: pos_order_return +#: model:ir.model.fields,field_description:pos_order_return.field_pos_partial_return_wizard___last_update +#: model:ir.model.fields,field_description:pos_order_return.field_pos_partial_return_wizard_line___last_update +msgid "Last Modified on" +msgstr "Última modificación en" + +#. module: pos_order_return +#: model:ir.model.fields,field_description:pos_order_return.field_pos_partial_return_wizard_line_write_uid +#: model:ir.model.fields,field_description:pos_order_return.field_pos_partial_return_wizard_write_uid +msgid "Last Updated by" +msgstr "Última actualización por" + +#. module: pos_order_return +#: model:ir.model.fields,field_description:pos_order_return.field_pos_partial_return_wizard_line_write_date +#: model:ir.model.fields,field_description:pos_order_return.field_pos_partial_return_wizard_write_date +msgid "Last Updated on" +msgstr "Última actualización el" + +#. module: pos_order_return +#: model:ir.model.fields,field_description:pos_order_return.field_pos_partial_return_wizard_line_pos_order_line_id +msgid "Line To Return" +msgstr "Línea a devolver" + +#. module: pos_order_return +#: model:ir.model,name:pos_order_return.model_pos_order_line +msgid "Lines of Point of Sale Orders" +msgstr "Líneas del Terminal Punto de Venta" + +#. module: pos_order_return +#: model:ir.model.fields,field_description:pos_order_return.field_pos_partial_return_wizard_line_ids +#: model:ir.ui.view,arch_db:pos_order_return.view_partial_return_wizard_form +msgid "Lines to Return" +msgstr "Líneas a devolver" + +#. module: pos_order_return +#: model:ir.model.fields,field_description:pos_order_return.field_pos_partial_return_wizard_order_id +msgid "Order to Return" +msgstr "Pedido a devolver" + +#. module: pos_order_return +#: model:ir.ui.view,arch_db:pos_order_return.view_partial_return_wizard_form +#: model:ir.ui.view,arch_db:pos_order_return.view_pos_order_form +msgid "Partial Return" +msgstr "Devolición parcial" + +#. module: pos_order_return +#: model:ir.actions.act_window,name:pos_order_return.action_pos_partial_return_wizard +msgid "Partial Return Wizard" +msgstr "Asistente de devolución parcial" + +#. module: pos_order_return +#: model:ir.model,name:pos_order_return.model_pos_order +msgid "Point of Sale Orders" +msgstr "Pedidos del TPV" + +#. module: pos_order_return +#: model:ir.model,name:pos_order_return.model_product_template +msgid "Product Template" +msgstr "Plantilla de producto" + +#. module: pos_order_return +#: model:ir.model.fields,help:pos_order_return.field_pos_partial_return_wizard_line_initial_qty +msgid "Quantity of Product initially sold" +msgstr "Cantidad de producto vendida inicialmente" + +#. module: pos_order_return +#: model:ir.ui.view,arch_db:pos_order_return.view_pos_order_form +#: model:ir.ui.view,arch_db:pos_order_return.view_pos_order_line_form +msgid "Refund" +msgstr "Factura rectificativa" + +#. module: pos_order_return +#: model:ir.model.fields,field_description:pos_order_return.field_pos_order_line_refund_line_ids +msgid "Refund Lines" +msgstr "Líneas de devolución" + +#. module: pos_order_return +#: model:ir.model.fields,field_description:pos_order_return.field_pos_order_refund_order_ids +msgid "Refund Orders" +msgstr "Pedidos de devolución" + +#. module: pos_order_return +#: model:ir.model.fields,field_description:pos_order_return.field_pos_order_refund_order_qty +msgid "Refund Orders Quantity" +msgstr "Cantidad de pedidos de devolución" + +#. module: pos_order_return +#: code:addons/pos_order_return/models/pos_order.py:55 +#, python-format +msgid "Return of %s" +msgstr "Devolución de %s" + +#. module: pos_order_return +#: model:product.product,name:pos_order_return.product_product_returnable_bottle +#: model:product.template,name:pos_order_return.product_product_returnable_bottle_product_template +msgid "Returnable Bottle" +msgstr "Botella retornable" + +#. module: pos_order_return +#: model:ir.model.fields,field_description:pos_order_return.field_pos_partial_return_wizard_line_max_returnable_qty +msgid "Returnable Quantity" +msgstr "Cantidad retornable" + +#. module: pos_order_return +#: model:ir.model.fields,field_description:pos_order_return.field_pos_order_line_returned_line_id +#: model:ir.model.fields,field_description:pos_order_return.field_pos_order_returned_order_id +msgid "Returned Order" +msgstr "Pedido devuelto" + +#. module: pos_order_return +#: model:ir.model.fields,field_description:pos_order_return.field_pos_partial_return_wizard_line_qty +msgid "Returned Quantity" +msgstr "Cantidad devuelta" + +#. module: pos_order_return +#: model:ir.model.fields,field_description:pos_order_return.field_pos_partial_return_wizard_line_wizard_id +msgid "Wizard" +msgstr "Asistente" + +#. module: pos_order_return +#: code:addons/pos_order_return/models/pos_order.py:176 +#, python-format +msgid "" +"You can not return %d %s of %s because some refunds have already been done.\n" +" Maximum quantity allowed : %d %s." +msgstr "" +"No puede devolver %d %s de %s porque ya se ha devuelto una parte.\n" +" Cantidad máxima permitida : %d %s." + +#. module: pos_order_return +#: code:addons/pos_order_return/models/pos_order.py:167 +#, python-format +msgid "" +"You can not return %d %s of %s because the original Order line only mentions " +"%d %s." +msgstr "" +"No puede devolver %d %s de %s porque el pedido original solo menciona %d %s." + +#. module: pos_order_return +#: model:ir.model,name:pos_order_return.model_pos_partial_return_wizard +msgid "pos.partial.return.wizard" +msgstr "pos.partial.return.wizard" + +#. module: pos_order_return +#: model:ir.model,name:pos_order_return.model_pos_partial_return_wizard_line +msgid "pos.partial.return.wizard.line" +msgstr "pos.partial.return.wizard.line" diff --git a/pos_order_return/i18n/fr.po b/pos_order_return/i18n/fr.po new file mode 100644 index 00000000..1a8a8d49 --- /dev/null +++ b/pos_order_return/i18n/fr.po @@ -0,0 +1,226 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * pos_return_order +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 9.0c\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2016-04-03 23:54+0000\n" +"PO-Revision-Date: 2016-04-03 23:54+0000\n" +"Last-Translator: <>\n" +"Language-Team: \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: pos_order_return +#: model:ir.model.fields,field_description:pos_order_return.field_product_product_pos_allow_negative_qty +#: model:ir.model.fields,field_description:pos_order_return.field_product_template_pos_allow_negative_qty +msgid "Allow Negative Quantity on PoS" +msgstr "PdV - Autoriser les quantités négatives" + +#. module: pos_order_return +#: model:ir.ui.view,arch_db:pos_order_return.view_partial_return_wizard_form +msgid "Cancel" +msgstr "Annuler" + +#. module: pos_order_return +#: model:ir.model.fields,help:pos_order_return.field_pos_partial_return_wizard_line_max_returnable_qty +msgid "" +"Compute maximum quantity that can be returned for this line, depending of " +"the quantity of the line and other possible refunds." +msgstr "" +"Calcule la quantité maximum qui peut être retournée pour cette ligne, en " +"fonction de la quantité de la ligne original, et des possibles retours." + +#. module: pos_order_return +#: model:ir.ui.view,arch_db:pos_order_return.view_partial_return_wizard_form +msgid "Confirm" +msgstr "Confirmer" + +#. module: pos_order_return +#: model:ir.model.fields,field_description:pos_order_return.field_pos_partial_return_wizard_create_uid +#: model:ir.model.fields,field_description:pos_order_return.field_pos_partial_return_wizard_line_create_uid +msgid "Created by" +msgstr "Créé par" + +#. module: pos_order_return +#: model:ir.model.fields,field_description:pos_order_return.field_pos_partial_return_wizard_create_date +#: model:ir.model.fields,field_description:pos_order_return.field_pos_partial_return_wizard_line_create_date +msgid "Created on" +msgstr "Créé le" + +#. module: pos_order_return +#: model:ir.model.fields,field_description:pos_order_return.field_pos_partial_return_wizard_display_name +#: model:ir.model.fields,field_description:pos_order_return.field_pos_partial_return_wizard_line_display_name +msgid "Display Name" +msgstr "Nom affiché" + +#. module: pos_order_return +#: code:addons/pos_order_return/models/pos_order.py:187 +#, python-format +msgid "" +"For legal and traceability reasons, you can not set a negative quantity (%d " +"%s of %s), without using return wizard." +msgstr "" + +#. module: pos_order_return +#: model:ir.model.fields,field_description:pos_order_return.field_pos_partial_return_wizard_id +#: model:ir.model.fields,field_description:pos_order_return.field_pos_partial_return_wizard_line_id +msgid "ID" +msgstr "ID" + +#. module: pos_order_return +#: model:ir.model.fields,field_description:pos_order_return.field_pos_partial_return_wizard_line_initial_qty +msgid "Initial Quantity" +msgstr "Quantité initial" + +#. module: pos_order_return +#: model:ir.model.fields,field_description:pos_order_return.field_pos_partial_return_wizard___last_update +#: model:ir.model.fields,field_description:pos_order_return.field_pos_partial_return_wizard_line___last_update +msgid "Last Modified on" +msgstr "Dernière modification le" + +#. module: pos_order_return +#: model:ir.model.fields,field_description:pos_order_return.field_pos_partial_return_wizard_line_write_uid +#: model:ir.model.fields,field_description:pos_order_return.field_pos_partial_return_wizard_write_uid +msgid "Last Updated by" +msgstr "Mis à jour par" + +#. module: pos_order_return +#: model:ir.model.fields,field_description:pos_order_return.field_pos_partial_return_wizard_line_write_date +#: model:ir.model.fields,field_description:pos_order_return.field_pos_partial_return_wizard_write_date +msgid "Last Updated on" +msgstr "Mis à jour le" + +#. module: pos_order_return +#: model:ir.model.fields,field_description:pos_order_return.field_pos_partial_return_wizard_line_pos_order_line_id +msgid "Line To Return" +msgstr "Ligne à retourner" + +#. module: pos_order_return +#: model:ir.model,name:pos_order_return.model_pos_order_line +#, fuzzy +msgid "Lines of Point of Sale Orders" +msgstr "Lignes de Points de Vente" + +#. module: pos_order_return +#: model:ir.model.fields,field_description:pos_order_return.field_pos_partial_return_wizard_line_ids +#: model:ir.ui.view,arch_db:pos_order_return.view_partial_return_wizard_form +msgid "Lines to Return" +msgstr "Lignes à retourner" + +#. module: pos_order_return +#: model:ir.model.fields,field_description:pos_order_return.field_pos_partial_return_wizard_order_id +msgid "Order to Return" +msgstr "Vente à retourner" + +#. module: pos_order_return +#: model:ir.ui.view,arch_db:pos_order_return.view_partial_return_wizard_form +#: model:ir.ui.view,arch_db:pos_order_return.view_pos_order_form +msgid "Partial Return" +msgstr "Retourner partiellement" + +#. module: pos_order_return +#: model:ir.actions.act_window,name:pos_order_return.action_pos_partial_return_wizard +msgid "Partial Return Wizard" +msgstr "Assistant de retour partiel" + +#. module: pos_order_return +#: model:ir.model,name:pos_order_return.model_pos_order +#, fuzzy +msgid "Point of Sale Orders" +msgstr "Point de vente" + +#. module: pos_order_return +#: model:ir.model,name:pos_order_return.model_product_template +msgid "Product Template" +msgstr "Modèle d'article" + +#. module: pos_order_return +#: model:ir.model.fields,help:pos_order_return.field_pos_partial_return_wizard_line_initial_qty +msgid "Quantity of Product initially sold" +msgstr "Quantité de produit initialement vendue" + +#. module: pos_order_return +#: model:ir.ui.view,arch_db:pos_order_return.view_pos_order_form +#: model:ir.ui.view,arch_db:pos_order_return.view_pos_order_line_form +msgid "Refund" +msgstr "Avoir" + +#. module: pos_order_return +#: model:ir.model.fields,field_description:pos_order_return.field_pos_order_line_refund_line_ids +msgid "Refund Lines" +msgstr "Lignes de vente retournées" + +#. module: pos_order_return +#: model:ir.model.fields,field_description:pos_order_return.field_pos_order_refund_order_ids +msgid "Refund Orders" +msgstr "Ventes retournées" + +#. module: pos_order_return +#: model:ir.model.fields,field_description:pos_order_return.field_pos_order_refund_order_qty +msgid "Refund Orders Quantity" +msgstr "Nombre de ventes retournées" + +#. module: pos_order_return +#: code:addons/pos_order_return/models/pos_order.py:55 +#, python-format +msgid "Return of %s" +msgstr "" + +#. module: pos_order_return +#: model:product.product,name:pos_order_return.product_product_returnable_bottle +#: model:product.template,name:pos_order_return.product_product_returnable_bottle_product_template +msgid "Returnable Bottle" +msgstr "Bouteille consignée" + +#. module: pos_order_return +#: model:ir.model.fields,field_description:pos_order_return.field_pos_partial_return_wizard_line_max_returnable_qty +msgid "Returnable Quantity" +msgstr "Quantité retournable" + +#. module: pos_order_return +#: model:ir.model.fields,field_description:pos_order_return.field_pos_order_line_returned_line_id +#: model:ir.model.fields,field_description:pos_order_return.field_pos_order_returned_order_id +msgid "Returned Order" +msgstr "Vente retournée" + +#. module: pos_order_return +#: model:ir.model.fields,field_description:pos_order_return.field_pos_partial_return_wizard_line_qty +msgid "Returned Quantity" +msgstr "Quantité retournée" + +#. module: pos_order_return +#: model:ir.model.fields,field_description:pos_order_return.field_pos_partial_return_wizard_line_wizard_id +msgid "Wizard" +msgstr "Assistant" + +#. module: pos_order_return +#: code:addons/pos_order_return/models/pos_order.py:176 +#, python-format +msgid "" +"You can not return %d %s of %s because some refunds have already been done.\n" +" Maximum quantity allowed : %d %s." +msgstr "" + +#. module: pos_order_return +#: code:addons/pos_order_return/models/pos_order.py:167 +#, python-format +msgid "" +"You can not return %d %s of %s because the original Order line only mentions " +"%d %s." +msgstr "" + +#. module: pos_order_return +#: model:ir.model,name:pos_order_return.model_pos_partial_return_wizard +msgid "pos.partial.return.wizard" +msgstr "pos.partial.return.wizard" + +#. module: pos_order_return +#: model:ir.model,name:pos_order_return.model_pos_partial_return_wizard_line +msgid "pos.partial.return.wizard.line" +msgstr "pos.partial.return.wizard.line" diff --git a/pos_order_return/i18n/pos_order_return.pot b/pos_order_return/i18n/pos_order_return.pot new file mode 100644 index 00000000..b4f802c4 --- /dev/null +++ b/pos_order_return/i18n/pos_order_return.pot @@ -0,0 +1,213 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * pos_order_return +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 11.0\n" +"Report-Msgid-Bugs-To: \n" +"Last-Translator: <>\n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: pos_order_return +#: model:ir.model.fields,field_description:pos_order_return.field_product_product_pos_allow_negative_qty +#: model:ir.model.fields,field_description:pos_order_return.field_product_template_pos_allow_negative_qty +msgid "Allow Negative Quantity on PoS" +msgstr "" + +#. module: pos_order_return +#: model:ir.ui.view,arch_db:pos_order_return.view_partial_return_wizard_form +msgid "Cancel" +msgstr "" + +#. module: pos_order_return +#: model:ir.model.fields,help:pos_order_return.field_pos_partial_return_wizard_line_max_returnable_qty +msgid "Compute maximum quantity that can be returned for this line, depending of the quantity of the line and other possible refunds." +msgstr "" + +#. module: pos_order_return +#: model:ir.ui.view,arch_db:pos_order_return.view_partial_return_wizard_form +msgid "Confirm" +msgstr "" + +#. module: pos_order_return +#: model:ir.model.fields,field_description:pos_order_return.field_pos_partial_return_wizard_create_uid +#: model:ir.model.fields,field_description:pos_order_return.field_pos_partial_return_wizard_line_create_uid +msgid "Created by" +msgstr "" + +#. module: pos_order_return +#: model:ir.model.fields,field_description:pos_order_return.field_pos_partial_return_wizard_create_date +#: model:ir.model.fields,field_description:pos_order_return.field_pos_partial_return_wizard_line_create_date +msgid "Created on" +msgstr "" + +#. module: pos_order_return +#: model:ir.model.fields,field_description:pos_order_return.field_pos_partial_return_wizard_display_name +#: model:ir.model.fields,field_description:pos_order_return.field_pos_partial_return_wizard_line_display_name +msgid "Display Name" +msgstr "" + +#. module: pos_order_return +#: code:addons/pos_order_return/models/pos_order.py:187 +#, python-format +msgid "For legal and traceability reasons, you can not set a negative quantity (%d %s of %s), without using return wizard." +msgstr "" + +#. module: pos_order_return +#: model:ir.model.fields,field_description:pos_order_return.field_pos_partial_return_wizard_id +#: model:ir.model.fields,field_description:pos_order_return.field_pos_partial_return_wizard_line_id +msgid "ID" +msgstr "" + +#. module: pos_order_return +#: model:ir.model.fields,field_description:pos_order_return.field_pos_partial_return_wizard_line_initial_qty +msgid "Initial Quantity" +msgstr "" + +#. module: pos_order_return +#: model:ir.model.fields,field_description:pos_order_return.field_pos_partial_return_wizard___last_update +#: model:ir.model.fields,field_description:pos_order_return.field_pos_partial_return_wizard_line___last_update +msgid "Last Modified on" +msgstr "" + +#. module: pos_order_return +#: model:ir.model.fields,field_description:pos_order_return.field_pos_partial_return_wizard_line_write_uid +#: model:ir.model.fields,field_description:pos_order_return.field_pos_partial_return_wizard_write_uid +msgid "Last Updated by" +msgstr "" + +#. module: pos_order_return +#: model:ir.model.fields,field_description:pos_order_return.field_pos_partial_return_wizard_line_write_date +#: model:ir.model.fields,field_description:pos_order_return.field_pos_partial_return_wizard_write_date +msgid "Last Updated on" +msgstr "" + +#. module: pos_order_return +#: model:ir.model.fields,field_description:pos_order_return.field_pos_partial_return_wizard_line_pos_order_line_id +msgid "Line To Return" +msgstr "" + +#. module: pos_order_return +#: model:ir.model,name:pos_order_return.model_pos_order_line +msgid "Lines of Point of Sale Orders" +msgstr "" + +#. module: pos_order_return +#: model:ir.model.fields,field_description:pos_order_return.field_pos_partial_return_wizard_line_ids +#: model:ir.ui.view,arch_db:pos_order_return.view_partial_return_wizard_form +msgid "Lines to Return" +msgstr "" + +#. module: pos_order_return +#: model:ir.model.fields,field_description:pos_order_return.field_pos_partial_return_wizard_order_id +msgid "Order to Return" +msgstr "" + +#. module: pos_order_return +#: model:ir.ui.view,arch_db:pos_order_return.view_partial_return_wizard_form +#: model:ir.ui.view,arch_db:pos_order_return.view_pos_order_form +msgid "Partial Return" +msgstr "" + +#. module: pos_order_return +#: model:ir.actions.act_window,name:pos_order_return.action_pos_partial_return_wizard +msgid "Partial Return Wizard" +msgstr "" + +#. module: pos_order_return +#: model:ir.model,name:pos_order_return.model_pos_order +msgid "Point of Sale Orders" +msgstr "" + +#. module: pos_order_return +#: model:ir.model,name:pos_order_return.model_product_template +msgid "Product Template" +msgstr "" + +#. module: pos_order_return +#: model:ir.model.fields,help:pos_order_return.field_pos_partial_return_wizard_line_initial_qty +msgid "Quantity of Product initially sold" +msgstr "" + +#. module: pos_order_return +#: model:ir.ui.view,arch_db:pos_order_return.view_pos_order_form +#: model:ir.ui.view,arch_db:pos_order_return.view_pos_order_line_form +msgid "Refund" +msgstr "" + +#. module: pos_order_return +#: model:ir.model.fields,field_description:pos_order_return.field_pos_order_line_refund_line_ids +msgid "Refund Lines" +msgstr "" + +#. module: pos_order_return +#: model:ir.model.fields,field_description:pos_order_return.field_pos_order_refund_order_ids +msgid "Refund Orders" +msgstr "" + +#. module: pos_order_return +#: model:ir.model.fields,field_description:pos_order_return.field_pos_order_refund_order_qty +msgid "Refund Orders Quantity" +msgstr "" + +#. module: pos_order_return +#: code:addons/pos_order_return/models/pos_order.py:55 +#, python-format +msgid "Return of %s" +msgstr "" + +#. module: pos_order_return +#: model:product.product,name:pos_order_return.product_product_returnable_bottle +#: model:product.template,name:pos_order_return.product_product_returnable_bottle_product_template +msgid "Returnable Bottle" +msgstr "" + +#. module: pos_order_return +#: model:ir.model.fields,field_description:pos_order_return.field_pos_partial_return_wizard_line_max_returnable_qty +msgid "Returnable Quantity" +msgstr "" + +#. module: pos_order_return +#: model:ir.model.fields,field_description:pos_order_return.field_pos_order_line_returned_line_id +#: model:ir.model.fields,field_description:pos_order_return.field_pos_order_returned_order_id +msgid "Returned Order" +msgstr "" + +#. module: pos_order_return +#: model:ir.model.fields,field_description:pos_order_return.field_pos_partial_return_wizard_line_qty +msgid "Returned Quantity" +msgstr "" + +#. module: pos_order_return +#: model:ir.model.fields,field_description:pos_order_return.field_pos_partial_return_wizard_line_wizard_id +msgid "Wizard" +msgstr "" + +#. module: pos_order_return +#: code:addons/pos_order_return/models/pos_order.py:176 +#, python-format +msgid "You can not return %d %s of %s because some refunds have already been done.\n" +" Maximum quantity allowed : %d %s." +msgstr "" + +#. module: pos_order_return +#: code:addons/pos_order_return/models/pos_order.py:167 +#, python-format +msgid "You can not return %d %s of %s because the original Order line only mentions %d %s." +msgstr "" + +#. module: pos_order_return +#: model:ir.model,name:pos_order_return.model_pos_partial_return_wizard +msgid "pos.partial.return.wizard" +msgstr "" + +#. module: pos_order_return +#: model:ir.model,name:pos_order_return.model_pos_partial_return_wizard_line +msgid "pos.partial.return.wizard.line" +msgstr "" + diff --git a/pos_order_return/models/__init__.py b/pos_order_return/models/__init__.py new file mode 100644 index 00000000..d7439b04 --- /dev/null +++ b/pos_order_return/models/__init__.py @@ -0,0 +1,3 @@ + +from . import product_template +from . import pos_order diff --git a/pos_order_return/models/pos_order.py b/pos_order_return/models/pos_order.py new file mode 100644 index 00000000..68dd7578 --- /dev/null +++ b/pos_order_return/models/pos_order.py @@ -0,0 +1,192 @@ +# Copyright 2016-2018 Sylvain LE GAL (https://twitter.com/legalsylvain) +# Copyright 2018 David Vidal +# Copyright 2018 Lambda IS DOOEL +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + + +from odoo import _, api, fields, models +from odoo.exceptions import ValidationError + + +class PosOrder(models.Model): + _inherit = 'pos.order' + + returned_order_id = fields.Many2one( + comodel_name='pos.order', + string='Returned Order', + readonly=True, + ) + refund_order_ids = fields.One2many( + comodel_name='pos.order', + inverse_name='returned_order_id', + string='Refund Orders', + readonly=True, + ) + refund_order_qty = fields.Integer( + compute='_compute_refund_order_qty', + string='Refund Orders Quantity', + ) + + def _compute_refund_order_qty(self): + order_data = self.env['pos.order'].read_group( + [('returned_order_id', 'in', self.ids)], + ['returned_order_id'], ['returned_order_id'] + ) + mapped_data = dict( + [(order['returned_order_id'][0], order['returned_order_id_count']) + for order in order_data]) + for order in self: + order.refund_order_qty = mapped_data.get(order.id, 0) + + def _blank_refund(self, res): + self.ensure_one() + new_order = self.browse(res['res_id']) + new_order.returned_order_id = self + # Remove created lines and recreate and link Lines + new_order.lines.unlink() + return new_order + + def _prepare_invoice(self): + res = super(PosOrder, self)._prepare_invoice() + if not self.returned_order_id.invoice_id: + return res + res.update({ + 'origin': self.returned_order_id.invoice_id.number, + 'name': _( + 'Return of %s' % self.returned_order_id.invoice_id.number), + 'refund_invoice_id': self.returned_order_id.invoice_id.id, + }) + return res + + def _action_pos_order_invoice(self): + """Wrap common process""" + self.action_pos_order_invoice() + self.invoice_id.sudo().action_invoice_open() + self.account_move = self.invoice_id.move_id + + def refund(self): + # Call super to use original refund algorithm (session management, ...) + ctx = dict(self.env.context, do_not_check_negative_qty=True) + res = super(PosOrder, self.with_context(ctx)).refund() + new_order = self._blank_refund(res) + for line in self.lines: + qty = - line.max_returnable_qty([]) + if qty != 0: + copy_line = line.copy() + copy_line.write({ + 'order_id': new_order.id, + 'returned_line_id': line.id, + 'qty': qty, + }) + return res + + def partial_refund(self, partial_return_wizard): + ctx = dict(self.env.context, partial_refund=True) + res = self.with_context(ctx).refund() + new_order = self._blank_refund(res) + for wizard_line in partial_return_wizard.line_ids: + qty = -wizard_line.qty + if qty != 0: + copy_line = wizard_line.pos_order_line_id.copy() + copy_line.write({ + 'order_id': new_order.id, + 'returned_line_id': wizard_line.pos_order_line_id.id, + 'qty': qty, + }) + return res + + def action_pos_order_paid(self): + res = super(PosOrder, self).action_pos_order_paid() + if self.returned_order_id and self.returned_order_id.invoice_id: + self._action_pos_order_invoice() + return res + + def _create_picking_return(self): + self.ensure_one() + picking = self.returned_order_id.picking_id + ctx = dict(self.env.context, + active_ids=picking.ids, active_id=picking.id) + wizard = self.env['stock.return.picking'].with_context(ctx).create({}) + # Discard not returned lines + wizard.product_return_moves.filtered( + lambda x: x.product_id not in self.mapped( + 'lines.product_id')).unlink() + to_return = {} + for product in self.lines.mapped('product_id'): + to_return[product] = -sum( + self.lines.filtered( + lambda x: x.product_id == product).mapped('qty')) + for move in wizard.product_return_moves: + if to_return[move.product_id] < move.quantity: + move.quantity = to_return[move.product_id] + to_return[move.product_id] -= move.quantity + return wizard + + def create_picking(self): + """Odoo bases return picking if the quantities are negative, but it's + not linked to the original one""" + res = super(PosOrder, self.filtered(lambda x: not x.returned_order_id) + ).create_picking() + for order in self.filtered('returned_order_id'): + wizard = order._create_picking_return() + res = wizard.create_returns() + order.write({'picking_id': res['res_id']}) + order._force_picking_done(order.picking_id) + return res + + +class PosOrderLine(models.Model): + _inherit = 'pos.order.line' + + returned_line_id = fields.Many2one( + comodel_name='pos.order.line', + string='Returned Order', + readonly=True, + ) + refund_line_ids = fields.One2many( + comodel_name='pos.order.line', + inverse_name='returned_line_id', + string='Refund Lines', + readonly=True, + ) + + @api.model + def max_returnable_qty(self, ignored_line_ids): + qty = self.qty + for refund_line in self.refund_line_ids: + if refund_line.id not in ignored_line_ids: + qty += refund_line.qty + return qty + + @api.constrains('returned_line_id', 'qty') + def _check_return_qty(self): + if self.env.context.get('do_not_check_negative_qty', False): + return True + for line in self: + if line.returned_line_id and -line.qty > line.returned_line_id.qty: + raise ValidationError(_( + "You can not return %d %s of %s because the original " + "Order line only mentions %d %s." + ) % (-line.qty, line.product_id.uom_id.name, + line.product_id.name, line.returned_line_id.qty, + line.product_id.uom_id.name)) + if (line.returned_line_id and + -line.qty > + line.returned_line_id.max_returnable_qty([line.id])): + raise ValidationError(_( + "You can not return %d %s of %s because some refunds" + " have already been done.\n Maximum quantity allowed :" + " %d %s." + ) % (-line.qty, line.product_id.uom_id.name, + line.product_id.name, + line.returned_line_id.max_returnable_qty([line.id]), + line.product_id.uom_id.name)) + if (not line.returned_line_id and + line.qty < 0 and not + line.product_id.product_tmpl_id.pos_allow_negative_qty): + raise ValidationError(_( + "For legal and traceability reasons, you can not set a" + " negative quantity (%d %s of %s), without using " + "return wizard." + ) % (line.qty, line.product_id.uom_id.name, + line.product_id.name)) diff --git a/pos_order_return/models/product_template.py b/pos_order_return/models/product_template.py new file mode 100644 index 00000000..e86154e7 --- /dev/null +++ b/pos_order_return/models/product_template.py @@ -0,0 +1,14 @@ +# Copyright 2016-2018 Sylvain LE GAL (https://twitter.com/legalsylvain) +# Copyright 2018 Lambda IS DOOEL +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from odoo import fields, models + + +class ProductTemplate(models.Model): + _inherit = 'product.template' + + pos_allow_negative_qty = fields.Boolean( + string='Allow Negative Quantity on PoS', + default=True, + ) diff --git a/pos_order_return/readme/CONFIGURE.rst b/pos_order_return/readme/CONFIGURE.rst new file mode 100644 index 00000000..91f5aa8f --- /dev/null +++ b/pos_order_return/readme/CONFIGURE.rst @@ -0,0 +1,11 @@ +In some cases, you may want to let the possibility to allow negative quantity +in a PoS Order, without mentioning initial order. This can happen for special +products like returnable products, etc. + +In that case, a checkbox is possible on Product Form View to allow such case + +.. image:: /pos_order_return/static/description/product_returnable_bottle.png + +.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas + :alt: Try me on Runbot + :target: https://runbot.odoo-community.org/runbot/184/10.0 diff --git a/pos_order_return/readme/CONTRIBUTORS.rst b/pos_order_return/readme/CONTRIBUTORS.rst new file mode 100644 index 00000000..4e1bf63f --- /dev/null +++ b/pos_order_return/readme/CONTRIBUTORS.rst @@ -0,0 +1,3 @@ +* Sylvain LE GAL +* David Vidal +* Kiril Vangelovski diff --git a/pos_order_return/readme/CREDITS.rst b/pos_order_return/readme/CREDITS.rst new file mode 100644 index 00000000..18cce2c1 --- /dev/null +++ b/pos_order_return/readme/CREDITS.rst @@ -0,0 +1,7 @@ +Funders +~~~~~~~ + +The development of this module has been financially supported by: + +* La Louve (www.lalouve.net) +* GRAP, Groupement Régional Alimentaire de Proximité (www.grap.coop) diff --git a/pos_order_return/readme/DESCRIPTION.rst b/pos_order_return/readme/DESCRIPTION.rst new file mode 100644 index 00000000..93ee72fc --- /dev/null +++ b/pos_order_return/readme/DESCRIPTION.rst @@ -0,0 +1,8 @@ +This module extends the functionality of odoo Point Of Sale about POS Order +returns. + +With this module, it is now forbidden to return more quantity than the initial +one. + +A link is created between the returned Order and the initial Order. +A link is created between the returned Order Line and the initial Order Line. diff --git a/pos_order_return/readme/USAGE.rst b/pos_order_return/readme/USAGE.rst new file mode 100644 index 00000000..860c0042 --- /dev/null +++ b/pos_order_return/readme/USAGE.rst @@ -0,0 +1,23 @@ +Select an PoS Order an choose either *Return Products* (full return of the +order) or *Partial Return*. In this case, a wizard allows to select just some +products and quantities to return: + +.. image:: /pos_order_return/static/description/partial_return_wizard.png + +Register the refund payment to finish the return. If the original order was +invoiced, a refund invoice will be made. + +**Implemented Constraints** + +* User can not return more products than the initial quantity: + +.. image:: /pos_order_return/static/description/returned_qty_over_initial.png + +* If a line has been partially refund, only a reduced quantity can be returned: + +.. image:: /pos_order_return/static/description/sum_returned_qty_over_initial.png + +* It is not possible to set a negative quantity if the initial Pos Order is + not indicated: + +.. image:: /pos_order_return/static/description/initial_pos_order_required.png diff --git a/pos_order_return/static/description/icon.png b/pos_order_return/static/description/icon.png new file mode 100644 index 00000000..3134d33e Binary files /dev/null and b/pos_order_return/static/description/icon.png differ diff --git a/pos_order_return/static/description/index.html b/pos_order_return/static/description/index.html new file mode 100644 index 00000000..8e326baf --- /dev/null +++ b/pos_order_return/static/description/index.html @@ -0,0 +1,476 @@ + + + + + + +Point of Sale Order Return + + + +
+

Point of Sale Order Return

+ + +

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

+

This module extends the functionality of odoo Point Of Sale about POS Order +returns.

+

With this module, it is now forbidden to return more quantity than the initial +one.

+

A link is created between the returned Order and the initial Order. +A link is created between the returned Order Line and the initial Order Line.

+

Table of contents

+ +
+

Configuration

+

In some cases, you may want to let the possibility to allow negative quantity +in a PoS Order, without mentioning initial order. This can happen for special +products like returnable products, etc.

+

In that case, a checkbox is possible on Product Form View to allow such case

+https://raw.githubusercontent.com/pos_order_return/static/description/product_returnable_bottle.png +Try me on Runbot +
+
+

Usage

+

Select an PoS Order an choose either Return Products (full return of the +order) or Partial Return. In this case, a wizard allows to select just some +products and quantities to return:

+https://raw.githubusercontent.com/pos_order_return/static/description/partial_return_wizard.png +

Register the refund payment to finish the return. If the original order was +invoiced, a refund invoice will be made.

+

Implemented Constraints

+
    +
  • User can not return more products than the initial quantity:
  • +
+https://raw.githubusercontent.com/pos_order_return/static/description/returned_qty_over_initial.png +
    +
  • If a line has been partially refund, only a reduced quantity can be returned:
  • +
+https://raw.githubusercontent.com/pos_order_return/static/description/sum_returned_qty_over_initial.png +
    +
  • It is not possible to set a negative quantity if the initial Pos Order is +not indicated:
  • +
+https://raw.githubusercontent.com/pos_order_return/static/description/initial_pos_order_required.png +
+
+

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

+
    +
  • La Louve
  • +
  • GRAP
  • +
  • Tecnativa
  • +
  • Lambda IS
  • +
+
+
+

Contributors

+ +
+ +
+

Funders

+

The development of this module has been financially supported by:

+
    +
  • La Louve (www.lalouve.net)
  • +
  • GRAP, Groupement Régional Alimentaire de Proximité (www.grap.coop)
  • +
+
+
+

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.

+

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

+

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

+
+
+
+ + diff --git a/pos_order_return/static/description/initial_pos_order_required.png b/pos_order_return/static/description/initial_pos_order_required.png new file mode 100644 index 00000000..57d28c26 Binary files /dev/null and b/pos_order_return/static/description/initial_pos_order_required.png differ diff --git a/pos_order_return/static/description/partial_return_wizard.png b/pos_order_return/static/description/partial_return_wizard.png new file mode 100644 index 00000000..cef08d52 Binary files /dev/null and b/pos_order_return/static/description/partial_return_wizard.png differ diff --git a/pos_order_return/static/description/product_returnable_bottle.png b/pos_order_return/static/description/product_returnable_bottle.png new file mode 100644 index 00000000..131d4e39 Binary files /dev/null and b/pos_order_return/static/description/product_returnable_bottle.png differ diff --git a/pos_order_return/static/description/returned_qty_over_initial.png b/pos_order_return/static/description/returned_qty_over_initial.png new file mode 100644 index 00000000..d7c2927c Binary files /dev/null and b/pos_order_return/static/description/returned_qty_over_initial.png differ diff --git a/pos_order_return/static/description/sum_returned_qty_over_initial.png b/pos_order_return/static/description/sum_returned_qty_over_initial.png new file mode 100644 index 00000000..48ef2273 Binary files /dev/null and b/pos_order_return/static/description/sum_returned_qty_over_initial.png differ diff --git a/pos_order_return/static/img/product_returnable_bottle-image.jpg b/pos_order_return/static/img/product_returnable_bottle-image.jpg new file mode 100644 index 00000000..ff474204 Binary files /dev/null and b/pos_order_return/static/img/product_returnable_bottle-image.jpg differ diff --git a/pos_order_return/tests/__init__.py b/pos_order_return/tests/__init__.py new file mode 100644 index 00000000..24d89b38 --- /dev/null +++ b/pos_order_return/tests/__init__.py @@ -0,0 +1,2 @@ + +from . import test_pos_order_return diff --git a/pos_order_return/tests/test_pos_order_return.py b/pos_order_return/tests/test_pos_order_return.py new file mode 100644 index 00000000..b174be4a --- /dev/null +++ b/pos_order_return/tests/test_pos_order_return.py @@ -0,0 +1,127 @@ +# Copyright 2018 Tecnativa - David Vidal +# Copyright 2018 Lambda IS DOOEL +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from odoo.tests import common + + +@common.at_install(False) +@common.post_install(True) +class TestPOSOrderReturn(common.HttpCase): + def setUp(self): + super(TestPOSOrderReturn, self).setUp() + self.pricelist = self.env['product.pricelist'].create({ + 'name': 'Test pricelist', + 'item_ids': [(0, 0, { + 'applied_on': '3_global', + 'compute_price': 'formula', + 'base': 'list_price', + })] + }) + self.partner = self.env['res.partner'].create({ + 'name': 'Mr. Odoo', + 'property_product_pricelist': self.pricelist.id, + }) + self.product_1 = self.env['product.product'].create({ + 'name': 'Test product 1', + 'standard_price': 1.0, + 'type': 'product', + 'pos_allow_negative_qty': False, + 'taxes_id': False, + }) + self.product_2 = self.env['product.product'].create({ + 'name': 'Test product 2', + 'standard_price': 1.0, + 'type': 'product', + 'pos_allow_negative_qty': True, + 'taxes_id': False, + }) + self.PosOrder = self.env['pos.order'] + self.pos_config = self.env.ref('point_of_sale.pos_config_main') + self.pos_config.write({ + 'available_pricelist_ids': [(6, 0, self.pricelist.ids)], + 'pricelist_id': self.pricelist.id, + }) + self.pos_config.open_session_cb() + self.pos_order = self.PosOrder.create({ + 'session_id': self.pos_config.current_session_id.id, + 'partner_id': self.partner.id, + 'pricelist_id': self.partner.property_product_pricelist.id, + 'amount_tax': 0, + 'amount_total': 2700, + 'amount_paid': 2700, + 'amount_return': 0, + 'lines': [ + (0, 0, { + 'name': 'POSLINE/0001', + 'product_id': self.product_1.id, + 'price_unit': 450, + 'price_subtotal': 450, + 'price_subtotal_incl': 450, + 'qty': 2.0, + }), + (0, 0, { + 'name': 'POSLINE/0002', + 'product_id': self.product_2.id, + 'price_unit': 450, + 'price_subtotal': 450, + 'price_subtotal_incl': 450, + 'qty': 2.0, + }), + (0, 0, { + 'name': 'POSLINE/0003', + 'product_id': self.product_1.id, + 'price_unit': 450, + 'price_subtotal': 450, + 'price_subtotal_incl': 450, + 'qty': 2.0, + }), + ], + }) + pos_make_payment = self.env['pos.make.payment'].with_context({ + 'active_ids': [self.pos_order.id], + 'active_id': self.pos_order.id, + }).create({}) + pos_make_payment.with_context(active_id=self.pos_order.id).check() + self.pos_order.create_picking() + res = self.pos_order.action_pos_order_invoice() + self.invoice = self.env['account.invoice'].browse(res['res_id']) + + def test_pos_order_full_refund(self): + self.pos_order.refund() + refund_order = self.pos_order.refund_order_ids + self.assertEqual(len(refund_order), 1) + pos_make_payment = self.env['pos.make.payment'].with_context({ + 'active_ids': refund_order.ids, + 'active_id': refund_order.id, + }).create({}) + pos_make_payment.with_context(active_id=refund_order.id).check() + refund_invoice = refund_order.invoice_id + self.assertEqual(refund_invoice.refund_invoice_id, self.invoice) + # Partner balance is 0 + self.assertEqual(sum( + self.partner.mapped('invoice_ids.amount_total_signed')), 0) + self.assertEqual(self.pos_order.picking_id.state, 'done') + + def test_pos_order_partial_refund(self): + partial_refund = self.env['pos.partial.return.wizard'].with_context({ + 'active_ids': self.pos_order.ids, + 'active_id': self.pos_order.id, + }).create({}) + # Return just 1 item from line POSLINE/0001 + partial_refund.line_ids[0].qty = 1 + # Return 2 items from line POSLINE/0003 + partial_refund.line_ids[1].qty = 2 + partial_refund.confirm() + refund_order = self.pos_order.refund_order_ids + self.assertEqual(len(refund_order), 1) + self.assertEqual(len(refund_order.lines), 2) + pos_make_payment = self.env['pos.make.payment'].with_context({ + 'active_ids': refund_order.ids, + 'active_id': refund_order.id, + }).create({}) + pos_make_payment.with_context(active_id=refund_order.id).check() + # Partner balance is 1350 + self.assertEqual(sum( + self.partner.mapped('invoice_ids.amount_total_signed')), 1350) + self.assertEqual(self.pos_order.picking_id.state, 'done') diff --git a/pos_order_return/views/pos_order_view.xml b/pos_order_return/views/pos_order_view.xml new file mode 100644 index 00000000..e217eae8 --- /dev/null +++ b/pos_order_return/views/pos_order_view.xml @@ -0,0 +1,56 @@ + + + + + + + Partial Return Wizard + ir.actions.act_window + pos.partial.return.wizard + form + form + new + + + + pos.order + + + + + + + + + + + + + + + + + + + + + + pos.order.line + + + + + + + + + + + + diff --git a/pos_order_return/views/product_product_view.xml b/pos_order_return/views/product_product_view.xml new file mode 100644 index 00000000..587a1b85 --- /dev/null +++ b/pos_order_return/views/product_product_view.xml @@ -0,0 +1,17 @@ + + + + + + + product.template + + + + + + + + + diff --git a/pos_order_return/wizard/__init__.py b/pos_order_return/wizard/__init__.py new file mode 100644 index 00000000..ee446c7c --- /dev/null +++ b/pos_order_return/wizard/__init__.py @@ -0,0 +1,2 @@ + +from . import pos_partial_return_wizard diff --git a/pos_order_return/wizard/pos_partial_return_wizard.py b/pos_order_return/wizard/pos_partial_return_wizard.py new file mode 100644 index 00000000..e3db24fc --- /dev/null +++ b/pos_order_return/wizard/pos_partial_return_wizard.py @@ -0,0 +1,74 @@ +# Copyright 2016-2018 Sylvain LE GAL (https://twitter.com/legalsylvain) +# Copyright 2018 Lambda IS DOOEL +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from odoo import api, fields, models + + +class PosPartialReturnWizard(models.TransientModel): + _name = 'pos.partial.return.wizard' + _description = 'Partial Return Wizard' + + order_id = fields.Many2one( + comodel_name='pos.order', + string='Order to Return', + ) + line_ids = fields.One2many( + comodel_name='pos.partial.return.wizard.line', + inverse_name='wizard_id', + string='Lines to Return', + ) + + def confirm(self): + self.ensure_one() + return self[0].order_id.partial_refund(self[0]) + + @api.model + def default_get(self, fields): + order_obj = self.env['pos.order'] + res = super(PosPartialReturnWizard, self).default_get(fields) + order = order_obj.browse(self.env.context.get('active_id', False)) + if order: + line_ids = [] + for line in order.lines: + line_ids.append((0, 0, { + 'pos_order_line_id': line.id, + 'initial_qty': line.qty, + 'max_returnable_qty': line.max_returnable_qty([]), + })) + res.update({ + 'order_id': order.id, + 'line_ids': line_ids}) + return res + + +class PosPartialReturnWizardLine(models.TransientModel): + _name = 'pos.partial.return.wizard.line' + _description = 'Partial Return Wizard Line' + + wizard_id = fields.Many2one( + comodel_name='pos.partial.return.wizard', + string='Wizard', + ) + pos_order_line_id = fields.Many2one( + comodel_name='pos.order.line', + required=True, + readonly=True, + string='Line To Return', + ) + initial_qty = fields.Float( + string='Initial Quantity', + readonly=True, + help="Quantity of Product initially sold", + ) + max_returnable_qty = fields.Float( + string='Returnable Quantity', + readonly=True, + help="Compute maximum quantity that can be returned for this line, " + "depending of the quantity of the line and other possible " + "refunds.", + ) + qty = fields.Float( + string='Returned Quantity', + default=0.0, + ) diff --git a/pos_order_return/wizard/pos_partial_return_wizard_view.xml b/pos_order_return/wizard/pos_partial_return_wizard_view.xml new file mode 100644 index 00000000..dd92734d --- /dev/null +++ b/pos_order_return/wizard/pos_partial_return_wizard_view.xml @@ -0,0 +1,29 @@ + + + + + + + pos.partial.return.wizard + +
+ + + + + + + + + + + +
+
+
+ +