diff --git a/purchase_service_picking/README.rst b/purchase_service_picking/README.rst new file mode 100644 index 00000000000..c1b13a3d077 --- /dev/null +++ b/purchase_service_picking/README.rst @@ -0,0 +1,87 @@ +======================== +Purchase service picking +======================== + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:9932b4c4a1a3857b71ceb81a1ed61b9da72e66cd23aff07f428b82f6fd7a6546 + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |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%2Fpurchase--workflow-lightgray.png?logo=github + :target: https://github.com/OCA/purchase-workflow/tree/14.0/purchase_service_picking + :alt: OCA/purchase-workflow +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/purchase-workflow-14-0/purchase-workflow-14-0-purchase_service_picking + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png + :target: https://runboat.odoo-community.org/builds?repo=OCA/purchase-workflow&target_branch=14.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| + +The module allows to manage the reception of service lines for purchase orders. + +**Table of contents** + +.. contents:: + :local: + +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 to smash 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 `_: + + * Víctor Martínez + * Pedro M. Baeza + +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-victoralmau| image:: https://github.com/victoralmau.png?size=40px + :target: https://github.com/victoralmau + :alt: victoralmau + +Current `maintainer `__: + +|maintainer-victoralmau| + +This module is part of the `OCA/purchase-workflow `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/purchase_service_picking/__init__.py b/purchase_service_picking/__init__.py new file mode 100644 index 00000000000..aee8895e7a3 --- /dev/null +++ b/purchase_service_picking/__init__.py @@ -0,0 +1,2 @@ +from . import models +from . import wizards diff --git a/purchase_service_picking/__manifest__.py b/purchase_service_picking/__manifest__.py new file mode 100644 index 00000000000..e577df55755 --- /dev/null +++ b/purchase_service_picking/__manifest__.py @@ -0,0 +1,25 @@ +# Copyright 2025 Tecnativa - Víctor Martínez +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +{ + "name": "Purchase service picking", + "version": "14.0.1.0.0", + "category": "Purchase", + "author": "Tecnativa, Odoo Community Association (OCA)", + "website": "https://github.com/OCA/purchase-workflow", + "license": "AGPL-3", + "depends": ["purchase"], + "maintainers": ["victoralmau"], + "data": [ + "security/ir.model.access.csv", + "security/security.xml", + "data/ir_sequence_data.xml", + "views/purchase_order_views.xml", + "wizards/service_picking_return_views.xml", + "views/service_picking_views.xml", + "wizards/service_immediate_transfer_views.xml", + "wizards/service_backorder_confirmation_views.xml", + ], + "installable": True, + "auto_install": False, +} diff --git a/purchase_service_picking/data/ir_sequence_data.xml b/purchase_service_picking/data/ir_sequence_data.xml new file mode 100644 index 00000000000..0cebf6225f7 --- /dev/null +++ b/purchase_service_picking/data/ir_sequence_data.xml @@ -0,0 +1,12 @@ + + + + Service picking + service.picking + SP/ + 5 + 1 + 1 + + + diff --git a/purchase_service_picking/i18n/es.po b/purchase_service_picking/i18n/es.po new file mode 100644 index 00000000000..5564be5a3c7 --- /dev/null +++ b/purchase_service_picking/i18n/es.po @@ -0,0 +1,803 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * purchase_service_picking +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 14.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2025-01-14 12:14+0000\n" +"PO-Revision-Date: 2025-01-14 13:16+0100\n" +"Last-Translator: \n" +"Language-Team: \n" +"Language: es\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: \n" +"X-Generator: Poedit 3.0.1\n" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking__message_needaction +msgid "Action Needed" +msgstr "Acción requerida" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking__activity_ids +msgid "Activities" +msgstr "Actividades" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking__activity_exception_decoration +msgid "Activity Exception Decoration" +msgstr "Decoración de Actividad de Excepción" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking__activity_state +msgid "Activity State" +msgstr "Estado de la Actividad" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking__activity_type_icon +msgid "Activity Type Icon" +msgstr "Icono de tipo de actividad" + +#. module: purchase_service_picking +#: model_terms:ir.ui.view,arch_db:purchase_service_picking.view_service_picking_form +msgid "Additional Info" +msgstr "Información adicional" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking_line__returned_line_ids +msgid "All returned lines" +msgstr "Todas las líneas devueltas" + +#. module: purchase_service_picking +#: model_terms:ir.ui.view,arch_db:purchase_service_picking.view_immediate_transfer +msgid "Apply" +msgstr "Aplicar" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking__message_attachment_count +msgid "Attachment Count" +msgstr "Nº de archivos adjuntos" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking__backorder_id +msgid "Back Order of" +msgstr "Entrega parcial de" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking__backorder_ids +msgid "Back Orders" +msgstr "Entregas parciales" + +#. module: purchase_service_picking +#: model:ir.model,name:purchase_service_picking.model_service_backorder_confirmation +msgid "Backorder Confirmation" +msgstr "Confirmación de entrega parcial" + +#. module: purchase_service_picking +#: model:ir.model,name:purchase_service_picking.model_service_backorder_confirmation_line +msgid "Backorder Confirmation Line" +msgstr "Línea de confirmación de entrega parcial" + +#. module: purchase_service_picking +#: model_terms:ir.ui.view,arch_db:purchase_service_picking.view_backorder_confirmation +msgid "Backorder creation" +msgstr "Creación de entrega parcial" + +#. module: purchase_service_picking +#: model_terms:ir.ui.view,arch_db:purchase_service_picking.view_service_picking_search +msgid "Backorders" +msgstr "Entregas parciales" + +#. module: purchase_service_picking +#: model_terms:ir.ui.view,arch_db:purchase_service_picking.view_immediate_transfer +#: model_terms:ir.ui.view,arch_db:purchase_service_picking.view_service_picking_form +#: model_terms:ir.ui.view,arch_db:purchase_service_picking.view_service_return_picking_form +msgid "Cancel" +msgstr "Cancelar" + +#. module: purchase_service_picking +#: model:ir.model.fields.selection,name:purchase_service_picking.selection__service_picking__state__cancel +#: model_terms:ir.ui.view,arch_db:purchase_service_picking.view_service_picking_search +msgid "Cancelled" +msgstr "Cancelado" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking__company_id +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking_line__company_id +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_return_picking__company_id +msgid "Company" +msgstr "Compañía" + +#. module: purchase_service_picking +#: model_terms:ir.ui.view,arch_db:purchase_service_picking.view_service_picking_form +msgid "Confirm" +msgstr "Confirmar" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking__partner_id +#: model_terms:ir.ui.view,arch_db:purchase_service_picking.view_service_picking_form +msgid "Contact" +msgstr "Contacto" + +#. module: purchase_service_picking +#: model_terms:ir.ui.view,arch_db:purchase_service_picking.view_backorder_confirmation +msgid "Create Backorder" +msgstr "Crear entrega parcial" + +#. module: purchase_service_picking +#: code:addons/purchase_service_picking/models/service_picking.py:0 +#, python-format +msgid "Create Backorder?" +msgstr "¿Crear entrega parcial?" + +#. module: purchase_service_picking +#: model_terms:ir.ui.view,arch_db:purchase_service_picking.view_backorder_confirmation +msgid "" +"Create a backorder if you expect to process the remaining\n" +" services later. Do not create a backorder if you " +"will not\n" +" process the remaining services." +msgstr "" +"Cree un pedido en espera si espera procesar los productos restantes más " +"adelante. No cree un pedido pendiente si no procesará los productos " +"restantes." + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_backorder_confirmation__create_uid +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_backorder_confirmation_line__create_uid +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_immediate_transfer__create_uid +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_immediate_transfer_line__create_uid +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking__create_uid +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking_line__create_uid +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_return_picking__create_uid +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_return_picking_line__create_uid +msgid "Created by" +msgstr "Creado por" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_backorder_confirmation__create_date +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_backorder_confirmation_line__create_date +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_immediate_transfer__create_date +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_immediate_transfer_line__create_date +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking__create_date +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking_line__create_date +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_return_picking__create_date +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_return_picking_line__create_date +msgid "Created on" +msgstr "Creado el" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking__date +msgid "Creation Date" +msgstr "Fecha de Creación" + +#. module: purchase_service_picking +#: model:ir.model.fields,help:purchase_service_picking.field_service_picking__date +msgid "Creation Date, usually the time of the order" +msgstr "Fecha de creación, usualmente la del pedido" + +#. module: purchase_service_picking +#: model:ir.model.fields,help:purchase_service_picking.field_service_picking__date_deadline +#: model:ir.model.fields,help:purchase_service_picking.field_service_picking_line__date_deadline +msgid "Date Promise to the customer on the top level document (SO/PO)" +msgstr "Fecha Promesa al cliente en el documento de nivel superior (SO/PO)" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking_line__date +msgid "Date Scheduled" +msgstr "Fecha programada" + +#. module: purchase_service_picking +#: model:ir.model.fields,help:purchase_service_picking.field_service_picking__date_done +msgid "Date at which the transfer has been processed or cancelled." +msgstr "Fecha en la que la transferencia ha sido procesada o cancelada." + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking__date_done +msgid "Date of Transfer" +msgstr "Fecha de transferencia" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking__date_deadline +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking_line__date_deadline +msgid "Deadline" +msgstr "Fecha límite" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking_line__product_qty +msgid "Demand" +msgstr "Demanda" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking_line__name +msgid "Description" +msgstr "Descripción" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_purchase_order__display_name +#: model:ir.model.fields,field_description:purchase_service_picking.field_purchase_order_line__display_name +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_backorder_confirmation__display_name +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_backorder_confirmation_line__display_name +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_immediate_transfer__display_name +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_immediate_transfer_line__display_name +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking__display_name +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking_line__display_name +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_return_picking__display_name +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_return_picking_line__display_name +msgid "Display Name" +msgstr "Nombre mostrado" + +#. module: purchase_service_picking +#: model:ir.model.fields.selection,name:purchase_service_picking.selection__service_picking__state__done +#: model_terms:ir.ui.view,arch_db:purchase_service_picking.view_service_picking_form +#: model_terms:ir.ui.view,arch_db:purchase_service_picking.view_service_picking_search +msgid "Done" +msgstr "Hecho" + +#. module: purchase_service_picking +#: model:ir.model.fields.selection,name:purchase_service_picking.selection__service_picking__state__draft +#: model_terms:ir.ui.view,arch_db:purchase_service_picking.view_service_picking_search +msgid "Draft" +msgstr "Borrador" + +#. module: purchase_service_picking +#: model_terms:ir.ui.view,arch_db:purchase_service_picking.view_service_picking_form +msgid "Effective Date" +msgstr "Fecha efectiva" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking__message_follower_ids +msgid "Followers" +msgstr "Seguidores" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking__message_channel_ids +msgid "Followers (Channels)" +msgstr "Seguidores (Canales)" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking__message_partner_ids +msgid "Followers (Partners)" +msgstr "Seguidores (Contactos)" + +#. module: purchase_service_picking +#: model:ir.model.fields,help:purchase_service_picking.field_service_picking__activity_type_icon +msgid "Font awesome icon e.g. fa-tasks" +msgstr "Icono de Font Awesome ej. fa-tasks" + +#. module: purchase_service_picking +#: model_terms:ir.ui.view,arch_db:purchase_service_picking.view_service_picking_search +msgid "Group By" +msgstr "Agrupar por" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_purchase_order__id +#: model:ir.model.fields,field_description:purchase_service_picking.field_purchase_order_line__id +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_backorder_confirmation__id +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_backorder_confirmation_line__id +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_immediate_transfer__id +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_immediate_transfer_line__id +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking__id +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking_line__id +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_return_picking__id +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_return_picking_line__id +msgid "ID" +msgstr "ID" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking__activity_exception_icon +msgid "Icon" +msgstr "Icono" + +#. module: purchase_service_picking +#: model:ir.model.fields,help:purchase_service_picking.field_service_picking__activity_exception_icon +msgid "Icon to indicate an exception activity." +msgstr "Icono para indicar una actividad de excepción." + +#. module: purchase_service_picking +#: model:ir.model.fields,help:purchase_service_picking.field_service_picking__message_needaction +#: model:ir.model.fields,help:purchase_service_picking.field_service_picking__message_unread +msgid "If checked, new messages require your attention." +msgstr "Si está marcado hay nuevos mensajes que requieren su atención." + +#. module: purchase_service_picking +#: model:ir.model.fields,help:purchase_service_picking.field_service_picking__message_has_error +msgid "If checked, some messages have a delivery error." +msgstr "Si se encuentra marcado, algunos mensajes tienen error de envío." + +#. module: purchase_service_picking +#: model:ir.model,name:purchase_service_picking.model_service_immediate_transfer +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_backorder_confirmation_line__backorder_confirmation_id +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_immediate_transfer_line__immediate_transfer_id +msgid "Immediate Transfer" +msgstr "Transferencia inmediata" + +#. module: purchase_service_picking +#: model:ir.model,name:purchase_service_picking.model_service_immediate_transfer_line +msgid "Immediate Transfer Line" +msgstr "Línea de transferencia inmediata" + +#. module: purchase_service_picking +#: code:addons/purchase_service_picking/models/service_picking.py:0 +#, python-format +msgid "Immediate Transfer?" +msgstr "¿Transferencia inmediata?" + +#. module: purchase_service_picking +#: model_terms:ir.ui.view,arch_db:purchase_service_picking.view_immediate_transfer +msgid "Immediate transfer?" +msgstr "¿Transferencia inmediata?" + +#. module: purchase_service_picking +#: model:ir.model.fields.selection,name:purchase_service_picking.selection__service_picking__state__in_progress +#: model_terms:ir.ui.view,arch_db:purchase_service_picking.view_service_picking_search +msgid "In progress" +msgstr "En progreso" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking__message_is_follower +msgid "Is Follower" +msgstr "Es un seguidor" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_purchase_order____last_update +#: model:ir.model.fields,field_description:purchase_service_picking.field_purchase_order_line____last_update +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_backorder_confirmation____last_update +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_backorder_confirmation_line____last_update +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_immediate_transfer____last_update +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_immediate_transfer_line____last_update +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking____last_update +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking_line____last_update +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_return_picking____last_update +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_return_picking_line____last_update +msgid "Last Modified on" +msgstr "Última modificación el" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_backorder_confirmation__write_uid +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_backorder_confirmation_line__write_uid +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_immediate_transfer__write_uid +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_immediate_transfer_line__write_uid +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking__write_uid +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking_line__write_uid +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_return_picking__write_uid +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_return_picking_line__write_uid +msgid "Last Updated by" +msgstr "Última actualización por" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_backorder_confirmation__write_date +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_backorder_confirmation_line__write_date +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_immediate_transfer__write_date +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_immediate_transfer_line__write_date +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking__write_date +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking_line__write_date +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_return_picking__write_date +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_return_picking_line__write_date +msgid "Last Updated on" +msgstr "Última actualización el" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_return_picking_line__line_id +msgid "Line" +msgstr "Línea" + +#. module: purchase_service_picking +#: model:ir.model.fields,help:purchase_service_picking.field_service_picking_line__origin_returned_line_id +msgid "Line that created the return line" +msgstr "" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_backorder_confirmation__line_ids +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_immediate_transfer__line_ids +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking__line_ids +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_return_picking__product_return_lines +#: model_terms:ir.ui.view,arch_db:purchase_service_picking.view_service_picking_form +msgid "Lines" +msgstr "Líneas" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking__message_main_attachment_id +msgid "Main Attachment" +msgstr "Adjuntos principales" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking__message_has_error +msgid "Message Delivery error" +msgstr "Error de Envío de Mensaje" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking__message_ids +msgid "Messages" +msgstr "Mensajes" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking__my_activity_date_deadline +msgid "My Activity Deadline" +msgstr "Mi fecha límite de actividad" + +#. module: purchase_service_picking +#: model_terms:ir.ui.view,arch_db:purchase_service_picking.view_service_picking_search +msgid "My Transfers" +msgstr "Mis transferencias" + +#. module: purchase_service_picking +#: code:addons/purchase_service_picking/models/service_picking.py:0 +#, python-format +msgid "New" +msgstr "Nuevo" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking__activity_date_deadline +msgid "Next Activity Deadline" +msgstr "Siguiente plazo de actividad" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking__activity_summary +msgid "Next Activity Summary" +msgstr "Resumen de la siguiente actividad" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking__activity_type_id +msgid "Next Activity Type" +msgstr "Siguiente tipo de actividad" + +#. module: purchase_service_picking +#: model_terms:ir.ui.view,arch_db:purchase_service_picking.view_backorder_confirmation +msgid "No Backorder" +msgstr "No crear entrega parcial" + +#. module: purchase_service_picking +#: code:addons/purchase_service_picking/wizards/service_picking_return.py:0 +#, python-format +msgid "" +"No products to return (only lines in Done state and not fully returned yet " +"can be returned)." +msgstr "" +"No hay productos a devolver (sólo las líneas en estado realizado y no " +"totalmente devueltas puede sen devueltas)" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking__message_needaction_counter +msgid "Number of Actions" +msgstr "Número de acciones" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking__message_has_error_counter +msgid "Number of errors" +msgstr "Número de errores" + +#. module: purchase_service_picking +#: model:ir.model.fields,help:purchase_service_picking.field_service_picking__message_needaction_counter +msgid "Number of messages which requires an action" +msgstr "Número de mensajes que requieren una acción" + +#. module: purchase_service_picking +#: model:ir.model.fields,help:purchase_service_picking.field_service_picking__message_has_error_counter +msgid "Number of messages with delivery error" +msgstr "Número de mensajes con error de envío" + +#. module: purchase_service_picking +#: model:ir.model.fields,help:purchase_service_picking.field_service_picking__message_unread_counter +msgid "Number of unread messages" +msgstr "Número de mensajes no leidos" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking_line__origin_returned_line_id +msgid "Origin return line" +msgstr "" + +#. module: purchase_service_picking +#: model_terms:ir.ui.view,arch_db:purchase_service_picking.view_service_picking_form +msgid "Other Information" +msgstr "Otra información" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_backorder_confirmation__picking_ids +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_immediate_transfer__picking_ids +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking_line__picking_id +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_return_picking__picking_id +msgid "Picking" +msgstr "Albarán" + +#. module: purchase_service_picking +#: code:addons/purchase_service_picking/wizards/service_picking_return.py:0 +#, python-format +msgid "Please specify at least one non-zero quantity." +msgstr "Por favor, especifique al menos una cantidad que no sea cero." + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking_line__product_id +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_return_picking_line__product_id +msgid "Product" +msgstr "Producto" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking_line__purchase_line_id +msgid "Purchase Line" +msgstr "Línea de compra" + +#. module: purchase_service_picking +#: model:ir.model,name:purchase_service_picking.model_purchase_order +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking__purchase_id +msgid "Purchase Order" +msgstr "Pedido de compra" + +#. module: purchase_service_picking +#: model:ir.model,name:purchase_service_picking.model_purchase_order_line +msgid "Purchase Order Line" +msgstr "Línea de pedido de compra" + +#. module: purchase_service_picking +#: model_terms:ir.ui.view,arch_db:purchase_service_picking.view_service_picking_form +msgid "Qty" +msgstr "Cant." + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_return_picking_line__quantity +msgid "Quantity" +msgstr "Cantidad" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking_line__quantity_done +msgid "Quantity Done" +msgstr "Cantidad hecha" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking__name +msgid "Reference" +msgstr "Referencia" + +#. module: purchase_service_picking +#: model:ir.model.constraint,message:purchase_service_picking.constraint_service_picking_name_uniq +msgid "Reference must be unique per company!" +msgstr "¡La referencia debe ser única por compañía!" + +#. module: purchase_service_picking +#: model:ir.model.fields,help:purchase_service_picking.field_service_picking__origin +msgid "Reference of the document" +msgstr "Referencia del documento" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking__user_id +msgid "Responsible" +msgstr "Responsable" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking__activity_user_id +msgid "Responsible User" +msgstr "Usuario responsable" + +#. module: purchase_service_picking +#: model_terms:ir.ui.view,arch_db:purchase_service_picking.view_service_picking_form +#: model_terms:ir.ui.view,arch_db:purchase_service_picking.view_service_return_picking_form +msgid "Return" +msgstr "Devolver" + +#. module: purchase_service_picking +#: code:addons/purchase_service_picking/wizards/service_picking_return.py:0 +#, python-format +msgid "Return of %s" +msgstr "Retorno de %s" + +#. module: purchase_service_picking +#: code:addons/purchase_service_picking/wizards/service_picking_return.py:0 +#, python-format +msgid "Returned Picking" +msgstr "Albarán devuelto" + +#. module: purchase_service_picking +#: model:ir.actions.act_window,name:purchase_service_picking.act_service_return_picking +msgid "Reverse Service Picking" +msgstr "" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking__scheduled_date +#: model_terms:ir.ui.view,arch_db:purchase_service_picking.view_service_picking_search +msgid "Scheduled Date" +msgstr "Fecha prevista" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking_line__sequence +msgid "Sequence" +msgstr "Secuencia" + +#. module: purchase_service_picking +#: model:ir.model,name:purchase_service_picking.model_service_picking +#: model_terms:ir.ui.view,arch_db:purchase_service_picking.purchase_order_view_form +msgid "Service Picking" +msgstr "Albarán de servicio" + +#. module: purchase_service_picking +#: model:ir.model,name:purchase_service_picking.model_service_picking_line +msgid "Service Picking Line" +msgstr "" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_purchase_order__service_picking_count +msgid "Service Picking count" +msgstr "" + +#. module: purchase_service_picking +#: model:ir.actions.act_window,name:purchase_service_picking.action_service_picking_tree_all +msgid "Service Pickings" +msgstr "Albaranes de servicio" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_purchase_order__service_picking_ids +msgid "Service Receptions" +msgstr "" + +#. module: purchase_service_picking +#: model:ir.model,name:purchase_service_picking.model_service_return_picking +msgid "Service Return Picking" +msgstr "" + +#. module: purchase_service_picking +#: model:ir.model,name:purchase_service_picking.model_service_return_picking_line +msgid "Service Return Picking Line" +msgstr "" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_purchase_order_line__service_picking_line_ids +msgid "Service picking lines" +msgstr "" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking__origin +#: model_terms:ir.ui.view,arch_db:purchase_service_picking.view_service_picking_search +msgid "Source Document" +msgstr "Documento origen" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking__state +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking_line__state +#: model_terms:ir.ui.view,arch_db:purchase_service_picking.view_service_picking_search +msgid "Status" +msgstr "Estado" + +#. module: purchase_service_picking +#: model:ir.model.fields,help:purchase_service_picking.field_service_picking__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: purchase_service_picking +#: code:addons/purchase_service_picking/models/service_picking.py:0 +#, python-format +msgid "" +"The backorder %s " +"has been created." +msgstr "" +"El pedido pendiente %s ha sido creada." + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_backorder_confirmation_line__to_backorder +msgid "To Backorder" +msgstr "Para hacer un pedido pendiente" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_immediate_transfer_line__to_immediate +msgid "To Process" +msgstr "A Procesar" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_backorder_confirmation_line__picking_id +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_immediate_transfer_line__picking_id +#: model_terms:ir.ui.view,arch_db:purchase_service_picking.view_service_picking_search +msgid "Transfer" +msgstr "Transferir" + +#. module: purchase_service_picking +#: model:ir.model.fields,help:purchase_service_picking.field_service_picking__activity_exception_decoration +msgid "Type of the exception activity on record." +msgstr "Tipo de actividad de excepción registrada." + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking_line__product_uom_id +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_return_picking_line__uom_id +#: model_terms:ir.ui.view,arch_db:purchase_service_picking.view_service_picking_form +msgid "Unit of Measure" +msgstr "Unidad de medida" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking__message_unread +msgid "Unread Messages" +msgstr "Mensajes sin leer" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking__message_unread_counter +msgid "Unread Messages Counter" +msgstr "Nº de mensajes sin leer" + +#. module: purchase_service_picking +#: model_terms:ir.ui.view,arch_db:purchase_service_picking.view_service_picking_form +msgid "Validate" +msgstr "Validar" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking__website_message_ids +msgid "Website Messages" +msgstr "Mensajes del sitio web" + +#. module: purchase_service_picking +#: model:ir.model.fields,help:purchase_service_picking.field_service_picking__website_message_ids +msgid "Website communication history" +msgstr "Historial de comunicaciones del sitio web" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_return_picking_line__wizard_id +msgid "Wizard" +msgstr "Asistente" + +#. module: purchase_service_picking +#: code:addons/purchase_service_picking/models/service_picking.py:0 +#, python-format +msgid "You cannot change the Scheduled Date on a done or cancelled transfer." +msgstr "" +"No puede cambiar la Fecha programada en una transferencia realizada o " +"cancelada." + +#. module: purchase_service_picking +#: code:addons/purchase_service_picking/models/purchase_order.py:0 +#, python-format +msgid "" +"You cannot decrease the ordered quantity below the received quantity.\n" +"Create a return first." +msgstr "" +"No puede disminuir la cantidad pedida por debajo de la cantidad recibida.\n" +"Crea un retorno primero." + +#. module: purchase_service_picking +#: code:addons/purchase_service_picking/wizards/service_picking_return.py:0 +#, python-format +msgid "You cannot set a quantity greater than the quantity done." +msgstr "No puede definir una cantidad mayor a la cantidad hecha." + +#. module: purchase_service_picking +#: model_terms:ir.ui.view,arch_db:purchase_service_picking.view_immediate_transfer +msgid "" +"You have not recorded done quantities yet, by clicking on apply Odoo will process all the quantities." +msgstr "" +"Aún no ha registrado las cantidades realizadas , al hacer clic en " +"Aplicar, Odoo procesará todas las cantidades." + +#. module: purchase_service_picking +#: model_terms:ir.ui.view,arch_db:purchase_service_picking.view_backorder_confirmation +msgid "You have processed less services than the initial demand." +msgstr "Ha procesado menos servicios que la demanda inicial." + +#. module: purchase_service_picking +#: code:addons/purchase_service_picking/wizards/service_picking_return.py:0 +#, python-format +msgid "You may only return Done pickings." +msgstr "Solo puede devolver las recolecciones realizadas." + +#. module: purchase_service_picking +#: code:addons/purchase_service_picking/wizards/service_picking_return.py:0 +#, python-format +msgid "You may only return one picking at a time." +msgstr "Sólo puede devolver un albarán a la vez." + +#. module: purchase_service_picking +#: model_terms:ir.ui.view,arch_db:purchase_service_picking.view_backorder_confirmation +msgid "_Cancel" +msgstr "_Cancelar" + +#. module: purchase_service_picking +#: model_terms:ir.ui.view,arch_db:purchase_service_picking.view_service_picking_form +msgid "e.g. PO0032" +msgstr "Por ejemplo, PO0032" diff --git a/purchase_service_picking/i18n/purchase_service_picking.pot b/purchase_service_picking/i18n/purchase_service_picking.pot new file mode 100644 index 00000000000..5fdfb9bed28 --- /dev/null +++ b/purchase_service_picking/i18n/purchase_service_picking.pot @@ -0,0 +1,783 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * purchase_service_picking +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 14.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2025-01-14 12:14+0000\n" +"PO-Revision-Date: 2025-01-14 12:14+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: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking__message_needaction +msgid "Action Needed" +msgstr "" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking__activity_ids +msgid "Activities" +msgstr "" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking__activity_exception_decoration +msgid "Activity Exception Decoration" +msgstr "" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking__activity_state +msgid "Activity State" +msgstr "" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking__activity_type_icon +msgid "Activity Type Icon" +msgstr "" + +#. module: purchase_service_picking +#: model_terms:ir.ui.view,arch_db:purchase_service_picking.view_service_picking_form +msgid "Additional Info" +msgstr "" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking_line__returned_line_ids +msgid "All returned lines" +msgstr "" + +#. module: purchase_service_picking +#: model_terms:ir.ui.view,arch_db:purchase_service_picking.view_immediate_transfer +msgid "Apply" +msgstr "" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking__message_attachment_count +msgid "Attachment Count" +msgstr "" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking__backorder_id +msgid "Back Order of" +msgstr "" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking__backorder_ids +msgid "Back Orders" +msgstr "" + +#. module: purchase_service_picking +#: model:ir.model,name:purchase_service_picking.model_service_backorder_confirmation +msgid "Backorder Confirmation" +msgstr "" + +#. module: purchase_service_picking +#: model:ir.model,name:purchase_service_picking.model_service_backorder_confirmation_line +msgid "Backorder Confirmation Line" +msgstr "" + +#. module: purchase_service_picking +#: model_terms:ir.ui.view,arch_db:purchase_service_picking.view_backorder_confirmation +msgid "Backorder creation" +msgstr "" + +#. module: purchase_service_picking +#: model_terms:ir.ui.view,arch_db:purchase_service_picking.view_service_picking_search +msgid "Backorders" +msgstr "" + +#. module: purchase_service_picking +#: model_terms:ir.ui.view,arch_db:purchase_service_picking.view_immediate_transfer +#: model_terms:ir.ui.view,arch_db:purchase_service_picking.view_service_picking_form +#: model_terms:ir.ui.view,arch_db:purchase_service_picking.view_service_return_picking_form +msgid "Cancel" +msgstr "" + +#. module: purchase_service_picking +#: model:ir.model.fields.selection,name:purchase_service_picking.selection__service_picking__state__cancel +#: model_terms:ir.ui.view,arch_db:purchase_service_picking.view_service_picking_search +msgid "Cancelled" +msgstr "" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking__company_id +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking_line__company_id +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_return_picking__company_id +msgid "Company" +msgstr "" + +#. module: purchase_service_picking +#: model_terms:ir.ui.view,arch_db:purchase_service_picking.view_service_picking_form +msgid "Confirm" +msgstr "" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking__partner_id +#: model_terms:ir.ui.view,arch_db:purchase_service_picking.view_service_picking_form +msgid "Contact" +msgstr "" + +#. module: purchase_service_picking +#: model_terms:ir.ui.view,arch_db:purchase_service_picking.view_backorder_confirmation +msgid "Create Backorder" +msgstr "" + +#. module: purchase_service_picking +#: code:addons/purchase_service_picking/models/service_picking.py:0 +#, python-format +msgid "Create Backorder?" +msgstr "" + +#. module: purchase_service_picking +#: model_terms:ir.ui.view,arch_db:purchase_service_picking.view_backorder_confirmation +msgid "" +"Create a backorder if you expect to process the remaining\n" +" services later. Do not create a backorder if you will not\n" +" process the remaining services." +msgstr "" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_backorder_confirmation__create_uid +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_backorder_confirmation_line__create_uid +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_immediate_transfer__create_uid +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_immediate_transfer_line__create_uid +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking__create_uid +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking_line__create_uid +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_return_picking__create_uid +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_return_picking_line__create_uid +msgid "Created by" +msgstr "" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_backorder_confirmation__create_date +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_backorder_confirmation_line__create_date +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_immediate_transfer__create_date +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_immediate_transfer_line__create_date +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking__create_date +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking_line__create_date +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_return_picking__create_date +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_return_picking_line__create_date +msgid "Created on" +msgstr "" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking__date +msgid "Creation Date" +msgstr "" + +#. module: purchase_service_picking +#: model:ir.model.fields,help:purchase_service_picking.field_service_picking__date +msgid "Creation Date, usually the time of the order" +msgstr "" + +#. module: purchase_service_picking +#: model:ir.model.fields,help:purchase_service_picking.field_service_picking__date_deadline +#: model:ir.model.fields,help:purchase_service_picking.field_service_picking_line__date_deadline +msgid "Date Promise to the customer on the top level document (SO/PO)" +msgstr "" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking_line__date +msgid "Date Scheduled" +msgstr "" + +#. module: purchase_service_picking +#: model:ir.model.fields,help:purchase_service_picking.field_service_picking__date_done +msgid "Date at which the transfer has been processed or cancelled." +msgstr "" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking__date_done +msgid "Date of Transfer" +msgstr "" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking__date_deadline +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking_line__date_deadline +msgid "Deadline" +msgstr "" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking_line__product_qty +msgid "Demand" +msgstr "" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking_line__name +msgid "Description" +msgstr "" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_purchase_order__display_name +#: model:ir.model.fields,field_description:purchase_service_picking.field_purchase_order_line__display_name +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_backorder_confirmation__display_name +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_backorder_confirmation_line__display_name +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_immediate_transfer__display_name +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_immediate_transfer_line__display_name +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking__display_name +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking_line__display_name +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_return_picking__display_name +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_return_picking_line__display_name +msgid "Display Name" +msgstr "" + +#. module: purchase_service_picking +#: model:ir.model.fields.selection,name:purchase_service_picking.selection__service_picking__state__done +#: model_terms:ir.ui.view,arch_db:purchase_service_picking.view_service_picking_form +#: model_terms:ir.ui.view,arch_db:purchase_service_picking.view_service_picking_search +msgid "Done" +msgstr "" + +#. module: purchase_service_picking +#: model:ir.model.fields.selection,name:purchase_service_picking.selection__service_picking__state__draft +#: model_terms:ir.ui.view,arch_db:purchase_service_picking.view_service_picking_search +msgid "Draft" +msgstr "" + +#. module: purchase_service_picking +#: model_terms:ir.ui.view,arch_db:purchase_service_picking.view_service_picking_form +msgid "Effective Date" +msgstr "" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking__message_follower_ids +msgid "Followers" +msgstr "" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking__message_channel_ids +msgid "Followers (Channels)" +msgstr "" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking__message_partner_ids +msgid "Followers (Partners)" +msgstr "" + +#. module: purchase_service_picking +#: model:ir.model.fields,help:purchase_service_picking.field_service_picking__activity_type_icon +msgid "Font awesome icon e.g. fa-tasks" +msgstr "" + +#. module: purchase_service_picking +#: model_terms:ir.ui.view,arch_db:purchase_service_picking.view_service_picking_search +msgid "Group By" +msgstr "" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_purchase_order__id +#: model:ir.model.fields,field_description:purchase_service_picking.field_purchase_order_line__id +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_backorder_confirmation__id +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_backorder_confirmation_line__id +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_immediate_transfer__id +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_immediate_transfer_line__id +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking__id +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking_line__id +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_return_picking__id +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_return_picking_line__id +msgid "ID" +msgstr "" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking__activity_exception_icon +msgid "Icon" +msgstr "" + +#. module: purchase_service_picking +#: model:ir.model.fields,help:purchase_service_picking.field_service_picking__activity_exception_icon +msgid "Icon to indicate an exception activity." +msgstr "" + +#. module: purchase_service_picking +#: model:ir.model.fields,help:purchase_service_picking.field_service_picking__message_needaction +#: model:ir.model.fields,help:purchase_service_picking.field_service_picking__message_unread +msgid "If checked, new messages require your attention." +msgstr "" + +#. module: purchase_service_picking +#: model:ir.model.fields,help:purchase_service_picking.field_service_picking__message_has_error +msgid "If checked, some messages have a delivery error." +msgstr "" + +#. module: purchase_service_picking +#: model:ir.model,name:purchase_service_picking.model_service_immediate_transfer +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_backorder_confirmation_line__backorder_confirmation_id +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_immediate_transfer_line__immediate_transfer_id +msgid "Immediate Transfer" +msgstr "" + +#. module: purchase_service_picking +#: model:ir.model,name:purchase_service_picking.model_service_immediate_transfer_line +msgid "Immediate Transfer Line" +msgstr "" + +#. module: purchase_service_picking +#: code:addons/purchase_service_picking/models/service_picking.py:0 +#, python-format +msgid "Immediate Transfer?" +msgstr "" + +#. module: purchase_service_picking +#: model_terms:ir.ui.view,arch_db:purchase_service_picking.view_immediate_transfer +msgid "Immediate transfer?" +msgstr "" + +#. module: purchase_service_picking +#: model:ir.model.fields.selection,name:purchase_service_picking.selection__service_picking__state__in_progress +#: model_terms:ir.ui.view,arch_db:purchase_service_picking.view_service_picking_search +msgid "In progress" +msgstr "" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking__message_is_follower +msgid "Is Follower" +msgstr "" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_purchase_order____last_update +#: model:ir.model.fields,field_description:purchase_service_picking.field_purchase_order_line____last_update +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_backorder_confirmation____last_update +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_backorder_confirmation_line____last_update +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_immediate_transfer____last_update +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_immediate_transfer_line____last_update +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking____last_update +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking_line____last_update +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_return_picking____last_update +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_return_picking_line____last_update +msgid "Last Modified on" +msgstr "" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_backorder_confirmation__write_uid +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_backorder_confirmation_line__write_uid +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_immediate_transfer__write_uid +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_immediate_transfer_line__write_uid +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking__write_uid +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking_line__write_uid +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_return_picking__write_uid +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_return_picking_line__write_uid +msgid "Last Updated by" +msgstr "" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_backorder_confirmation__write_date +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_backorder_confirmation_line__write_date +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_immediate_transfer__write_date +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_immediate_transfer_line__write_date +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking__write_date +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking_line__write_date +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_return_picking__write_date +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_return_picking_line__write_date +msgid "Last Updated on" +msgstr "" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_return_picking_line__line_id +msgid "Line" +msgstr "" + +#. module: purchase_service_picking +#: model:ir.model.fields,help:purchase_service_picking.field_service_picking_line__origin_returned_line_id +msgid "Line that created the return line" +msgstr "" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_backorder_confirmation__line_ids +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_immediate_transfer__line_ids +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking__line_ids +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_return_picking__product_return_lines +#: model_terms:ir.ui.view,arch_db:purchase_service_picking.view_service_picking_form +msgid "Lines" +msgstr "" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking__message_main_attachment_id +msgid "Main Attachment" +msgstr "" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking__message_has_error +msgid "Message Delivery error" +msgstr "" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking__message_ids +msgid "Messages" +msgstr "" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking__my_activity_date_deadline +msgid "My Activity Deadline" +msgstr "" + +#. module: purchase_service_picking +#: model_terms:ir.ui.view,arch_db:purchase_service_picking.view_service_picking_search +msgid "My Transfers" +msgstr "" + +#. module: purchase_service_picking +#: code:addons/purchase_service_picking/models/service_picking.py:0 +#, python-format +msgid "New" +msgstr "" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking__activity_date_deadline +msgid "Next Activity Deadline" +msgstr "" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking__activity_summary +msgid "Next Activity Summary" +msgstr "" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking__activity_type_id +msgid "Next Activity Type" +msgstr "" + +#. module: purchase_service_picking +#: model_terms:ir.ui.view,arch_db:purchase_service_picking.view_backorder_confirmation +msgid "No Backorder" +msgstr "" + +#. module: purchase_service_picking +#: code:addons/purchase_service_picking/wizards/service_picking_return.py:0 +#, python-format +msgid "" +"No products to return (only lines in Done state and not fully returned yet " +"can be returned)." +msgstr "" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking__message_needaction_counter +msgid "Number of Actions" +msgstr "" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking__message_has_error_counter +msgid "Number of errors" +msgstr "" + +#. module: purchase_service_picking +#: model:ir.model.fields,help:purchase_service_picking.field_service_picking__message_needaction_counter +msgid "Number of messages which requires an action" +msgstr "" + +#. module: purchase_service_picking +#: model:ir.model.fields,help:purchase_service_picking.field_service_picking__message_has_error_counter +msgid "Number of messages with delivery error" +msgstr "" + +#. module: purchase_service_picking +#: model:ir.model.fields,help:purchase_service_picking.field_service_picking__message_unread_counter +msgid "Number of unread messages" +msgstr "" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking_line__origin_returned_line_id +msgid "Origin return line" +msgstr "" + +#. module: purchase_service_picking +#: model_terms:ir.ui.view,arch_db:purchase_service_picking.view_service_picking_form +msgid "Other Information" +msgstr "" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_backorder_confirmation__picking_ids +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_immediate_transfer__picking_ids +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking_line__picking_id +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_return_picking__picking_id +msgid "Picking" +msgstr "" + +#. module: purchase_service_picking +#: code:addons/purchase_service_picking/wizards/service_picking_return.py:0 +#, python-format +msgid "Please specify at least one non-zero quantity." +msgstr "" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking_line__product_id +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_return_picking_line__product_id +msgid "Product" +msgstr "" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking_line__purchase_line_id +msgid "Purchase Line" +msgstr "" + +#. module: purchase_service_picking +#: model:ir.model,name:purchase_service_picking.model_purchase_order +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking__purchase_id +msgid "Purchase Order" +msgstr "" + +#. module: purchase_service_picking +#: model:ir.model,name:purchase_service_picking.model_purchase_order_line +msgid "Purchase Order Line" +msgstr "" + +#. module: purchase_service_picking +#: model_terms:ir.ui.view,arch_db:purchase_service_picking.view_service_picking_form +msgid "Qty" +msgstr "" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_return_picking_line__quantity +msgid "Quantity" +msgstr "" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking_line__quantity_done +msgid "Quantity Done" +msgstr "" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking__name +msgid "Reference" +msgstr "" + +#. module: purchase_service_picking +#: model:ir.model.constraint,message:purchase_service_picking.constraint_service_picking_name_uniq +msgid "Reference must be unique per company!" +msgstr "" + +#. module: purchase_service_picking +#: model:ir.model.fields,help:purchase_service_picking.field_service_picking__origin +msgid "Reference of the document" +msgstr "" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking__user_id +msgid "Responsible" +msgstr "" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking__activity_user_id +msgid "Responsible User" +msgstr "" + +#. module: purchase_service_picking +#: model_terms:ir.ui.view,arch_db:purchase_service_picking.view_service_picking_form +#: model_terms:ir.ui.view,arch_db:purchase_service_picking.view_service_return_picking_form +msgid "Return" +msgstr "" + +#. module: purchase_service_picking +#: code:addons/purchase_service_picking/wizards/service_picking_return.py:0 +#, python-format +msgid "Return of %s" +msgstr "" + +#. module: purchase_service_picking +#: code:addons/purchase_service_picking/wizards/service_picking_return.py:0 +#, python-format +msgid "Returned Picking" +msgstr "" + +#. module: purchase_service_picking +#: model:ir.actions.act_window,name:purchase_service_picking.act_service_return_picking +msgid "Reverse Service Picking" +msgstr "" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking__scheduled_date +#: model_terms:ir.ui.view,arch_db:purchase_service_picking.view_service_picking_search +msgid "Scheduled Date" +msgstr "" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking_line__sequence +msgid "Sequence" +msgstr "" + +#. module: purchase_service_picking +#: model:ir.model,name:purchase_service_picking.model_service_picking +#: model_terms:ir.ui.view,arch_db:purchase_service_picking.purchase_order_view_form +msgid "Service Picking" +msgstr "" + +#. module: purchase_service_picking +#: model:ir.model,name:purchase_service_picking.model_service_picking_line +msgid "Service Picking Line" +msgstr "" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_purchase_order__service_picking_count +msgid "Service Picking count" +msgstr "" + +#. module: purchase_service_picking +#: model:ir.actions.act_window,name:purchase_service_picking.action_service_picking_tree_all +msgid "Service Pickings" +msgstr "" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_purchase_order__service_picking_ids +msgid "Service Receptions" +msgstr "" + +#. module: purchase_service_picking +#: model:ir.model,name:purchase_service_picking.model_service_return_picking +msgid "Service Return Picking" +msgstr "" + +#. module: purchase_service_picking +#: model:ir.model,name:purchase_service_picking.model_service_return_picking_line +msgid "Service Return Picking Line" +msgstr "" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_purchase_order_line__service_picking_line_ids +msgid "Service picking lines" +msgstr "" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking__origin +#: model_terms:ir.ui.view,arch_db:purchase_service_picking.view_service_picking_search +msgid "Source Document" +msgstr "" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking__state +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking_line__state +#: model_terms:ir.ui.view,arch_db:purchase_service_picking.view_service_picking_search +msgid "Status" +msgstr "" + +#. module: purchase_service_picking +#: model:ir.model.fields,help:purchase_service_picking.field_service_picking__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: purchase_service_picking +#: code:addons/purchase_service_picking/models/service_picking.py:0 +#, python-format +msgid "" +"The backorder %s " +"has been created." +msgstr "" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_backorder_confirmation_line__to_backorder +msgid "To Backorder" +msgstr "" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_immediate_transfer_line__to_immediate +msgid "To Process" +msgstr "" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_backorder_confirmation_line__picking_id +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_immediate_transfer_line__picking_id +#: model_terms:ir.ui.view,arch_db:purchase_service_picking.view_service_picking_search +msgid "Transfer" +msgstr "" + +#. module: purchase_service_picking +#: model:ir.model.fields,help:purchase_service_picking.field_service_picking__activity_exception_decoration +msgid "Type of the exception activity on record." +msgstr "" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking_line__product_uom_id +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_return_picking_line__uom_id +#: model_terms:ir.ui.view,arch_db:purchase_service_picking.view_service_picking_form +msgid "Unit of Measure" +msgstr "" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking__message_unread +msgid "Unread Messages" +msgstr "" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking__message_unread_counter +msgid "Unread Messages Counter" +msgstr "" + +#. module: purchase_service_picking +#: model_terms:ir.ui.view,arch_db:purchase_service_picking.view_service_picking_form +msgid "Validate" +msgstr "" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_picking__website_message_ids +msgid "Website Messages" +msgstr "" + +#. module: purchase_service_picking +#: model:ir.model.fields,help:purchase_service_picking.field_service_picking__website_message_ids +msgid "Website communication history" +msgstr "" + +#. module: purchase_service_picking +#: model:ir.model.fields,field_description:purchase_service_picking.field_service_return_picking_line__wizard_id +msgid "Wizard" +msgstr "" + +#. module: purchase_service_picking +#: code:addons/purchase_service_picking/models/service_picking.py:0 +#, python-format +msgid "You cannot change the Scheduled Date on a done or cancelled transfer." +msgstr "" + +#. module: purchase_service_picking +#: code:addons/purchase_service_picking/models/purchase_order.py:0 +#, python-format +msgid "" +"You cannot decrease the ordered quantity below the received quantity.\n" +"Create a return first." +msgstr "" + +#. module: purchase_service_picking +#: code:addons/purchase_service_picking/wizards/service_picking_return.py:0 +#, python-format +msgid "You cannot set a quantity greater than the quantity done." +msgstr "" + +#. module: purchase_service_picking +#: model_terms:ir.ui.view,arch_db:purchase_service_picking.view_immediate_transfer +msgid "" +"You have not recorded done quantities yet, by clicking on " +"apply Odoo will process all the quantities." +msgstr "" + +#. module: purchase_service_picking +#: model_terms:ir.ui.view,arch_db:purchase_service_picking.view_backorder_confirmation +msgid "You have processed less services than the initial demand." +msgstr "" + +#. module: purchase_service_picking +#: code:addons/purchase_service_picking/wizards/service_picking_return.py:0 +#, python-format +msgid "You may only return Done pickings." +msgstr "" + +#. module: purchase_service_picking +#: code:addons/purchase_service_picking/wizards/service_picking_return.py:0 +#, python-format +msgid "You may only return one picking at a time." +msgstr "" + +#. module: purchase_service_picking +#: model_terms:ir.ui.view,arch_db:purchase_service_picking.view_backorder_confirmation +msgid "_Cancel" +msgstr "" + +#. module: purchase_service_picking +#: model_terms:ir.ui.view,arch_db:purchase_service_picking.view_service_picking_form +msgid "e.g. PO0032" +msgstr "" diff --git a/purchase_service_picking/models/__init__.py b/purchase_service_picking/models/__init__.py new file mode 100644 index 00000000000..2afd0f288ed --- /dev/null +++ b/purchase_service_picking/models/__init__.py @@ -0,0 +1,2 @@ +from . import purchase_order +from . import service_picking diff --git a/purchase_service_picking/models/purchase_order.py b/purchase_service_picking/models/purchase_order.py new file mode 100644 index 00000000000..0b685db1dc6 --- /dev/null +++ b/purchase_service_picking/models/purchase_order.py @@ -0,0 +1,250 @@ +# Copyright 2025 Tecnativa - Víctor Martínez +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl) +from odoo import SUPERUSER_ID, _, api, fields, models +from odoo.exceptions import UserError +from odoo.tools.float_utils import float_compare, float_is_zero + + +class PurchaseOrder(models.Model): + _inherit = "purchase.order" + + service_picking_count = fields.Integer( + compute="_compute_service_picking", + string="Service Picking count", + default=0, + store=True, + ) + service_picking_ids = fields.Many2many( + comodel_name="service.picking", + compute="_compute_service_picking", + string="Service Receptions", + copy=False, + store=True, + ) + + @api.depends("order_line.service_picking_line_ids.picking_id") + def _compute_service_picking(self): + for order in self: + pickings = order.order_line.mapped("service_picking_line_ids.picking_id") + order.service_picking_ids = pickings + order.service_picking_count = len(pickings) + + def _prepare_service_picking_vals(self): + return { + "partner_id": self.partner_id.id, + "user_id": False, + "date": self.date_order, + "origin": self.name, + "company_id": self.company_id.id, + "purchase_id": self.id, + "state": "in_progress", + } + + def _create_service_picking(self): + sp_model = self.env["service.picking"] + for order in self.filtered(lambda x: x.state in ("purchase", "done")): + if any(p.type == "service" for p in order.order_line.product_id): + order = order.with_company(order.company_id) + service_pickings = order.service_picking_ids.filtered( + lambda x: x.state not in ("done", "cancel") + ) + if not service_pickings: + picking = sp_model.with_user(SUPERUSER_ID).create( + order._prepare_service_picking_vals() + ) + else: + picking = service_pickings[0] + picking_lines = order.order_line._create_service_picking_lines(picking) + seq = 0 + for picking_line in sorted(picking_lines, key=lambda pl: pl.date): + seq += 5 + picking_line.sequence = seq + picking.message_post_with_view( + "mail.message_origin_link", + values={"self": picking, "origin": order}, + subtype_id=self.env.ref("mail.mt_note").id, + ) + return True + + def button_approve(self, force=False): + result = super().button_approve(force=force) + self._create_service_picking() + return result + + def action_view_service_picking(self): + result = self.env["ir.actions.actions"]._for_xml_id( + "purchase_service_picking.action_service_picking_tree_all" + ) + result["context"] = { + "default_partner_id": self.partner_id.id, + "default_origin": self.name, + "default_purchcase_id": self.id, + } + pick_ids = self.mapped("service_picking_ids") + # choose the view_mode accordingly + if not pick_ids or len(pick_ids) > 1: + result["domain"] = "[('id','in',%s)]" % (pick_ids.ids) + elif len(pick_ids) == 1: + res = self.env.ref( + "view_service_picking_form.view_service_picking_form", False + ) + form_view = [(res and res.id or False, "form")] + if "views" in result: + result["views"] = form_view + [ + (state, view) for state, view in result["views"] if view != "form" + ] + else: + result["views"] = form_view + result["res_id"] = pick_ids.id + return result + + +class PurchaseOrderLine(models.Model): + _inherit = "purchase.order.line" + + service_picking_line_ids = fields.One2many( + comodel_name="service.picking.line", + inverse_name="purchase_line_id", + string="Service picking lines", + readonly=True, + copy=False, + ) + + @api.depends( + "service_picking_line_ids.state", + "service_picking_line_ids.product_qty", + "service_picking_line_ids.product_uom_id", + ) + def _compute_qty_received(self): + custom_lines = self.filtered( + lambda x: x.product_id.type == "service" + and x.product_id.purchase_method == "receive" + ) + res = super(PurchaseOrderLine, self - custom_lines)._compute_qty_received() + for line in custom_lines: + total = 0 + for spl in line.service_picking_line_ids.filtered( + lambda x: x.state == "done" + ): + spl_qty = spl.product_uom_id._compute_quantity( + spl.quantity_done, line.product_uom, rounding_method="HALF-UP" + ) + if spl.origin_returned_line_id: + total -= spl_qty + else: + total += spl_qty + line._track_qty_received(total) + line.qty_received = total + return res + + def _get_outgoing_incoming_service_picking_lines(self): + lines = self.service_picking_line_ids.filtered(lambda x: x.state != "cancel") + outgoing_lines = lines.filtered(lambda x: x.origin_returned_line_id) + incoming_lines = lines - outgoing_lines + return outgoing_lines, incoming_lines + + def _prepare_service_picking_line_vals(self, picking, product_qty): + self.ensure_one() + date_planned = self.date_planned or self.order_id.date_planned + return { + "name": (self.product_id.display_name or "")[:2000], + "product_id": self.product_id.id, + "date": date_planned, + "picking_id": picking.id, + "purchase_line_id": self.id, + "product_qty": product_qty, + "product_uom_id": self.product_uom.id, + "sequence": self.sequence, + } + + def _prepare_service_picking_lines(self, picking): + self.ensure_one() + res = [] + uom = self.product_uom + qty_to_attach = 0 + qty = 0.0 + out_lines, in_lines = self._get_outgoing_incoming_service_picking_lines() + for line in out_lines: + qty -= line.product_uom_id._compute_quantity( + line.product_qty, uom, rounding_method="HALF-UP" + ) + for line in in_lines: + qty += line.product_uom_id._compute_quantity( + line.product_qty, uom, rounding_method="HALF-UP" + ) + qty_to_push = self.product_qty - qty + if float_compare(qty_to_attach, 0.0, precision_rounding=uom.rounding) > 0: + product_qty = uom._compute_quantity( + qty_to_attach, uom, rounding_method="HALF-UP" + ) + res.append(self._prepare_service_picking_line_vals(picking, product_qty)) + if not float_is_zero(qty_to_push, precision_rounding=uom.rounding): + product_qty = uom._compute_quantity( + qty_to_push, uom, rounding_method="HALF-UP" + ) + res.append(self._prepare_service_picking_line_vals(picking, product_qty)) + return res + + def _create_service_picking_lines(self, picking): + values = [] + for line in self.filtered(lambda x: x.product_id.type == "service"): + for val in line._prepare_service_picking_lines(picking): + values.append(val) + return self.env["service.picking.line"].create(values) + + def _create_or_update_sevice_picking(self): + for pol in self.filtered(lambda x: x.product_id.type == "service"): + # Prevent decreasing below received quantity + if ( + float_compare( + pol.product_qty, pol.qty_received, pol.product_uom.rounding + ) + < 0 + ): + raise UserError( + _( + "You cannot decrease the ordered quantity below the received " + "quantity.\nCreate a return first." + ) + ) + # If the user increased quantity of existing line or created a new line + pickings = pol.order_id.service_picking_ids.filtered( + lambda x: x.state not in ("done", "cancel") + ) + picking = pickings and pickings[0] or False + if not picking: + picking = self.env["service.picking"].create( + pol.order_id._prepare_service_picking_vals() + ) + pol._create_service_picking_lines(picking) + else: + self.env["service.picking.line"].create( + pol._prepare_service_picking_lines(picking) + ) + picking.line_ids._merge_lines() + + @api.model_create_multi + def create(self, vals_list): + lines = super().create(vals_list) + lines.filtered( + lambda x: x.order_id.state == "purchase" and x.product_id.type == "service" + )._create_or_update_sevice_picking() + return lines + + def write(self, values): + lines = self.filtered( + lambda x: x.order_id.state == "purchase" and x.product_id.type == "service" + ) + previous_product_qty = {line.id: line.product_qty for line in lines} + result = super().write(values) + if "product_qty" in values: + lines = lines.filtered( + lambda x: float_compare( + previous_product_qty[x.id], + x.product_qty, + precision_rounding=x.product_uom.rounding, + ) + != 0 + ) + lines._create_or_update_sevice_picking() + return result diff --git a/purchase_service_picking/models/service_picking.py b/purchase_service_picking/models/service_picking.py new file mode 100644 index 00000000000..7f65a4d02ca --- /dev/null +++ b/purchase_service_picking/models/service_picking.py @@ -0,0 +1,442 @@ +# Copyright 2025 Tecnativa - Víctor Martínez +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl) +from itertools import groupby +from operator import itemgetter + +from odoo import SUPERUSER_ID, _, api, fields, models +from odoo.exceptions import UserError +from odoo.tools.float_utils import float_compare, float_is_zero + + +class ServicePicking(models.Model): + _name = "service.picking" + _inherit = ["mail.thread", "mail.activity.mixin"] + _description = "Service Picking" + _order = "scheduled_date asc, id desc" + + name = fields.Char( + string="Reference", default="/", copy=False, index=True, readonly=True + ) + origin = fields.Char( + string="Source Document", + index=True, + states={"done": [("readonly", True)], "cancel": [("readonly", True)]}, + help="Reference of the document", + ) + backorder_id = fields.Many2one( + comodel_name="service.picking", + string="Back Order of", + copy=False, + index=True, + readonly=True, + check_company=True, + ) + backorder_ids = fields.One2many( + comodel_name="service.picking", + inverse_name="backorder_id", + string="Back Orders", + ) + purchase_id = fields.Many2one( + comodel_name="purchase.order", + string="Purchase Order", + ondelete="cascade", + index=True, + copy=False, + ) + state = fields.Selection( + selection=[ + ("draft", "Draft"), + ("in_progress", "In progress"), + ("done", "Done"), + ("cancel", "Cancelled"), + ], + string="Status", + default="draft", + copy=False, + index=True, + readonly=True, + tracking=True, + ) + scheduled_date = fields.Datetime( + string="Scheduled Date", + compute="_compute_scheduled_date", + inverse="_inverse_scheduled_date", + store=True, + index=True, + default=fields.Datetime.now, + tracking=True, + states={"done": [("readonly", True)], "cancel": [("readonly", True)]}, + ) + date_deadline = fields.Datetime( + string="Deadline", + compute="_compute_date_deadline", + store=True, + help="Date Promise to the customer on the top level document (SO/PO)", + ) + date = fields.Datetime( + string="Creation Date", + default=fields.Datetime.now, + index=True, + tracking=True, + states={"done": [("readonly", True)], "cancel": [("readonly", True)]}, + help="Creation Date, usually the time of the order", + ) + date_done = fields.Datetime( + string="Date of Transfer", + copy=False, + readonly=True, + help="Date at which the transfer has been processed or cancelled.", + ) + line_ids = fields.One2many( + comodel_name="service.picking.line", + inverse_name="picking_id", + string="Lines", + copy=False, + ) + partner_id = fields.Many2one( + comodel_name="res.partner", + string="Contact", + check_company=True, + states={"done": [("readonly", True)], "cancel": [("readonly", True)]}, + ) + company_id = fields.Many2one( + comodel_name="res.company", + string="Company", + required=True, + index=True, + default=lambda self: self.env.company.id, + readonly=True, + states={"draft": [("readonly", False)], "reserved": [("readonly", False)]}, + ) + user_id = fields.Many2one( + comodel_name="res.users", + string="Responsible", + tracking=True, + domain=lambda self: [ + ("groups_id", "in", self.env.ref("purchase.group_purchase_user").id) + ], + states={"done": [("readonly", True)], "cancel": [("readonly", True)]}, + default=lambda self: self.env.user, + ) + + _sql_constraints = [ + ( + "name_uniq", + "unique(name, company_id)", + "Reference must be unique per company!", + ), + ] + + @api.depends("line_ids.state", "line_ids.date") + def _compute_scheduled_date(self): + for picking in self: + lines_dates = picking.line_ids.filtered( + lambda x: x.state not in ("done", "cancel") + ).mapped("date") + picking.scheduled_date = min( + lines_dates, default=picking.scheduled_date or fields.Datetime.now() + ) + + @api.depends("line_ids.date_deadline") + def _compute_date_deadline(self): + for picking in self: + picking.date_deadline = min( + picking.line_ids.filtered("date_deadline").mapped("date_deadline"), + default=False, + ) + + def _inverse_scheduled_date(self): + for picking in self: + if picking.state in ("done", "cancel"): + raise UserError( + _( + "You cannot change the Scheduled Date on a done or cancelled transfer." + ) + ) + picking.line_ids.write({"date": picking.scheduled_date}) + + @api.model_create_multi + def create(self, vals_list): + for vals in vals_list: + if not vals.get("name") or vals["name"] == "/": + vals["name"] = self.env["ir.sequence"].next_by_code( + "service.picking" + ) or _("New") + return super().create(vals_list) + + def action_confirm(self): + for item in self.filtered(lambda x: x.state == "draft"): + item.state = "in_progress" + + def _check_immediate(self): + immediate_pickings = self.browse() + precision_digits = self.env["decimal.precision"].precision_get( + "Product Unit of Measure" + ) + for picking in self.filtered(lambda x: x.state == "in_progress"): + if all( + float_is_zero(line.quantity_done, precision_digits=precision_digits) + for line in picking.line_ids + ): + immediate_pickings |= picking + return immediate_pickings + + def _check_backorder(self): + prec = self.env["decimal.precision"].precision_get("Product Unit of Measure") + backorder_pickings = self.browse() + for picking in self: + qty_todo = {} + qty_done = {} + for line in picking.mapped("line_ids").filtered( + lambda x: x.state != "cancel" + ): + product = line.product_id + qty_todo.setdefault(product.id, 0) + qty_done.setdefault(product.id, 0) + qty_todo_line = line.product_uom_id._compute_quantity( + line.product_qty, product.uom_id, rounding_method="HALF-UP" + ) + qty_todo[product.id] += qty_todo_line + qty_done_line = line.product_uom_id._compute_quantity( + line.quantity_done, + product.uom_id, + rounding_method="HALF-UP", + ) + qty_done[product.id] += qty_done_line + if any( + float_compare( + qty_done[x], + qty_todo.get(x, 0), + precision_digits=prec, + ) + == -1 + for x in qty_done + ): + backorder_pickings |= picking + return backorder_pickings + + def _prepare_split_vals(self): + return { + "backorder_id": self.id, + "partner_id": self.partner_id.id, + "user_id": self.user_id.id, + "date": self.date, + "origin": self.origin, + "company_id": self.company_id.id, + "purchase_id": self.purchase_id.id, + "state": "in_progress", + } + + def _create_backorder(self): + sp_model = self.env["service.picking"] + backorder = sp_model + backorder_line_vals = [] + rounding = self.env["decimal.precision"].precision_get( + "Product Unit of Measure" + ) + for line in self.line_ids: + if ( + float_compare( + line.quantity_done, line.product_qty, precision_digits=rounding + ) + < 0 + ): + qty_split = line.product_uom_id._compute_quantity( + line.product_qty - line.quantity_done, + line.product_id.uom_id, + rounding_method="HALF-UP", + ) + if not backorder: + backorder = sp_model.with_user(SUPERUSER_ID).create( + self._prepare_split_vals() + ) + self.message_post( + body=_( + "The backorder %s has been created." + ) + % (backorder.id, backorder.name) + ) + backorder_line_vals.append( + line._prepare_line_split_vals(backorder, qty_split) + ) + if backorder_line_vals: + self.env["service.picking.line"].create(backorder_line_vals) + + def action_validate(self): + self = self.filtered(lambda x: x.state == "in_progress") + if not self.env.context.get("action_validate_picking_ids"): + self = self.with_context(action_validate_picking_ids=self.ids) + pickings_not_to_backorder = self.env["service.picking"] + if self.env.context.get("picking_ids_not_to_backorder"): + pickings_not_to_backorder = self.browse( + self.env.context["picking_ids_not_to_backorder"] + ) + for item in self: + # Inmediate wizard + if not self.env.context.get("skip_immediate"): + pickings_to_immediate = self._check_immediate() + if pickings_to_immediate: + return pickings_to_immediate._action_generate_immediate_wizard() + # Backorder wizard + if not self.env.context.get("skip_backorder"): + pickings_to_backorder = self._check_backorder() + if pickings_to_backorder: + return pickings_to_backorder._action_generate_backorder_wizard() + # Backorder process + if item not in pickings_not_to_backorder: + item._create_backorder() + item.line_ids.write({"date": fields.Datetime.now()}) + item.write({"state": "done", "date_done": fields.Datetime.now()}) + + def action_cancel(self): + for item in self.filtered(lambda x: x.state == "in_progress"): + item.state = "cancel" + + def _action_generate_immediate_wizard(self): + view = self.env.ref("purchase_service_picking.view_immediate_transfer") + return { + "name": _("Immediate Transfer?"), + "type": "ir.actions.act_window", + "view_mode": "form", + "res_model": "service.immediate.transfer", + "views": [(view.id, "form")], + "view_id": view.id, + "target": "new", + "context": dict( + self.env.context, default_picking_ids=[(4, p.id) for p in self] + ), + } + + def _action_generate_backorder_wizard(self): + view = self.env.ref("purchase_service_picking.view_backorder_confirmation") + return { + "name": _("Create Backorder?"), + "type": "ir.actions.act_window", + "view_mode": "form", + "res_model": "service.backorder.confirmation", + "views": [(view.id, "form")], + "view_id": view.id, + "target": "new", + "context": dict( + self.env.context, default_picking_ids=[(4, p.id) for p in self] + ), + } + + +class ServicePickingLine(models.Model): + _name = "service.picking.line" + _description = "Service Picking Line" + _order = "sequence, id" + + picking_id = fields.Many2one( + comodel_name="service.picking", + string="Picking", + index=True, + required=True, + ondelete="cascade", + ) + sequence = fields.Integer(string="Sequence", default=10) + company_id = fields.Many2one( + comodel_name="res.company", + string="Company", + default=lambda self: self.env.company, + index=True, + required=True, + ) + purchase_line_id = fields.Many2one(comodel_name="purchase.order.line") + state = fields.Selection(related="picking_id.state") + name = fields.Char(string="Description", index=True, required=True) + origin_returned_line_id = fields.Many2one( + comodel_name="service.picking.line", + string="Origin return line", + copy=False, + index=True, + help="Line that created the return line", + ) + returned_line_ids = fields.One2many( + comodel_name="service.picking.line", + inverse_name="origin_returned_line_id", + string="All returned lines", + ) + date = fields.Datetime( + string="Date Scheduled", + default=fields.Datetime.now, + index=True, + required=True, + ) + date_deadline = fields.Datetime( + string="Deadline", + readonly=True, + help="Date Promise to the customer on the top level document (SO/PO)", + ) + product_id = fields.Many2one( + comodel_name="product.product", + string="Product", + check_company=True, + domain="""[('type', '=', 'service'), '|', ('company_id', '=', False), + ('company_id', '=', company_id)]""", + index=True, + required=True, + states={"done": [("readonly", True)]}, + ) + product_qty = fields.Float( + string="Demand", + digits="Product Unit of Measure", + default=0.0, + required=True, + states={"done": [("readonly", True)]}, + ) + product_uom_id = fields.Many2one( + comodel_name="uom.uom", string="Unit of Measure", required=True + ) + quantity_done = fields.Float( + string="Quantity Done", digits="Product Unit of Measure", copy=False + ) + + def _prepare_line_split_vals(self, picking, qty): + return { + "picking_id": picking.id, + "company_id": self.company_id.id, + "purchase_line_id": self.purchase_line_id.id, + "name": self.name, + "date_deadline": self.date_deadline, + "product_id": self.product_id.id, + "product_qty": qty, + "product_uom_id": self.product_uom_id.id, + } + + @api.model + def _prepare_merge_lines_distinct_fields(self): + return ["purchase_line_id", "product_id", "product_uom_id"] + + @api.model + def _prepare_merge_line_sort_method(self, line): + line.ensure_one() + return [line.purchase_line_id.id, line.product_id.id, line.product_uom_id.id] + + def _merge_lines_fields(self): + return { + "product_qty": sum(self.mapped("product_qty")), + } + + def _merge_lines(self): + spl_model = self.env["service.picking.line"] + distinct_fields = self._prepare_merge_lines_distinct_fields() + lines_to_unlink = spl_model + lines_to_merge = [] + for _k, g in groupby( + sorted(self, key=self._prepare_merge_line_sort_method), + key=itemgetter(*distinct_fields), + ): + lines = spl_model.concat(*g).filtered(lambda x: x.state == "in_progress") + if len(lines) > 1: + lines_to_merge.append(lines) + + for lines in lines_to_merge: + lines[0].write(lines._merge_lines_fields()) + lines_to_unlink |= lines[1:] + + if lines_to_unlink: + lines_to_unlink.sudo().unlink() + return (self | spl_model.concat(*lines_to_merge)) - lines_to_unlink diff --git a/purchase_service_picking/readme/CONFIGURE.rst b/purchase_service_picking/readme/CONFIGURE.rst new file mode 100644 index 00000000000..e69de29bb2d diff --git a/purchase_service_picking/readme/CONTRIBUTORS.rst b/purchase_service_picking/readme/CONTRIBUTORS.rst new file mode 100644 index 00000000000..5fb71305308 --- /dev/null +++ b/purchase_service_picking/readme/CONTRIBUTORS.rst @@ -0,0 +1,4 @@ +* `Tecnativa `_: + + * Víctor Martínez + * Pedro M. Baeza diff --git a/purchase_service_picking/readme/DESCRIPTION.rst b/purchase_service_picking/readme/DESCRIPTION.rst new file mode 100644 index 00000000000..2923951cad9 --- /dev/null +++ b/purchase_service_picking/readme/DESCRIPTION.rst @@ -0,0 +1 @@ +The module allows to manage the reception of service lines for purchase orders. diff --git a/purchase_service_picking/readme/USAGE.rst b/purchase_service_picking/readme/USAGE.rst new file mode 100644 index 00000000000..e69de29bb2d diff --git a/purchase_service_picking/security/ir.model.access.csv b/purchase_service_picking/security/ir.model.access.csv new file mode 100644 index 00000000000..b12d1ce1653 --- /dev/null +++ b/purchase_service_picking/security/ir.model.access.csv @@ -0,0 +1,10 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_service_picking_purchase_user,service.picking,model_service_picking,purchase.group_purchase_user,1,1,1,1 +access_service_picking_line_purchase_user,service.picking.line,model_service_picking_line,purchase.group_purchase_user,1,1,1,0 +access_service_picking_line_purchase_user_manager,service.picking.line,model_service_picking_line,purchase.group_purchase_manager,1,1,1,1 +access_service_immediate_transfer,access.service.immediate.transfer,model_service_immediate_transfer,purchase.group_purchase_user,1,1,1,0 +access_service_immediate_transfer_line,access.service.immediate.transfer.line,model_service_immediate_transfer_line,purchase.group_purchase_user,1,1,1,0 +access_service_backorder_confirmation,access.service.backorder.confirmation,model_service_backorder_confirmation,purchase.group_purchase_user,1,1,1,0 +access_service_backorder_confirmation_line,access.service.backorder.confirmation.line,model_service_backorder_confirmation_line,purchase.group_purchase_user,1,1,1,0 +access_service_return_picking,access.service.return.picking,model_service_return_picking,purchase.group_purchase_user,1,1,1,0 +access_service_return_picking_line,access.service.return.picking.line,model_service_return_picking_line,purchase.group_purchase_user,1,1,1,1 diff --git a/purchase_service_picking/security/security.xml b/purchase_service_picking/security/security.xml new file mode 100644 index 00000000000..1cc067863b5 --- /dev/null +++ b/purchase_service_picking/security/security.xml @@ -0,0 +1,13 @@ + + + + service_picking multi-company + + [('company_id', 'in', company_ids)] + + + service_picking_line multi-company + + [('company_id', 'in', company_ids)] + + diff --git a/purchase_service_picking/static/description/icon.png b/purchase_service_picking/static/description/icon.png new file mode 100644 index 00000000000..3a0328b516c Binary files /dev/null and b/purchase_service_picking/static/description/icon.png differ diff --git a/purchase_service_picking/static/description/index.html b/purchase_service_picking/static/description/index.html new file mode 100644 index 00000000000..eb5341a0fa9 --- /dev/null +++ b/purchase_service_picking/static/description/index.html @@ -0,0 +1,426 @@ + + + + + +Purchase service picking + + + +
+

