diff --git a/sale_order_line_position/README.rst b/sale_order_line_position/README.rst new file mode 100644 index 00000000..4a413aa9 --- /dev/null +++ b/sale_order_line_position/README.rst @@ -0,0 +1,125 @@ +======================== +Sale 0rder Line Position +======================== + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:a2f028ac3d09416abe5a3131ce82fc402965b9a17c59db23deba5fae9c81e776 + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |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%2Fsale--reporting-lightgray.png?logo=github + :target: https://github.com/OCA/sale-reporting/tree/18.0/sale_order_line_position + :alt: OCA/sale-reporting +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/sale-reporting-18-0/sale-reporting-18-0-sale_order_line_position + :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/sale-reporting&target_branch=18.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| + +This module adds an auto computed position on sale order line. This +position number is also printed on the report. + +The position can be used to keep track of each line during the delivery +and invoicing of the order with the customer. This is why there are +related modules on account-invoice-reporting and +stock-logisics-reporting. + +The positions are recomputed when the sale order is printed, sent and +set to confirm. + +The positions are not changed on the line after the order has been +confirmed, but if new line are added they will receive a position +number. + +An action is also availabled to manually recompute the positions. + +**Table of contents** + +.. contents:: + :local: + +Known issues / Roadmap +====================== + +The way the positions are computed on the create of sale.order.line +record could lead to a performance issue. There is a few improvements +that have been suggested: + +Remove it and handle the computation on the write and/or create method +of the sale.order. + +Have a context key to enable/disable the recomputation. + +Do not set any value in the position fields before the sale order lines +are locked (in the current implementation, before sending). And add a +recompute button in the UI. + +Set the position values with an SQL query using a TRIGGER. + +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 +------- + +* Camptocamp + +Contributors +------------ + +- Thierry Ducrest +- `Trobz `__: + + - Nguyen Hoang Hiep + - Do Anh Duy + - Kien Kim Khoi + +Other credits +------------- + +The migration of this module from 16.0 to 17.0 was financially supported +by Camptocamp + +The migration of this module from 17.0 to 18.0 was financially supported +by: + +- Camptocamp + +Maintainers +----------- + +This module is maintained by the OCA. + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use. + +This module is part of the `OCA/sale-reporting `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/sale_order_line_position/__init__.py b/sale_order_line_position/__init__.py new file mode 100644 index 00000000..0650744f --- /dev/null +++ b/sale_order_line_position/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/sale_order_line_position/__manifest__.py b/sale_order_line_position/__manifest__.py new file mode 100644 index 00000000..f205a625 --- /dev/null +++ b/sale_order_line_position/__manifest__.py @@ -0,0 +1,19 @@ +# Copyright 2021 Camptocamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) + +{ + "name": "Sale Order Line Position", + "summary": "Adds position number on sale order line.", + "version": "18.0.1.0.0", + "category": "Sales", + "author": "Camptocamp, Odoo Community Association (OCA)", + "license": "AGPL-3", + "website": "https://github.com/OCA/sale-reporting", + "depends": ["sale"], + "data": [ + "views/sale_order.xml", + "views/res_config_settings.xml", + "report/sale_order_report.xml", + ], + "installable": True, +} diff --git a/sale_order_line_position/i18n/ca.po b/sale_order_line_position/i18n/ca.po new file mode 100644 index 00000000..c3db54f0 --- /dev/null +++ b/sale_order_line_position/i18n/ca.po @@ -0,0 +1,84 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * sale_order_line_position +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 15.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2022-06-03 10:05+0000\n" +"Last-Translator: jabelchi \n" +"Language-Team: none\n" +"Language: ca\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 4.3.2\n" + +#. module: sale_order_line_position +#: model:ir.model,name:sale_order_line_position.model_res_company +msgid "Companies" +msgstr "" + +#. module: sale_order_line_position +#: model:ir.model,name:sale_order_line_position.model_res_config_settings +msgid "Config Settings" +msgstr "" + +#. module: sale_order_line_position +#: model:ir.model.fields,field_description:sale_order_line_position.field_res_company__disable_sale_position_recompute +#: model:ir.model.fields,field_description:sale_order_line_position.field_res_config_settings__disable_sale_position_recompute +msgid "Do not recompute positions on sale orders" +msgstr "" + +#. module: sale_order_line_position +#: model_terms:ir.ui.view,arch_db:sale_order_line_position.res_config_settings_view_form +msgid "Do not recompute positions when line is removed" +msgstr "" + +#. module: sale_order_line_position +#: model:ir.model.fields,field_description:sale_order_line_position.field_sale_order__locked_positions +msgid "Locked Positions" +msgstr "Posicions bloquejades" + +#. module: sale_order_line_position +#: model_terms:ir.ui.view,arch_db:sale_order_line_position.report_saleorder_document +#: model_terms:ir.ui.view,arch_db:sale_order_line_position.view_order_form_inherit +msgid "Pos" +msgstr "Pos" + +#. module: sale_order_line_position +#: model:ir.model.fields,field_description:sale_order_line_position.field_sale_order_line__position +msgid "Position" +msgstr "Posició" + +#. module: sale_order_line_position +#: model:ir.model.fields,field_description:sale_order_line_position.field_sale_order_line__position_formatted +msgid "Position Formatted" +msgstr "Posició formatada" + +#. module: sale_order_line_position +#: model:ir.actions.server,name:sale_order_line_position.action_compute_sale_position +msgid "Recompute positions" +msgstr "Recalcula posicions" + +#. module: sale_order_line_position +#: model:ir.model,name:sale_order_line_position.model_ir_actions_report +msgid "Report Action" +msgstr "Acció d'informe" + +#. module: sale_order_line_position +#: model_terms:ir.ui.view,arch_db:sale_order_line_position.res_config_settings_view_form +msgid "Sale Order Line Positions" +msgstr "" + +#. module: sale_order_line_position +#: model:ir.model,name:sale_order_line_position.model_sale_order +msgid "Sales Order" +msgstr "Comanda de venda" + +#. module: sale_order_line_position +#: model:ir.model,name:sale_order_line_position.model_sale_order_line +msgid "Sales Order Line" +msgstr "Línia de comanda de venda" diff --git a/sale_order_line_position/i18n/de.po b/sale_order_line_position/i18n/de.po new file mode 100644 index 00000000..c780967a --- /dev/null +++ b/sale_order_line_position/i18n/de.po @@ -0,0 +1,93 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * sale_order_line_position +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 14.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2022-05-11 12:05+0000\n" +"Last-Translator: Maria Sparenberg \n" +"Language-Team: none\n" +"Language: de\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 4.3.2\n" + +#. module: sale_order_line_position +#: model:ir.model,name:sale_order_line_position.model_res_company +msgid "Companies" +msgstr "" + +#. module: sale_order_line_position +#: model:ir.model,name:sale_order_line_position.model_res_config_settings +msgid "Config Settings" +msgstr "" + +#. module: sale_order_line_position +#: model:ir.model.fields,field_description:sale_order_line_position.field_res_company__disable_sale_position_recompute +#: model:ir.model.fields,field_description:sale_order_line_position.field_res_config_settings__disable_sale_position_recompute +msgid "Do not recompute positions on sale orders" +msgstr "" + +#. module: sale_order_line_position +#: model_terms:ir.ui.view,arch_db:sale_order_line_position.res_config_settings_view_form +msgid "Do not recompute positions when line is removed" +msgstr "" + +#. module: sale_order_line_position +#: model:ir.model.fields,field_description:sale_order_line_position.field_sale_order__locked_positions +msgid "Locked Positions" +msgstr "Gesperrte Positionen" + +#. module: sale_order_line_position +#: model_terms:ir.ui.view,arch_db:sale_order_line_position.report_saleorder_document +#: model_terms:ir.ui.view,arch_db:sale_order_line_position.view_order_form_inherit +msgid "Pos" +msgstr "Pos" + +#. module: sale_order_line_position +#: model:ir.model.fields,field_description:sale_order_line_position.field_sale_order_line__position +msgid "Position" +msgstr "Position" + +#. module: sale_order_line_position +#: model:ir.model.fields,field_description:sale_order_line_position.field_sale_order_line__position_formatted +msgid "Position Formatted" +msgstr "" + +#. module: sale_order_line_position +#: model:ir.actions.server,name:sale_order_line_position.action_compute_sale_position +msgid "Recompute positions" +msgstr "Positionen neuberechnen" + +#. module: sale_order_line_position +#: model:ir.model,name:sale_order_line_position.model_ir_actions_report +msgid "Report Action" +msgstr "" + +#. module: sale_order_line_position +#: model_terms:ir.ui.view,arch_db:sale_order_line_position.res_config_settings_view_form +msgid "Sale Order Line Positions" +msgstr "" + +#. module: sale_order_line_position +#: model:ir.model,name:sale_order_line_position.model_sale_order +msgid "Sales Order" +msgstr "Verkaufsauftrag" + +#. module: sale_order_line_position +#: model:ir.model,name:sale_order_line_position.model_sale_order_line +msgid "Sales Order Line" +msgstr "Auftragsposition" + +#~ msgid "Display Name" +#~ msgstr "Anzeigename" + +#~ msgid "ID" +#~ msgstr "ID" + +#~ msgid "Last Modified on" +#~ msgstr "Zuletzt geändert am" diff --git a/sale_order_line_position/i18n/it.po b/sale_order_line_position/i18n/it.po new file mode 100644 index 00000000..630be32c --- /dev/null +++ b/sale_order_line_position/i18n/it.po @@ -0,0 +1,84 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * sale_order_line_position +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 17.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2024-05-22 10:39+0000\n" +"Last-Translator: mymage \n" +"Language-Team: none\n" +"Language: it\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 4.17\n" + +#. module: sale_order_line_position +#: model:ir.model,name:sale_order_line_position.model_res_company +msgid "Companies" +msgstr "Aziende" + +#. module: sale_order_line_position +#: model:ir.model,name:sale_order_line_position.model_res_config_settings +msgid "Config Settings" +msgstr "Impostazioni configurazione" + +#. module: sale_order_line_position +#: model:ir.model.fields,field_description:sale_order_line_position.field_res_company__disable_sale_position_recompute +#: model:ir.model.fields,field_description:sale_order_line_position.field_res_config_settings__disable_sale_position_recompute +msgid "Do not recompute positions on sale orders" +msgstr "Non ricalcolare le posizioni negli ordini di vendita" + +#. module: sale_order_line_position +#: model_terms:ir.ui.view,arch_db:sale_order_line_position.res_config_settings_view_form +msgid "Do not recompute positions when line is removed" +msgstr "Non ricalcolare le posizioni quando viene rimossa la riga" + +#. module: sale_order_line_position +#: model:ir.model.fields,field_description:sale_order_line_position.field_sale_order__locked_positions +msgid "Locked Positions" +msgstr "Posizioni bloccate" + +#. module: sale_order_line_position +#: model_terms:ir.ui.view,arch_db:sale_order_line_position.report_saleorder_document +#: model_terms:ir.ui.view,arch_db:sale_order_line_position.view_order_form_inherit +msgid "Pos" +msgstr "Pos" + +#. module: sale_order_line_position +#: model:ir.model.fields,field_description:sale_order_line_position.field_sale_order_line__position +msgid "Position" +msgstr "Posizione" + +#. module: sale_order_line_position +#: model:ir.model.fields,field_description:sale_order_line_position.field_sale_order_line__position_formatted +msgid "Position Formatted" +msgstr "Posizione formattata" + +#. module: sale_order_line_position +#: model:ir.actions.server,name:sale_order_line_position.action_compute_sale_position +msgid "Recompute positions" +msgstr "Ricalcolo posizioni" + +#. module: sale_order_line_position +#: model:ir.model,name:sale_order_line_position.model_ir_actions_report +msgid "Report Action" +msgstr "Azione resoconto" + +#. module: sale_order_line_position +#: model_terms:ir.ui.view,arch_db:sale_order_line_position.res_config_settings_view_form +msgid "Sale Order Line Positions" +msgstr "Posizioni riga ordine di vendita" + +#. module: sale_order_line_position +#: model:ir.model,name:sale_order_line_position.model_sale_order +msgid "Sales Order" +msgstr "Ordine di vendita" + +#. module: sale_order_line_position +#: model:ir.model,name:sale_order_line_position.model_sale_order_line +msgid "Sales Order Line" +msgstr "Riga ordine di vendita" diff --git a/sale_order_line_position/i18n/sale_order_line_position.pot b/sale_order_line_position/i18n/sale_order_line_position.pot new file mode 100644 index 00000000..419d05a9 --- /dev/null +++ b/sale_order_line_position/i18n/sale_order_line_position.pot @@ -0,0 +1,81 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * sale_order_line_position +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 17.0\n" +"Report-Msgid-Bugs-To: \n" +"Last-Translator: \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: sale_order_line_position +#: model:ir.model,name:sale_order_line_position.model_res_company +msgid "Companies" +msgstr "" + +#. module: sale_order_line_position +#: model:ir.model,name:sale_order_line_position.model_res_config_settings +msgid "Config Settings" +msgstr "" + +#. module: sale_order_line_position +#: model:ir.model.fields,field_description:sale_order_line_position.field_res_company__disable_sale_position_recompute +#: model:ir.model.fields,field_description:sale_order_line_position.field_res_config_settings__disable_sale_position_recompute +msgid "Do not recompute positions on sale orders" +msgstr "" + +#. module: sale_order_line_position +#: model_terms:ir.ui.view,arch_db:sale_order_line_position.res_config_settings_view_form +msgid "Do not recompute positions when line is removed" +msgstr "" + +#. module: sale_order_line_position +#: model:ir.model.fields,field_description:sale_order_line_position.field_sale_order__locked_positions +msgid "Locked Positions" +msgstr "" + +#. module: sale_order_line_position +#: model_terms:ir.ui.view,arch_db:sale_order_line_position.report_saleorder_document +#: model_terms:ir.ui.view,arch_db:sale_order_line_position.view_order_form_inherit +msgid "Pos" +msgstr "" + +#. module: sale_order_line_position +#: model:ir.model.fields,field_description:sale_order_line_position.field_sale_order_line__position +msgid "Position" +msgstr "" + +#. module: sale_order_line_position +#: model:ir.model.fields,field_description:sale_order_line_position.field_sale_order_line__position_formatted +msgid "Position Formatted" +msgstr "" + +#. module: sale_order_line_position +#: model:ir.actions.server,name:sale_order_line_position.action_compute_sale_position +msgid "Recompute positions" +msgstr "" + +#. module: sale_order_line_position +#: model:ir.model,name:sale_order_line_position.model_ir_actions_report +msgid "Report Action" +msgstr "" + +#. module: sale_order_line_position +#: model_terms:ir.ui.view,arch_db:sale_order_line_position.res_config_settings_view_form +msgid "Sale Order Line Positions" +msgstr "" + +#. module: sale_order_line_position +#: model:ir.model,name:sale_order_line_position.model_sale_order +msgid "Sales Order" +msgstr "" + +#. module: sale_order_line_position +#: model:ir.model,name:sale_order_line_position.model_sale_order_line +msgid "Sales Order Line" +msgstr "" diff --git a/sale_order_line_position/models/__init__.py b/sale_order_line_position/models/__init__.py new file mode 100644 index 00000000..d5eab717 --- /dev/null +++ b/sale_order_line_position/models/__init__.py @@ -0,0 +1,5 @@ +from . import ir_actions_report +from . import sale_order +from . import sale_order_line +from . import res_company +from . import res_config_settings diff --git a/sale_order_line_position/models/ir_actions_report.py b/sale_order_line_position/models/ir_actions_report.py new file mode 100644 index 00000000..ab9fd54f --- /dev/null +++ b/sale_order_line_position/models/ir_actions_report.py @@ -0,0 +1,18 @@ +# Copyright 2021 Camptocamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html) + +from odoo import models + + +class IrActionsReport(models.Model): + _inherit = "ir.actions.report" + + def _render_qweb_pdf(self, report_ref, res_ids=None, data=None): + self.sale_recompute_positions(report_ref, res_ids) + return super()._render_qweb_pdf(report_ref, res_ids, data) + + def sale_recompute_positions(self, report_ref, res_ids): + report_sudo = self._get_report(report_ref) + if report_sudo.model == "sale.order": + sales = self.env["sale.order"].browse(res_ids) + sales.recompute_positions() diff --git a/sale_order_line_position/models/res_company.py b/sale_order_line_position/models/res_company.py new file mode 100644 index 00000000..345fbdfb --- /dev/null +++ b/sale_order_line_position/models/res_company.py @@ -0,0 +1,12 @@ +# Copyright 2022 Camptocamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html) + +from odoo import fields, models + + +class ResCompany(models.Model): + _inherit = "res.company" + + disable_sale_position_recompute = fields.Boolean( + string="Do not recompute positions on sale orders" + ) diff --git a/sale_order_line_position/models/res_config_settings.py b/sale_order_line_position/models/res_config_settings.py new file mode 100644 index 00000000..cf228b95 --- /dev/null +++ b/sale_order_line_position/models/res_config_settings.py @@ -0,0 +1,13 @@ +# Copyright 2022 Camptocamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html) + +from odoo import fields, models + + +class ResConfigSettings(models.TransientModel): + _inherit = "res.config.settings" + + disable_sale_position_recompute = fields.Boolean( + related="company_id.disable_sale_position_recompute", + readonly=False, + ) diff --git a/sale_order_line_position/models/sale_order.py b/sale_order_line_position/models/sale_order.py new file mode 100644 index 00000000..33087aac --- /dev/null +++ b/sale_order_line_position/models/sale_order.py @@ -0,0 +1,35 @@ +# Copyright 2021 Camptocamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html) + +from odoo import api, fields, models + + +class SaleOrder(models.Model): + _inherit = "sale.order" + + locked_positions = fields.Boolean(compute="_compute_locked_positions") + + @api.depends("state") + def _compute_locked_positions(self): + for record in self: + record.locked_positions = record.state != "draft" + + def action_confirm(self): + self.recompute_positions() + return super().action_confirm() + + def action_quotation_send(self): + self.recompute_positions() + return super().action_quotation_send() + + def recompute_positions(self): + for sale in self: + if sale.locked_positions or sale.company_id.disable_sale_position_recompute: + continue + sale._recompute_positions() + + def _recompute_positions(self): + lines = self.order_line.filtered(lambda line: not line.display_type) + lines.sorted(key=lambda x: (x.sequence, x.id)) + for position, line in enumerate(lines, start=1): + line.position = position diff --git a/sale_order_line_position/models/sale_order_line.py b/sale_order_line_position/models/sale_order_line.py new file mode 100644 index 00000000..60af976c --- /dev/null +++ b/sale_order_line_position/models/sale_order_line.py @@ -0,0 +1,59 @@ +# Copyright 2021 Camptocamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html) + +from odoo import api, fields, models + + +class SaleOrderLine(models.Model): + _inherit = "sale.order.line" + + position = fields.Integer(readonly=True, index=True, default=False) + position_formatted = fields.Char(compute="_compute_position_formatted") + + @api.depends("position") + def _compute_position_formatted(self): + for record in self: + record.position_formatted = record._format_position(record.position) + + @api.model_create_multi + def create(self, vals_list): + vals_list = self._add_next_position_on_new_line(vals_list) + return super().create(vals_list) + + def unlink(self): + sales = self.mapped("order_id") + res = super().unlink() + for sale in sales: + sale.recompute_positions() + return res + + def _add_next_position_on_new_line(self, vals_list): + sale_ids = [ + line["order_id"] + for line in vals_list + if not line.get("display_type") and line.get("order_id") + ] + if sale_ids: + ids = tuple(set(sale_ids)) + self.flush_model() + query = """ + SELECT order_id, max(position) FROM sale_order_line + WHERE order_id in %s GROUP BY order_id; + """ + self.env.cr.execute(query, (ids,)) + default_pos = {key: 1 for key in ids} + existing_pos = { + order_id: pos + 1 for order_id, pos in self.env.cr.fetchall() + } + sale_pos = {**default_pos, **existing_pos} + for line in vals_list: + if not line.get("display_type"): + line["position"] = sale_pos[line["order_id"]] + sale_pos[line["order_id"]] += 1 + return vals_list + + @api.model + def _format_position(self, position): + if not position: + return "" + return str(position).zfill(3) diff --git a/sale_order_line_position/pyproject.toml b/sale_order_line_position/pyproject.toml new file mode 100644 index 00000000..4231d0cc --- /dev/null +++ b/sale_order_line_position/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["whool"] +build-backend = "whool.buildapi" diff --git a/sale_order_line_position/readme/CONTRIBUTORS.md b/sale_order_line_position/readme/CONTRIBUTORS.md new file mode 100644 index 00000000..83abfe9f --- /dev/null +++ b/sale_order_line_position/readme/CONTRIBUTORS.md @@ -0,0 +1,5 @@ +- Thierry Ducrest \<\> +- [Trobz](https://trobz.com): + - Nguyen Hoang Hiep \<\> + - Do Anh Duy \<\> + - Kien Kim Khoi \<\> diff --git a/sale_order_line_position/readme/CREDITS.md b/sale_order_line_position/readme/CREDITS.md new file mode 100644 index 00000000..0ef17a50 --- /dev/null +++ b/sale_order_line_position/readme/CREDITS.md @@ -0,0 +1,5 @@ +The migration of this module from 16.0 to 17.0 was financially supported +by Camptocamp + +The migration of this module from 17.0 to 18.0 was financially supported by: + - Camptocamp diff --git a/sale_order_line_position/readme/DESCRIPTION.md b/sale_order_line_position/readme/DESCRIPTION.md new file mode 100644 index 00000000..20963d68 --- /dev/null +++ b/sale_order_line_position/readme/DESCRIPTION.md @@ -0,0 +1,16 @@ +This module adds an auto computed position on sale order line. This +position number is also printed on the report. + +The position can be used to keep track of each line during the delivery +and invoicing of the order with the customer. This is why there are +related modules on account-invoice-reporting and +stock-logisics-reporting. + +The positions are recomputed when the sale order is printed, sent and +set to confirm. + +The positions are not changed on the line after the order has been +confirmed, but if new line are added they will receive a position +number. + +An action is also availabled to manually recompute the positions. diff --git a/sale_order_line_position/readme/ROADMAP.md b/sale_order_line_position/readme/ROADMAP.md new file mode 100644 index 00000000..75a9d0a2 --- /dev/null +++ b/sale_order_line_position/readme/ROADMAP.md @@ -0,0 +1,14 @@ +The way the positions are computed on the create of sale.order.line +record could lead to a performance issue. There is a few improvements +that have been suggested: + +Remove it and handle the computation on the write and/or create method +of the sale.order. + +Have a context key to enable/disable the recomputation. + +Do not set any value in the position fields before the sale order lines +are locked (in the current implementation, before sending). And add a +recompute button in the UI. + +Set the position values with an SQL query using a TRIGGER. diff --git a/sale_order_line_position/report/sale_order_report.xml b/sale_order_line_position/report/sale_order_report.xml new file mode 100644 index 00000000..8a87f0d7 --- /dev/null +++ b/sale_order_line_position/report/sale_order_report.xml @@ -0,0 +1,16 @@ + + + + diff --git a/sale_order_line_position/static/description/icon.png b/sale_order_line_position/static/description/icon.png new file mode 100644 index 00000000..3a0328b5 Binary files /dev/null and b/sale_order_line_position/static/description/icon.png differ diff --git a/sale_order_line_position/static/description/index.html b/sale_order_line_position/static/description/index.html new file mode 100644 index 00000000..05bf3a5c --- /dev/null +++ b/sale_order_line_position/static/description/index.html @@ -0,0 +1,465 @@ + + + + + +Sale 0rder Line Position + + + +
+

