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
83 changes: 83 additions & 0 deletions product_download_feed/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
=======================
Product Feed CSV Export
=======================

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

.. |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%2Fproduct--attribute-lightgray.png?logo=github
:target: https://github.com/OCA/product-attribute/tree/18.0/product_download_feed
:alt: OCA/product-attribute
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
:target: https://translation.odoo-community.org/projects/product-attribute-18-0/product-attribute-18-0-product_download_feed
: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/product-attribute&target_branch=18.0
:alt: Try me on Runboat

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

| This module provides an **HTTP endpoint** that allows authenticated
Odoo users to export the product feed as a **CSV file**.
| It automatically applies the user's assigned **pricelist**, so the
exported prices match what the user would see in sales quotations or
the portal.

**Table of contents**

.. contents::
:local:

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

Bugs are tracked on `GitHub Issues <https://github.com/OCA/product-attribute/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/product-attribute/issues/new?body=module:%20product_download_feed%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
-------

* Odoo S.A.
* Tecnativa

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

- [Tecnativa](https://www.tecnativa.com):

- Eduardo Ezerouali

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/product-attribute <https://github.com/OCA/product-attribute/tree/18.0/product_download_feed>`_ project on GitHub.

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
3 changes: 3 additions & 0 deletions product_download_feed/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Copyright 2025 Tecnativa - Eduardo Ezerouali
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl)
from . import controllers
15 changes: 15 additions & 0 deletions product_download_feed/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Copyright 2025 Tecnativa - Eduardo Ezerouali
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl)
{
"name": "Product Feed CSV Export",
"version": "18.0.1.0.0",
"summary": "Download CSV with product feed from a pricelist using controller",
"category": "Product",
"license": "AGPL-3",
"website": "https://github.com/OCA/product-attribute",
"author": "Odoo S.A., Tecnativa, Odoo Community Association (OCA)",
"depends": ["sale_stock"],
"data": [],
"installable": True,
"application": False,
}
3 changes: 3 additions & 0 deletions product_download_feed/controllers/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Copyright 2025 Tecnativa - Eduardo Ezerouali
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl)
from . import main
71 changes: 71 additions & 0 deletions product_download_feed/controllers/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# Copyright 2025 Tecnativa - Eduardo Ezerouali
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl)
import csv
import io
from datetime import datetime

from odoo import http
from odoo.http import request


class CatalogExportController(http.Controller):
@http.route(
["/feed/export/products.csv"],
type="http",
auth="user",
methods=["GET"],
csrf=False,
)
def export_products_csv(self, **kwargs):
"""
Export product feed to CSV applying pricelist.
Params:
pricelist_id: ID of the pricelist (or user default pricelist)
fields: comma-separated list of fields
active: true/false
"""
fields_param = (
kwargs.get("fields") or "name,default_code,barcode,list_price,qty_available"
)
fields = [f.strip() for f in fields_param.split(",") if f.strip()]
active = kwargs.get("active", "true").lower() != "false"
pricelist_id = kwargs.get("pricelist_id")
pricelist = None
if pricelist_id:
pricelist = (
request.env["product.pricelist"].browse(int(pricelist_id)).exists()
)
if not pricelist:
partner = request.env.user.partner_id
pricelist = partner.property_product_pricelist
domain = [("active", "=", active), ("sale_ok", "=", True)]
products = request.env["product.product"].search(domain)
buf = io.StringIO()
writer = csv.writer(buf, quoting=csv.QUOTE_MINIMAL)
writer.writerow(fields)

def get_val(rec, field):
if field == "list_price":
return (
pricelist._get_product_price(rec, 1.0)
if pricelist
else rec.list_price
)
val = rec[field] if field in rec._fields else ""
if hasattr(val, "name") and val._name != "ir.attachment":
return val.name
if hasattr(val, "ids"):
return ",".join(str(x) for x in val.ids)
if isinstance(val, datetime):
return val.isoformat()
return val if val is not False else ""

for rec in products:
writer.writerow([get_val(rec, f) for f in fields])

csv_bytes = buf.getvalue().encode("utf-8-sig")
headers = [
("Content-Type", "text/csv; charset=utf-8"),
("Content-Disposition", 'attachment; filename="products.csv"'),
]
return request.make_response(csv_bytes, headers=headers)
3 changes: 3 additions & 0 deletions product_download_feed/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[build-system]
requires = ["whool"]
build-backend = "whool.buildapi"
2 changes: 2 additions & 0 deletions product_download_feed/readme/CONTRIBUTORS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
- \[Tecnativa\](<https://www.tecnativa.com>):
- Eduardo Ezerouali
2 changes: 2 additions & 0 deletions product_download_feed/readme/DESCRIPTION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
This module provides an **HTTP endpoint** that allows authenticated Odoo users to export the product feed as a **CSV file**.
It automatically applies the user's assigned **pricelist**, so the exported prices match what the user would see in sales quotations or the portal.
Binary file added product_download_feed/static/description/icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading