Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 99 additions & 0 deletions repair_sale_multi/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
=================
Repair Sale Multi
=================

..
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:9307b36d8ef4b6fe2e8fe3a6d25c99a58b59d15f293577fef1bcfe0048c5e909
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

.. |badge1| image:: https://img.shields.io/badge/maturity-Alpha-red.png
:target: https://odoo-community.org/page/development-status
:alt: Alpha
.. |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%2Frepair-lightgray.png?logo=github
:target: https://github.com/OCA/repair/tree/18.0/repair_sale_multi
:alt: OCA/repair
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
:target: https://translation.odoo-community.org/projects/repair-18-0/repair-18-0-repair_sale_multi
: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/repair&target_branch=18.0
:alt: Try me on Runboat

|badge1| |badge2| |badge3| |badge4| |badge5|

This module extends the repair workflow introduced in Odoo 17 by
providing tools to ease the transition to quotation-based invoicing. It
allows users to create a single quotation from multiple repair orders.

.. IMPORTANT::
This is an alpha version, the data model and design can change at any time without warning.
Only for development or testing purpose, do not use in production.
`More details on development status <https://odoo-community.org/page/development-status>`_

**Table of contents**

.. contents::
:local:

Usage
=====

From the repair order list view, select multiple repair orders and use
the **"Create Quotation"** action to generate a new quotation.

Bug Tracker
===========

Bugs are tracked on `GitHub Issues <https://github.com/OCA/repair/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 <https://github.com/OCA/repair/issues/new?body=module:%20repair_sale_multi%0Aversion:%2018.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.

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

Credits
=======

Authors
-------

* ForgeFlow

Contributors
------------

- `ForgeFlow <https://forgeflow.com>`__:

- Aaron Henriquez <aaron.henriquez@forgeflow.com>

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-AaronHForgeFlow| image:: https://github.com/AaronHForgeFlow.png?size=40px
:target: https://github.com/AaronHForgeFlow
:alt: AaronHForgeFlow

Current `maintainer <https://odoo-community.org/page/maintainer-role>`__:

|maintainer-AaronHForgeFlow|

This module is part of the `OCA/repair <https://github.com/OCA/repair/tree/18.0/repair_sale_multi>`_ project on GitHub.

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
1 change: 1 addition & 0 deletions repair_sale_multi/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import models, wizards
20 changes: 20 additions & 0 deletions repair_sale_multi/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Copyright 2025 ForgeFlow S.L.
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).

{
"name": "Repair Sale Multi",
"version": "18.0.1.0.0",
"author": "ForgeFlow, Odoo Community Association (OCA)",
"website": "https://github.com/OCA/repair",
"summary": "Repair Sale Multi",
"category": "Repair",
"depends": ["repair"],
"data": [
"security/ir.model.access.csv",
"views/repair_order_views.xml",
],
"installable": True,
"development_status": "Alpha",
"license": "AGPL-3",
"maintainers": ["AaronHForgeFlow"],
}
92 changes: 92 additions & 0 deletions repair_sale_multi/i18n/es_ES.po
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * repair_sale_multi
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 18.0+e\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-09-09 13:19+0000\n"
"PO-Revision-Date: 2025-09-09 13:19+0000\n"
"Last-Translator: \n"
"Language-Team: \n"
"Language: es_ES\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"

#. module: repair_sale_multi
#. odoo-python
#: code:addons/repair_sale_multi/wizards/repair_create_sale_order_wizard.py:0
msgid "All repair orders must belong to the same customer."
msgstr "Todas las órdenes de reparación deben pertenecer al mismo cliente."

#. module: repair_sale_multi
#. odoo-python
#: code:addons/repair_sale_multi/models/repair_order.py:0
msgid "All selected repair orders must have the same customer defined."
msgstr "Todas las órdenes de reparación seleccionadas deben tener el mismo cliente definido."

#. module: repair_sale_multi
#. odoo-python
#: code:addons/repair_sale_multi/models/repair_order.py:0
msgid "All selected repair orders must have the same picking type."
msgstr "Todas las órdenes de reparación seleccionadas deben tener el mismo tipo de picking."

#. module: repair_sale_multi
#: model:ir.actions.server,name:repair_sale_multi.action_create_sale_order_from_repairs
msgid "Create Quotation"
msgstr "Crear Presupuesto"