Purchase service picking

+ + +

Beta License: AGPL-3 OCA/purchase-workflow Translate me on Weblate Try me on Runboat

+

The module allows to manage the reception of service lines for purchase orders.

+

Table of contents

+ +
+

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 to smash 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:
      +
    • Víctor Martínez
    • +
    • Pedro M. Baeza
    • +
    +
  • +
+
+
+

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:

+

victoralmau

+

This module is part of the OCA/purchase-workflow project on GitHub.

+

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

+
+
+
+ + diff --git a/purchase_service_picking/tests/__init__.py b/purchase_service_picking/tests/__init__.py new file mode 100644 index 00000000000..b169b4f32fd --- /dev/null +++ b/purchase_service_picking/tests/__init__.py @@ -0,0 +1,3 @@ +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl) + +from . import test_purchase_service_picking diff --git a/purchase_service_picking/tests/test_purchase_service_picking.py b/purchase_service_picking/tests/test_purchase_service_picking.py new file mode 100644 index 00000000000..44c48041f2c --- /dev/null +++ b/purchase_service_picking/tests/test_purchase_service_picking.py @@ -0,0 +1,317 @@ +# Copyright 2025 Tecnativa - Víctor Martínez +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo.exceptions import UserError +from odoo.tests import Form, common +from odoo.tools import mute_logger + + +class TestPurchaseServicePicking(common.SavepointCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.env = cls.env( + context=dict( + cls.env.context, + mail_create_nolog=True, + mail_create_nosubscribe=True, + mail_notrack=True, + no_reset_password=True, + tracking_disable=True, + ) + ) + cls.partner = cls.env["res.partner"].create({"name": "Mr Odoo"}) + cls.service_product = cls.env["product.product"].create( + { + "name": "Test service product", + "type": "service", + } + ) + cls.consu_product = cls.env["product.product"].create( + { + "name": "Test consu product", + "type": "consu", + } + ) + cls.order = cls._create_order() + + @classmethod + def _create_order(cls): + order_form = Form(cls.env["purchase.order"]) + order_form.partner_id = cls.partner + with order_form.order_line.new() as line_form: + line_form.product_id = cls.service_product + line_form.product_qty = 2 + return order_form.save() + + def test_purchase_sevice_flow_with_consu_product(self): + """Order with consu product""" + self.order.order_line.product_id = self.consu_product + self.order.button_confirm() + self.assertFalse(self.order.service_picking_ids) + + def test_purchase_sevice_flow_manual_quantity_done(self): + """Order with service product""" + self.order.button_confirm() + self.assertTrue(self.order.service_picking_ids) + picking = self.order.service_picking_ids + self.assertEqual(picking.state, "in_progress") + self.assertEqual(picking.partner_id, self.partner) + self.assertFalse(picking.user_id) + self.assertEqual(picking.date, self.order.date_order) + self.assertEqual(picking.origin, self.order.name) + self.assertEqual(picking.purchase_id, self.order) + self.assertEqual(len(picking.line_ids), 1) + self.assertEqual(picking.line_ids.product_id, self.service_product) + self.assertEqual(picking.line_ids.product_qty, 2) + self.assertEqual(picking.line_ids.quantity_done, 0) + # Set quantity_done + picking.line_ids.quantity_done = 2 + picking.action_validate() + self.assertEqual(picking.state, "done") + self.assertEqual(self.order.order_line.qty_received, 2) + + def test_purchase_sevice_flow_cancel(self): + """Order with consu + service products + cancel process""" + service_line = self.order.order_line + order_form = Form(self.order) + with order_form.order_line.new() as line_form: + line_form.product_id = self.consu_product + self.order = order_form.save() + consu_line = self.order.order_line - service_line + self.order.button_confirm() + self.assertTrue(self.order.service_picking_ids) + picking = self.order.service_picking_ids + self.assertEqual(picking.state, "in_progress") + self.assertEqual(len(picking.line_ids), 1) + self.assertEqual(picking.line_ids.product_id, self.service_product) + self.assertTrue(service_line.service_picking_line_ids) + self.assertFalse(consu_line.service_picking_line_ids) + picking.action_cancel() + self.assertEqual(picking.state, "cancel") + self.assertEqual(service_line.qty_received, 0) + + def test_purchase_sevice_flow_immediate_transfer(self): + """Inmediate transfer process""" + self.order.button_confirm() + picking = self.order.service_picking_ids + res = picking.action_validate() + self.assertEqual(res["res_model"], "service.immediate.transfer") + wizard = self.env[res["res_model"]].with_context(res["context"]).create({}) + self.assertEqual(len(wizard.line_ids), 1) + self.assertEqual(wizard.line_ids.picking_id, picking) + self.assertTrue(wizard.line_ids.to_immediate) + wizard.process() + self.assertEqual(picking.state, "done") + self.assertEqual(picking.line_ids.quantity_done, 2) + self.assertEqual(self.order.order_line.qty_received, 2) + + def test_purchase_sevice_flow_backorder_01(self): + """Backorder process""" + self.order.button_confirm() + picking = self.order.service_picking_ids + picking.line_ids.quantity_done = 1 + picking = self.order.service_picking_ids + res = picking.action_validate() + self.assertEqual(res["res_model"], "service.backorder.confirmation") + wizard = self.env[res["res_model"]].with_context(res["context"]).create({}) + self.assertEqual(len(wizard.line_ids), 1) + self.assertEqual(wizard.line_ids.picking_id, picking) + self.assertTrue(wizard.line_ids.to_backorder) + wizard.process() + self.assertEqual(picking.state, "done") + self.assertEqual(picking.line_ids.quantity_done, 1) + self.assertEqual(self.order.order_line.qty_received, 1) + self.assertTrue(picking.backorder_ids) + backorder = picking.backorder_ids + self.assertEqual(backorder.backorder_id, picking) + self.assertEqual(backorder.state, "in_progress") + self.assertEqual(len(backorder.line_ids), 1) + self.assertEqual(backorder.line_ids.product_qty, 1) + self.assertEqual(backorder.line_ids.quantity_done, 0) + # Set quantity_done + backorder.line_ids.quantity_done = 1 + backorder.action_validate() + self.assertEqual(self.order.order_line.qty_received, 2) + + def test_purchase_sevice_flow_backorder_02(self): + """Cancel backorder process""" + self.order.button_confirm() + picking = self.order.service_picking_ids + picking.line_ids.quantity_done = 1 + picking = self.order.service_picking_ids + res = picking.action_validate() + self.assertEqual(res["res_model"], "service.backorder.confirmation") + wizard = self.env[res["res_model"]].with_context(res["context"]).create({}) + self.assertEqual(len(wizard.line_ids), 1) + self.assertEqual(wizard.line_ids.picking_id, picking) + self.assertTrue(wizard.line_ids.to_backorder) + wizard.process_cancel_backorder() + self.assertEqual(picking.state, "done") + self.assertEqual(picking.line_ids.quantity_done, 1) + self.assertEqual(self.order.order_line.qty_received, 1) + self.assertFalse(picking.backorder_ids) + + @mute_logger("odoo.models.unlink") + def test_purchase_sevice_flow_pol_update_01(self): + """Modify line quantity when order is already confirmed.""" + order_line = self.order.order_line + self.order.button_confirm() + picking = self.order.service_picking_ids + self.assertEqual(len(picking.line_ids), 1) + picking_line = picking.line_ids + self.assertEqual(picking_line.product_qty, 2) + order_line.write({"product_qty": 3}) + self.assertEqual(len(picking.line_ids), 1) + self.assertEqual(picking_line.product_qty, 3) + order_line.write({"product_qty": 1}) + self.assertEqual(len(picking.line_ids), 1) + self.assertEqual(picking_line.product_qty, 1) + order_form = Form( + self.order.with_context(default_product_id=self.service_product.id) + ) + with order_form.order_line.new() as line_form: + line_form.product_qty = 2 + order_form.save() + order_line_extra = self.order.order_line - order_line + self.assertEqual(len(picking.line_ids), 2) + picking_line_extra = picking.line_ids - picking_line + self.assertEqual(picking_line.product_qty, 1) + self.assertEqual(picking_line_extra.product_qty, 2) + picking_line.quantity_done = 1 + picking_line_extra.quantity_done = 2 + picking.action_validate() + self.assertEqual(picking.state, "done") + self.assertEqual(order_line.qty_received, 1) + self.assertEqual(order_line_extra.qty_received, 2) + + def test_purchase_sevice_flow_pol_update_02(self): + """Modify (reduce) line quantity when picking is done.""" + order_line = self.order.order_line + self.order.button_confirm() + picking = self.order.service_picking_ids + picking.line_ids.quantity_done = 2 + picking.action_validate() + self.assertEqual(picking.state, "done") + self.assertEqual(order_line.qty_received, 2) + with self.assertRaises(UserError): + order_line.write({"product_qty": 1}) + + def test_purchase_sevice_flow_pol_extra(self): + """Validate picking + extra picking.""" + order_line = self.order.order_line + self.order.button_confirm() + picking = self.order.service_picking_ids + self.assertEqual(len(picking.line_ids), 1) + self.assertEqual(picking.line_ids.product_qty, 2) + picking.line_ids.quantity_done = 2 + picking.action_validate() + self.assertEqual(picking.state, "done") + self.assertEqual(order_line.qty_received, 2) + order_form = Form( + self.order.with_context(default_product_id=self.service_product.id) + ) + with order_form.order_line.new() as line_form: + line_form.product_qty = 1 + order_form.save() + order_line_extra = self.order.order_line - order_line + picking_exta = self.order.service_picking_ids - picking + self.assertEqual(len(self.order.service_picking_ids), 2) + self.assertEqual(picking_exta.state, "in_progress") + self.assertEqual(len(picking_exta.line_ids), 1) + self.assertEqual(picking_exta.line_ids.product_qty, 1) + picking_exta.line_ids.quantity_done = 1 + picking_exta.action_validate() + self.assertEqual(picking_exta.state, "done") + self.assertEqual(order_line_extra.qty_received, 1) + + def _create_return_picking(self, picking): + wizard_return_form = Form( + self.env["service.return.picking"].with_context( + active_id=picking.id, active_ids=picking.ids, active_model=picking._name + ) + ) + return wizard_return_form.save() + + def test_purchase_sevice_flow_return_error_01(self): + """Error return picking (qty 0)""" + self.order.button_confirm() + picking = self.order.service_picking_ids + self.assertEqual(len(picking.line_ids), 1) + self.assertEqual(picking.line_ids.product_qty, 2) + picking.line_ids.quantity_done = 2 + picking.action_validate() + self.assertEqual(picking.state, "done") + self.assertEqual(self.order.order_line.qty_received, 2) + wizard_return = self._create_return_picking(picking) + self.assertTrue(wizard_return.product_return_lines) + wizard_return.product_return_lines.quantity = 0 + with self.assertRaises(UserError): + wizard_return.create_returns() + + def test_purchase_sevice_flow_return_error_02(self): + """Error return picking (higher qty)""" + self.order.button_confirm() + picking = self.order.service_picking_ids + self.assertEqual(len(picking.line_ids), 1) + self.assertEqual(picking.line_ids.product_qty, 2) + picking.line_ids.quantity_done = 2 + picking.action_validate() + self.assertEqual(picking.state, "done") + self.assertEqual(self.order.order_line.qty_received, 2) + wizard_return = self._create_return_picking(picking) + self.assertTrue(wizard_return.product_return_lines) + wizard_return.product_return_lines.quantity = 3 + with self.assertRaises(UserError): + wizard_return.create_returns() + + def test_purchase_sevice_flow_return_01(self): + """Full return picking""" + self.order.button_confirm() + picking = self.order.service_picking_ids + self.assertEqual(len(picking.line_ids), 1) + self.assertEqual(picking.line_ids.product_qty, 2) + picking.line_ids.quantity_done = 2 + picking.action_validate() + self.assertEqual(picking.state, "done") + self.assertEqual(self.order.order_line.qty_received, 2) + wizard_return = self._create_return_picking(picking) + res = wizard_return.create_returns() + self.assertEqual(res["res_model"], "service.picking") + return_picking = self.env[res["res_model"]].browse(res["res_id"]) + self.assertEqual(len(return_picking.line_ids), 1) + self.assertEqual(return_picking.line_ids.product_id, self.service_product) + self.assertEqual(return_picking.line_ids.product_qty, 2) + self.assertEqual(return_picking.line_ids.quantity_done, 0) + self.assertEqual( + return_picking.line_ids.origin_returned_line_id, picking.line_ids + ) + return_picking.line_ids.quantity_done = 2 + return_picking.action_validate() + self.assertEqual(self.order.order_line.qty_received, 0) + + def test_purchase_sevice_flow_return_02(self): + """Partial return picking""" + self.order.button_confirm() + picking = self.order.service_picking_ids + self.assertEqual(len(picking.line_ids), 1) + self.assertEqual(picking.line_ids.product_qty, 2) + picking.line_ids.quantity_done = 2 + picking.action_validate() + self.assertEqual(picking.state, "done") + self.assertEqual(self.order.order_line.qty_received, 2) + wizard_return = self._create_return_picking(picking) + wizard_return.product_return_lines.quantity = 1 + res = wizard_return.create_returns() + self.assertEqual(res["res_model"], "service.picking") + return_picking = self.env[res["res_model"]].browse(res["res_id"]) + self.assertEqual(len(return_picking.line_ids), 1) + self.assertEqual(return_picking.line_ids.product_id, self.service_product) + self.assertEqual(return_picking.line_ids.product_qty, 1) + self.assertEqual(return_picking.line_ids.quantity_done, 0) + self.assertEqual( + return_picking.line_ids.origin_returned_line_id, picking.line_ids + ) + return_picking.line_ids.quantity_done = 1 + return_picking.action_validate() + self.assertEqual(self.order.order_line.qty_received, 1) diff --git a/purchase_service_picking/views/purchase_order_views.xml b/purchase_service_picking/views/purchase_order_views.xml new file mode 100644 index 00000000000..1eae9dba84e --- /dev/null +++ b/purchase_service_picking/views/purchase_order_views.xml @@ -0,0 +1,26 @@ + + + + purchase.order.form + purchase.order + + + + + + + + diff --git a/purchase_service_picking/views/service_picking_views.xml b/purchase_service_picking/views/service_picking_views.xml new file mode 100644 index 00000000000..b9f5e2cf48b --- /dev/null +++ b/purchase_service_picking/views/service_picking_views.xml @@ -0,0 +1,278 @@ + + + + service.picking + service.picking + + + + + + + + + + + + + + + + + + + + + + + + service.picking.tree + service.picking + + + + + + + + + + + + + + + + service.picking.form + service.picking + +
+
+
+ +
+

