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
54 changes: 54 additions & 0 deletions sale_product_origin_report/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
.. image:: https://img.shields.io/badge/license-AGPL--3-blue.svg
:target: https://opensource.org/licenses/AGPL-3.0
:alt: License: AGPL-3

==========================
Sale Product Origin Report
==========================

This module extends sale quotation/order reports and adds product extra information
to each regular sale order line:

* Product name as line title (first line only)
* Product origin from sales description (``description_sale``)
* Optional external product URL (if available and valid)

Behavior
========

* The product name is shown in the Description column.
* If a valid product URL exists, product name is shown as a clickable link.
* If product sales description exists, it is shown below as ``Origin: ...``.
* Invalid URL placeholders such as ``0.0`` are ignored.
* If URL/origin do not exist, report remains unchanged for that line.

Compatibility
=============

URL lookup is compatible with these optional modules:

* ``product_variant_url`` (``external_url``)
* ``website_sale_product_external_link`` (``website_link``)

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

Bugs are tracked on `GitHub Issues
<https://github.com/avanzosc/odoo-addons/issues>`_. In case of trouble,
please check there if your issue has already been reported. If you spotted
it first, help us smash it by providing detailed and welcomed feedback.

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

Credits
=======

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

* AvanzOSC

License
=======

This project is licensed under the AGPL-3 License. For more details, refer to the LICENSE file or visit <https://opensource.org/licenses/AGPL-3.0>.
1 change: 1 addition & 0 deletions sale_product_origin_report/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import models
15 changes: 15 additions & 0 deletions sale_product_origin_report/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Copyright 2026 AvanzOSC
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
{
"name": "Sale Product Origin Report",
"version": "16.0.1.1.0",
"category": "Sales",
"summary": "Show product origin and valid external link in sale reports",
"depends": ["sale"],
"data": ["views/sale_report_templates.xml"],
"installable": True,
"auto_install": False,
"author": "AvanzOSC",
"license": "AGPL-3",
"website": "https://github.com/avanzosc/sale-addons",
}
22 changes: 22 additions & 0 deletions sale_product_origin_report/i18n/es.po
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * sale_product_origin_report
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 16.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-02-25 10:10+0000\n"
"PO-Revision-Date: 2026-02-25 10:10+0000\n"
"Last-Translator: \n"
"Language-Team: Spanish\n"
"Language: es\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"

#. module: sale_product_origin_report
#: model_terms:ir.ui.view,arch_db:sale_product_origin_report.report_saleorder_document
msgid "Origin:"
msgstr "Origen:"
21 changes: 21 additions & 0 deletions sale_product_origin_report/i18n/sale_product_origin_report.pot
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * sale_product_origin_report
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 16.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-02-25 10:10+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: \n"
"Language-Team: \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: sale_product_origin_report
#: model_terms:ir.ui.view,arch_db:sale_product_origin_report.report_saleorder_document
msgid "Origin:"
msgstr ""
1 change: 1 addition & 0 deletions sale_product_origin_report/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import sale_order_line
74 changes: 74 additions & 0 deletions sale_product_origin_report/models/sale_order_line.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# Copyright 2026 AvanzOSC
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).

from odoo import models


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

def _normalize_report_url(self, raw_url):
if not raw_url:
return False
if not isinstance(raw_url, str):
raw_url = str(raw_url)
url = raw_url.strip()
if not url:
return False

if url.lower() in {"0", "0.0", "false", "none", "null"}:
return False

if url.startswith(("http://", "https://")):
return url

if "://" not in url and "." in url and " " not in url:
return f"https://{url}"

return False

def _get_product_origin_for_report(self):
self.ensure_one()
product = self.product_id
if not product:
return False
origin = product.description_sale or product.product_tmpl_id.description_sale
if not origin:
return False
origin = origin.strip()
line_title = (self.name or "").split("\n")[0].strip()
if line_title and origin.lower() == line_title.lower():
return False
return origin

def _get_product_external_url_for_report(self):
self.ensure_one()
product = self.product_id
if not product:
return False

if "external_url" in product._fields and product.external_url:
url = self._normalize_report_url(product.external_url)
if url:
return url
if (
"external_url" in product.product_tmpl_id._fields
and product.product_tmpl_id.external_url
):
url = self._normalize_report_url(product.product_tmpl_id.external_url)
if url:
return url

if "website_link" in product._fields and product.website_link:
url = self._normalize_report_url(product.website_link)
if url:
return url
if (
"website_link" in product.product_tmpl_id._fields
and product.product_tmpl_id.website_link
):
url = self._normalize_report_url(product.product_tmpl_id.website_link)
if url:
return url

return False
41 changes: 41 additions & 0 deletions sale_product_origin_report/views/sale_report_templates.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<template
id="report_saleorder_document"
inherit_id="sale.report_saleorder_document"
priority="99"
>
<xpath expr="//td[@name='td_name']" position="replace">
<td name="td_name">
<t
t-set="product_url"
t-value="line._get_product_external_url_for_report()"
/>
<t t-set="line_title" t-value="(line.name or '').split('\n')[0]" />
<t t-if="product_url">
<a
t-att-href="product_url"
target="_blank"
rel="noopener noreferrer"
>
<span t-out="line_title" />
</a>
</t>
<t t-else="">
<span t-out="line_title" />
</t>
<t
t-set="product_origin"
t-value="line._get_product_origin_for_report()"
/>
<t t-if="product_origin">
<br />
<small>
<strong>Origin:</strong>
<span t-out="product_origin" />
</small>
</t>
</td>
</xpath>
</template>
</odoo>
6 changes: 6 additions & 0 deletions setup/sale_product_origin_report/setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import setuptools

setuptools.setup(
setup_requires=['setuptools-odoo'],
odoo_addon=True,
)