#. module: repair_sale_multi
#: model:ir.model,name:repair_sale_multi.model_repair_create_sale_order_wizard
msgid "Create Quotation from Repair Orders"
msgstr "Crear Presupuesto desde Órdenes de Reparación"

#. module: repair_sale_multi
#: model:ir.model.fields,field_description:repair_sale_multi.field_repair_create_sale_order_wizard__create_uid
msgid "Created by"
msgstr "Creado por"

#. module: repair_sale_multi
#: model:ir.model.fields,field_description:repair_sale_multi.field_repair_create_sale_order_wizard__create_date
msgid "Created on"
msgstr "Creado el"

#. module: repair_sale_multi
#: model:ir.model.fields,field_description:repair_sale_multi.field_repair_create_sale_order_wizard__display_name
msgid "Display Name"
msgstr "Nombre a Mostrar"

#. module: repair_sale_multi
#: model:ir.model.fields,field_description:repair_sale_multi.field_repair_create_sale_order_wizard__id
msgid "ID"
msgstr "ID"

#. module: repair_sale_multi
#: model:ir.model.fields,field_description:repair_sale_multi.field_repair_create_sale_order_wizard__write_uid
msgid "Last Updated by"
msgstr "Última Actualización por"

#. module: repair_sale_multi
#: model:ir.model.fields,field_description:repair_sale_multi.field_repair_create_sale_order_wizard__write_date
msgid "Last Updated on"
msgstr "Última Actualización el"

#. module: repair_sale_multi
#: model:ir.model,name:repair_sale_multi.model_repair_order
#: model:ir.model.fields,field_description:repair_sale_multi.field_repair_create_sale_order_wizard__repair_order_ids
#: model:ir.model.fields,field_description:repair_sale_multi.field_sale_order_line__repair_order_id
msgid "Repair Order"
msgstr "Orden de Reparación"

#. module: repair_sale_multi
#: model:ir.model,name:repair_sale_multi.model_sale_order_line
msgid "Sales Order Line"
msgstr "Línea de Pedido de Venta"

#. module: repair_sale_multi
#: model:ir.model,name:repair_sale_multi.model_stock_move
msgid "Stock Move"
msgstr "Movimiento de Stock"
91 changes: 91 additions & 0 deletions repair_sale_multi/i18n/repair_sale_multi.pot
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * repair_sale_multi
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 18.0+e\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-09-09 13:19+0000\n"
"PO-Revision-Date: 2025-09-09 13:19+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: repair_sale_multi
#. odoo-python
#: code:addons/repair_sale_multi/wizards/repair_create_sale_order_wizard.py:0
msgid "All repair orders must belong to the same customer."
msgstr ""

#. module: repair_sale_multi
#. odoo-python
#: code:addons/repair_sale_multi/models/repair_order.py:0
msgid "All selected repair orders must have the same customer defined."
msgstr ""

#. module: repair_sale_multi
#. odoo-python
#: code:addons/repair_sale_multi/models/repair_order.py:0
msgid "All selected repair orders must have the same picking type."
msgstr ""

#. module: repair_sale_multi
#: model:ir.actions.server,name:repair_sale_multi.action_create_sale_order_from_repairs
msgid "Create Quotation"
msgstr ""

#. module: repair_sale_multi
#: model:ir.model,name:repair_sale_multi.model_repair_create_sale_order_wizard
msgid "Create Quotation from Repair Orders"
msgstr ""

#. module: repair_sale_multi
#: model:ir.model.fields,field_description:repair_sale_multi.field_repair_create_sale_order_wizard__create_uid
msgid "Created by"
msgstr ""

#. module: repair_sale_multi
#: model:ir.model.fields,field_description:repair_sale_multi.field_repair_create_sale_order_wizard__create_date
msgid "Created on"
msgstr ""

#. module: repair_sale_multi
#: model:ir.model.fields,field_description:repair_sale_multi.field_repair_create_sale_order_wizard__display_name
msgid "Display Name"
msgstr ""

#. module: repair_sale_multi
#: model:ir.model.fields,field_description:repair_sale_multi.field_repair_create_sale_order_wizard__id
msgid "ID"
msgstr ""