+ +

+
+ + +
+
+ + +
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+
+ + Service Pickings + service.picking + ir.actions.act_window + tree,form + +
diff --git a/purchase_service_picking/wizards/__init__.py b/purchase_service_picking/wizards/__init__.py new file mode 100644 index 00000000000..7c0406039be --- /dev/null +++ b/purchase_service_picking/wizards/__init__.py @@ -0,0 +1,3 @@ +from . import service_backorder_confirmation +from . import service_immediate_transfer +from . import service_picking_return diff --git a/purchase_service_picking/wizards/service_backorder_confirmation.py b/purchase_service_picking/wizards/service_backorder_confirmation.py new file mode 100644 index 00000000000..7fca158d985 --- /dev/null +++ b/purchase_service_picking/wizards/service_backorder_confirmation.py @@ -0,0 +1,88 @@ +# Copyright 2025 Tecnativa - Víctor Martínez +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl) + +from odoo import api, fields, models +from odoo.tools.float_utils import float_compare + + +class ServiceBackorderConfirmation(models.TransientModel): + _name = "service.backorder.confirmation" + _description = "Backorder Confirmation" + + picking_ids = fields.Many2many( + comodel_name="service.picking", relation="service_picking_backorder_rel" + ) + line_ids = fields.One2many( + comodel_name="service.backorder.confirmation.line", + inverse_name="backorder_confirmation_id", + string="Lines", + ) + + @api.model + def default_get(self, fields): + res = super().default_get(fields) + if "line_ids" in fields and res.get("picking_ids"): + res["line_ids"] = [ + (0, 0, {"to_backorder": True, "picking_id": pick_id}) + for pick_id in res["picking_ids"][0][2] + ] + return res + + def _check_less_quantities_than_expected(self, pickings): + for picking in pickings: + moves_to_log = {} + for line in picking.line_ids: + if ( + float_compare( + line.product_qty, + line.quantity_done, + precision_rounding=line.product_uom_id.rounding, + ) + > 0 + ): + moves_to_log[line] = (line.quantity_done, line.product_qty) + if moves_to_log: + picking._log_less_quantities_than_expected(moves_to_log) + + def process(self): + pickings_to_do = self.env["service.picking"] + pickings_not_to_do = self.env["service.picking"] + for line in self.line_ids: + if line.to_backorder is True: + pickings_to_do |= line.picking_id + else: + pickings_not_to_do |= line.picking_id + + pickings_to_validate = self.env.context.get("action_validate_picking_ids") + if pickings_to_validate: + pickings = ( + self.env["service.picking"] + .browse(pickings_to_validate) + .with_context(skip_backorder=True) + ) + if pickings_not_to_do: + pickings = pickings.with_context( + picking_ids_not_to_backorder=pickings_not_to_do.ids + ) + return pickings.action_validate() + return True + + def process_cancel_backorder(self): + pickings_to_validate_ids = self.env.context.get("action_validate_picking_ids") + if pickings_to_validate_ids: + pickings = self.env["service.picking"].browse(pickings_to_validate_ids) + return pickings.with_context( + skip_backorder=True, picking_ids_not_to_backorder=self.picking_ids.ids + ).action_validate() + return True + + +class ServiceBackorderConfirmationLine(models.TransientModel): + _name = "service.backorder.confirmation.line" + _description = "Backorder Confirmation Line" + + backorder_confirmation_id = fields.Many2one( + comodel_name="service.backorder.confirmation", string="Immediate Transfer" + ) + picking_id = fields.Many2one(comodel_name="service.picking", string="Transfer") + to_backorder = fields.Boolean(string="To Backorder") diff --git a/purchase_service_picking/wizards/service_backorder_confirmation_views.xml b/purchase_service_picking/wizards/service_backorder_confirmation_views.xml new file mode 100644 index 00000000000..27e6c514977 --- /dev/null +++ b/purchase_service_picking/wizards/service_backorder_confirmation_views.xml @@ -0,0 +1,42 @@ + + + + service_backorder_confirmation + service.backorder.confirmation + +
+ +