Sale 0rder Line Position

+ + +

Beta License: AGPL-3 OCA/sale-reporting Translate me on Weblate Try me on Runboat

+

This module adds an auto computed position on sale order line. This +position number is also printed on the report.

+

The position can be used to keep track of each line during the delivery +and invoicing of the order with the customer. This is why there are +related modules on account-invoice-reporting and +stock-logisics-reporting.

+

The positions are recomputed when the sale order is printed, sent and +set to confirm.

+

The positions are not changed on the line after the order has been +confirmed, but if new line are added they will receive a position +number.

+

An action is also availabled to manually recompute the positions.

+

Table of contents

+ +
+

Known issues / Roadmap

+

The way the positions are computed on the create of sale.order.line +record could lead to a performance issue. There is a few improvements +that have been suggested:

+

Remove it and handle the computation on the write and/or create method +of the sale.order.

+

Have a context key to enable/disable the recomputation.

+

Do not set any value in the position fields before the sale order lines +are locked (in the current implementation, before sending). And add a +recompute button in the UI.

+

Set the position values with an SQL query using a TRIGGER.

+
+
+

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

+
    +
  • Camptocamp
  • +
+
+
+

Contributors

+ +
+
+

Other credits

+

The migration of this module from 16.0 to 17.0 was financially supported +by Camptocamp