#. module: repair_sale_multi
#: model:ir.model.fields,field_description:repair_sale_multi.field_repair_create_sale_order_wizard__write_uid
msgid "Last Updated by"
msgstr ""

#. module: repair_sale_multi
#: model:ir.model.fields,field_description:repair_sale_multi.field_repair_create_sale_order_wizard__write_date
msgid "Last Updated on"
msgstr ""

#. module: repair_sale_multi
#: model:ir.model,name:repair_sale_multi.model_repair_order
#: model:ir.model.fields,field_description:repair_sale_multi.field_repair_create_sale_order_wizard__repair_order_ids
#: model:ir.model.fields,field_description:repair_sale_multi.field_sale_order_line__repair_order_id
msgid "Repair Order"
msgstr ""

#. module: repair_sale_multi
#: model:ir.model,name:repair_sale_multi.model_sale_order_line
msgid "Sales Order Line"
msgstr ""

#. module: repair_sale_multi
#: model:ir.model,name:repair_sale_multi.model_stock_move
msgid "Stock Move"
msgstr ""
3 changes: 3 additions & 0 deletions repair_sale_multi/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from . import repair_order
from . import sale_order_line
from . import stock_move
51 changes: 51 additions & 0 deletions repair_sale_multi/models/repair_order.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# Copyright 2025 ForgeFlow S.L.
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
from odoo import models
from odoo.exceptions import UserError
from odoo.fields import Command


class RepairOrder(models.Model):
_inherit = "repair.order"

def _prepare_combined_sale_order_vals(self, partners, picking_types):
return {
"company_id": self.company_id.id,
"partner_id": partners.id,
"warehouse_id": picking_types.warehouse_id.id,
"repair_order_ids": [Command.link(ro.id) for ro in self],
}

def action_create_combined_sale_order(self):
if not self:
return
already_linked = self.filtered("sale_order_id")
if already_linked:
ref_str = "\n".join(ro.name for ro in already_linked)
error_msg = (
"You cannot create a quotation for repair orders already linked to "
f"a sale order:\n{ref_str}"
Copy link
Contributor

Choose a reason for hiding this comment

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

You can't interpolate a string before it's translated, the dynamic translation term won't ever be matched.

Please use the built-in string interpolation self.env._("This is something: %s", something)

)
raise UserError(self.env._(error_msg))
picking_types = self.mapped("picking_type_id")
if not picking_types or len(picking_types) != 1:
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
if not picking_types or len(picking_types) != 1:
if len(picking_types) != 1:

redundant

raise UserError(
self.env._(
"All selected repair orders must have the same picking type."
)
)

partners = self.mapped("partner_id")
if not partners or len(partners) != 1:
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
if not partners or len(partners) != 1:
if len(partners) != 1:

redundant

raise UserError(
self.env._(
"All selected repair orders must have the same customer defined."
)
)

sale_order_vals = self._prepare_combined_sale_order_vals(
partners, picking_types
)
self.env["sale.order"].create(sale_order_vals)
self.mapped("move_ids")._create_repair_sale_order_line()
return self.action_view_sale_order()
11 changes: 11 additions & 0 deletions repair_sale_multi/models/sale_order_line.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from odoo import fields, models


class SaleOrderLine(models.Model):
_inherit = "sale.order.line"

# to be able to group lines by repair in sections
repair_order_id = fields.Many2one(
"repair.order",
copy=False,
)
23 changes: 23 additions & 0 deletions repair_sale_multi/models/stock_move.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
from odoo import models


class StockMove(models.Model):
_inherit = "stock.move"

def _create_repair_sale_order_line(self):
res = super()._create_repair_sale_order_line()
for move in self:
sale_lines = self.env["sale.order.line"].search(
[
("move_ids", "in", move.id),
(
"order_id",
"=",
move.repair_id.sale_order_id.id
if move.repair_id.sale_order_id
else False,
Comment on lines +17 to +18
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
if move.repair_id.sale_order_id
else False,

redundant. move.repair_id.sale_order_id.id will be False if any recordset in the chain is empty

Copy link
Contributor

Choose a reason for hiding this comment

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

Perhaps you can avoid the whole thing if the move.repair_id.sale_order_id is empty. Cause it makes no sense to do the search in this case

),
]
)
sale_lines.write({"repair_order_id": move.repair_id.id})
return res
Loading