+ You have processed less services than the initial demand. +

+ Create a backorder if you expect to process the remaining + services later. Do not create a backorder if you will not + process the remaining services. +

+
+ + + + + + + +
+
+ +
+
+
diff --git a/purchase_service_picking/wizards/service_immediate_transfer.py b/purchase_service_picking/wizards/service_immediate_transfer.py new file mode 100644 index 00000000000..4c3bc8c3a34 --- /dev/null +++ b/purchase_service_picking/wizards/service_immediate_transfer.py @@ -0,0 +1,66 @@ +# Copyright 2025 Tecnativa - Víctor Martínez +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl) + +from odoo import api, fields, models + + +class ServiceImmediateTransfer(models.TransientModel): + _name = "service.immediate.transfer" + _description = "Immediate Transfer" + + picking_ids = fields.Many2many( + comodel_name="service.picking", relation="service_picking_transfer_rel" + ) + line_ids = fields.One2many( + comodel_name="service.immediate.transfer.line", + inverse_name="immediate_transfer_id", + string="Lines", + ) + + @api.model + def default_get(self, fields): + res = super().default_get(fields) + if "line_ids" in fields and res.get("picking_ids"): + res["line_ids"] = [ + (0, 0, {"to_immediate": True, "picking_id": pick_id}) + for pick_id in res["picking_ids"][0][2] + ] + return res + + def process(self): + pickings_to_do = self.env["service.picking"] + pickings_not_to_do = self.env["service.picking"] + for line in self.line_ids: + if line.to_immediate: + pickings_to_do |= line.picking_id + else: + pickings_not_to_do |= line.picking_id + + for line in pickings_to_do.line_ids: + line.quantity_done = line.product_qty + + pickings_to_validate = self.env.context.get("action_validate_picking_ids") + if pickings_to_validate: + pickings_to_validate = self.env["service.picking"].browse( + pickings_to_validate + ) + pickings_to_validate = pickings_to_validate - pickings_not_to_do + return pickings_to_validate.with_context( + skip_immediate=True + ).action_validate() + return True + + +class ServiceImmediateTransferLine(models.TransientModel): + _name = "service.immediate.transfer.line" + _description = "Immediate Transfer Line" + + immediate_transfer_id = fields.Many2one( + comodel_name="service.immediate.transfer", + string="Immediate Transfer", + required=True, + ) + picking_id = fields.Many2one( + comodel_name="service.picking", string="Transfer", required=True + ) + to_immediate = fields.Boolean(string="To Process") diff --git a/purchase_service_picking/wizards/service_immediate_transfer_views.xml b/purchase_service_picking/wizards/service_immediate_transfer_views.xml new file mode 100644 index 00000000000..8fa7093498b --- /dev/null +++ b/purchase_service_picking/wizards/service_immediate_transfer_views.xml @@ -0,0 +1,32 @@ + + + + service.immediate.transfer.view.form + service.immediate.transfer + +
+ +