+

The migration of this module from 17.0 to 18.0 was financially supported +by:

+
    +
  • Camptocamp
  • +
+
+
+

Maintainers

+

This module is maintained by the OCA.

+ +Odoo Community Association + +

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

+

This module is part of the OCA/sale-reporting project on GitHub.

+

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

+
+
+
+ + diff --git a/sale_order_line_position/tests/__init__.py b/sale_order_line_position/tests/__init__.py new file mode 100644 index 00000000..8636f14d --- /dev/null +++ b/sale_order_line_position/tests/__init__.py @@ -0,0 +1 @@ +from . import test_sale_order_line_position diff --git a/sale_order_line_position/tests/test_sale_order_line_position.py b/sale_order_line_position/tests/test_sale_order_line_position.py new file mode 100644 index 00000000..d273fa34 --- /dev/null +++ b/sale_order_line_position/tests/test_sale_order_line_position.py @@ -0,0 +1,90 @@ +# Copyright 2021 Camptocamp SA +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl) +from odoo.tests import TransactionCase + + +class TestSaleOrderLinePosition(TransactionCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.env = cls.env(context=dict(cls.env.context, tracking_disable=True)) + cls.partner = cls.env.ref("base.res_partner_12") + cls.product = cls.env.ref("product.product_product_9") + cls.order = cls.env["sale.order"].create( + { + "partner_id": cls.partner.id, + "order_line": [ + ( + 0, + 0, + { + "product_id": cls.product.id, + "product_uom": cls.product.uom_id.id, + "product_uom_qty": 3.0, + }, + ), + (0, 0, {"display_type": "line_section", "name": "Section"}), + ( + 0, + 0, + { + "product_id": cls.product.id, + "product_uom": cls.product.uom_id.id, + "product_uom_qty": 5.0, + }, + ), + ], + } + ) + + def test_new_line_position(self): + """Check that new line created get a new incremental position number.""" + line1 = self.order.order_line[0] + self.assertEqual(line1.position, 1) + self.assertEqual(line1.position_formatted, "001") + line2 = self.order.order_line[1] + self.assertEqual(line2.position, 0) + self.assertEqual(line2.position_formatted, "") + line3 = self.env["sale.order.line"].create( + [ + { + "order_id": self.order.id, + "product_id": self.product.id, + "product_uom": self.product.uom_id.id, + "product_uom_qty": 9.0, + }, + ] + ) + self.assertEqual(line3.position, 3) + self.assertEqual(line3.position_formatted, "003") + + def test_unlink_line(self): + """Check that when line are being removed position are recomputed.""" + self.order.order_line[0].unlink() + self.assertEqual(len(self.order.order_line), 2) + self.assertEqual(self.order.order_line[1].position, 1) + + def test_unlink_no_recompute_line(self): + """Check that when parameter disable_sale_position_recompute is True + and line are being removed position are not recomputed.""" + self.order.company_id.disable_sale_position_recompute = True + self.order.order_line[0].unlink() + self.assertEqual(len(self.order.order_line), 2) + self.assertEqual(self.order.order_line[1].position, 2) + + def test_locked_positions(self): + """Check that when order is sent, position are not recomputed.""" + new_line = self.env["sale.order.line"].create( + [ + { + "order_id": self.order.id, + "product_id": self.product.id, + "product_uom": self.product.uom_id.id, + "product_uom_qty": 15.0, + }, + ] + ) + self.assertEqual(new_line.position, 3) + self.order.state = "sent" + self.order.order_line[0].unlink() + self.assertEqual(new_line.position, 3) diff --git a/sale_order_line_position/views/res_config_settings.xml b/sale_order_line_position/views/res_config_settings.xml new file mode 100644 index 00000000..7a3e167d --- /dev/null +++ b/sale_order_line_position/views/res_config_settings.xml @@ -0,0 +1,23 @@ + + + + sale.config.positioning.form + res.config.settings + + + + + + + + + + + + diff --git a/sale_order_line_position/views/sale_order.xml b/sale_order_line_position/views/sale_order.xml new file mode 100644 index 00000000..05347861 --- /dev/null +++ b/sale_order_line_position/views/sale_order.xml @@ -0,0 +1,33 @@ + + + + sale.order.form.sale.inherit + sale.order + + 100 + + + + + + + + + + + + Recompute positions + + + code + + records.recompute_positions() + + +