You have not recorded done quantities yet, by clicking on apply Odoo will process all the quantities.

+
+ + + + + + + +
+
+ +
+
+
diff --git a/purchase_service_picking/wizards/service_picking_return.py b/purchase_service_picking/wizards/service_picking_return.py new file mode 100644 index 00000000000..decce11a1f0 --- /dev/null +++ b/purchase_service_picking/wizards/service_picking_return.py @@ -0,0 +1,155 @@ +# Copyright 2025 Tecnativa - Víctor Martínez +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl) + +from odoo import _, api, fields, models +from odoo.exceptions import UserError +from odoo.tools.float_utils import float_compare, float_is_zero + + +class ServiceReturnPicking(models.TransientModel): + _name = "service.return.picking" + _description = "Service Return Picking" + + @api.model + def default_get(self, fields): + if len(self.env.context.get("active_ids", list())) > 1: + raise UserError(_("You may only return one picking at a time.")) + res = super().default_get(fields) + if ( + self.env.context.get("active_id") + and self.env.context.get("active_model") == "service.picking" + ): + picking = self.env["service.picking"].browse( + self.env.context.get("active_id") + ) + if picking.exists(): + res.update({"picking_id": picking.id}) + return res + + picking_id = fields.Many2one(comodel_name="service.picking") + product_return_lines = fields.One2many( + comodel_name="service.return.picking.line", + inverse_name="wizard_id", + string="Lines", + ) + company_id = fields.Many2one(related="picking_id.company_id") + + @api.onchange("picking_id") + def _onchange_picking_id(self): + product_return_lines = [(5,)] + if self.picking_id and self.picking_id.state != "done": + raise UserError(_("You may only return Done pickings.")) + spl_model = self.env["service.return.picking.line"] + line_fields = [f for f in spl_model._fields.keys()] + product_return_lines_data_tmpl = spl_model.default_get(line_fields) + for line in self.picking_id.line_ids: + product_return_lines_data = dict(product_return_lines_data_tmpl) + product_return_lines_data.update( + self._prepare_service_return_picking_line_vals(line) + ) + product_return_lines.append((0, 0, product_return_lines_data)) + if self.picking_id and not product_return_lines: + raise UserError( + _( + "No products to return (only lines in Done state and not fully " + "returned yet can be returned)." + ) + ) + if self.picking_id: + self.product_return_lines = product_return_lines + + @api.model + def _prepare_service_return_picking_line_vals(self, line): + return { + "product_id": line.product_id.id, + "quantity": line.product_qty, + "line_id": line.id, + "uom_id": line.product_id.uom_id.id, + } + + def _create_return(self): + precision_digits = self.env["decimal.precision"].precision_get( + "Product Unit of Measure" + ) + for line in self.product_return_lines: + if float_is_zero(line.quantity, precision_digits=precision_digits): + raise UserError(_("Please specify at least one non-zero quantity.")) + elif ( + float_compare( + line.quantity, + line.line_id.quantity_done, + precision_digits=precision_digits, + ) + > 0 + ): + raise UserError( + _("You cannot set a quantity greater than the quantity done.") + ) + picking_line_vals = [] + new_picking = self.picking_id.copy( + { + "state": "in_progress", + "origin": _("Return of %s", self.picking_id.name), + } + ) + new_picking.message_post_with_view( + "mail.message_origin_link", + values={"self": new_picking, "origin": self.picking_id}, + subtype_id=self.env.ref("mail.mt_note").id, + ) + for line in self.product_return_lines: + if float_is_zero(line.quantity, precision_digits=precision_digits): + continue + picking_line_vals.append(line._prepare_picking_line_vals(new_picking)) + self.env["service.picking.line"].create(picking_line_vals) + return new_picking + + def create_returns(self): + new_picking = self._create_return() + return { + "name": _("Returned Picking"), + "view_mode": "form,tree", + "res_model": "service.picking", + "res_id": new_picking.id, + "type": "ir.actions.act_window", + "context": dict(self.env.context), + } + + +class ServiceReturnPickingLine(models.TransientModel): + _name = "service.return.picking.line" + _rec_name = "product_id" + _description = "Service Return Picking Line" + + product_id = fields.Many2one( + comodel_name="product.product", + string="Product", + required=True, + domain="[('id', '=', product_id)]", + ) + quantity = fields.Float( + string="Quantity", digits="Product Unit of Measure", required=True + ) + uom_id = fields.Many2one( + comodel_name="uom.uom", + string="Unit of Measure", + related="line_id.product_uom_id", + ) + wizard_id = fields.Many2one(comodel_name="service.return.picking", string="Wizard") + line_id = fields.Many2one( + comodel_name="service.picking.line", + string="Line", + required=True, + ) + + def _prepare_picking_line_vals(self, new_picking): + return { + "product_id": self.product_id.id, + "product_qty": self.quantity, + "product_uom_id": self.uom_id.id, + "picking_id": new_picking.id, + "date": fields.Datetime.now(), + "origin_returned_line_id": self.line_id.id, + "purchase_line_id": self.line_id.purchase_line_id.id, + "name": self.line_id.name, + } diff --git a/purchase_service_picking/wizards/service_picking_return_views.xml b/purchase_service_picking/wizards/service_picking_return_views.xml new file mode 100644 index 00000000000..eaf6845d530 --- /dev/null +++ b/purchase_service_picking/wizards/service_picking_return_views.xml @@ -0,0 +1,45 @@ + + + + Reverse Service Picking + service.return.picking + form + new + + + Return lines + service.return.picking + +
+ + + + + + + + + + + + + + + + + +
+
+
diff --git a/setup/purchase_service_picking/odoo/addons/purchase_service_picking b/setup/purchase_service_picking/odoo/addons/purchase_service_picking new file mode 120000 index 00000000000..7a237697e8d --- /dev/null +++ b/setup/purchase_service_picking/odoo/addons/purchase_service_picking @@ -0,0 +1 @@ +../../../../purchase_service_picking \ No newline at end of file diff --git a/setup/purchase_service_picking/setup.py b/setup/purchase_service_picking/setup.py new file mode 100644 index 00000000000..28c57bb6403 --- /dev/null +++ b/setup/purchase_service_picking/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +)