Skip to content
Draft
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
231 changes: 231 additions & 0 deletions punchout/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,231 @@
========
Punchout
========

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

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

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

This module setup the base configuration to access a punchout platform,
in order to populate odoo. For example, creating purchase orders. This
alpha version has been implemented using the eShop Wurth.

**Table of contents**

.. contents::
:local:

Use Cases / Context
===================

[ This file is optional but strongly suggested to allow end-users to
evaluate the module's usefulness in their context. ]

This module set up the base of the connection to a punchout system. Sub
module can be created for example to create a purchase order from a
shopping cart created in the punchout platform. To do this, you can make
your model inherit from "punchout.request" and add the import process by
inheriting "action_process" method.

Linked modules:

- punchout_environment : allows to configure a punchout backend through
server environment files
- punchout_queue_job : link the punchout request to queue jobs,
automatically call the process method when a response is received

Installation
============

[ This file must only be present if there are very specific installation
instructions, such as installing non-python dependencies. The audience
is systems administrators. ]

Configuration
=============

To configure this module:

1. Go to **Settings > Technical > PunchOut > PunchOut Backends**

2. Create a new backend record with:

- **Name**: A unique identifier for this supplier connection
- **Description**: Human-readable description
- **Protocol**: Select the punchout protocol (cXML, OCI, or IDS -
requires additional modules)
- **URL**: The supplier's punchout setup URL
- **Browser form post URL**: The callback URL where the supplier
sends the shopping cart (can be relative like
``/punchout/cxml/receive/``)
- **Session duration**: Maximum time (in seconds) a punchout session
is valid

3. Configure protocol-specific credentials (requires Administrator
access):

- cXML: From/To identities, SharedSecret
- OCI: Custom vendor parameters
- IDS: Customer name, number, and password

4. Optionally add UoM mappings to translate supplier unit codes to Odoo
UoMs

**Note**: Backend configuration and credential fields are restricted to
users with the ``Administration/Settings`` permission group for security
reasons.

Usage
=====

To use this module:

1. Configure a punchout backend with your supplier's credentials
2. Set the backend state to "Open"
3. Click the **Access** button to start a punchout session
4. Browse the supplier's catalog and add items to your cart
5. Complete the checkout on the supplier's site - this sends the cart
back to Odoo
6. The punchout session will show status "To Process" when the cart is
received
7. Process the session to create purchase orders (requires
``punchout_purchase`` module)

**Session States:**

- **Draft**: Session created, waiting for supplier response
- **To Process**: Cart received from supplier, ready to create purchase
order
- **Done**: Session processed successfully
- **Error**: Something went wrong (check error message)

**Auditing a backend:** the backend form carries a chatter and tracks
changes to ``state``, ``protocol``, ``url``, ``browser_form_post_url``
and ``session_duration``. A "Sessions" smart button on the backend opens
the filtered list of every session that used it.

**See also:** when ``punchout_purchase`` is installed, additional entry
points appear (Browse Supplier Catalog from a draft PO and from a vendor
record, per-product "Open at supplier" deep-links). Refer to that
module's README for the purchase-side flows.

Known issues / Roadmap
======================

[ Enumerate known caveats and future potential improvements. It is
mostly intended for end-users, and can also help potential new
contributors discovering new features to implement. ]

-

Changelog
=========

18.0.1.0.0 (2026)
-----------------

- [MIG] Migration to Odoo 18.0.
- [IMP] ``punchout.uom.mapping`` now resolves supplier UoM codes through
a 6-tier chain: backend → supplier → global → UNECE → uom name →
caller default.
- [IMP] Ship ``data/uom_mapping_data.xml`` with common non-UNECE codes
as global defaults (STUECK, ST, STK, PC, PCS, EACH, KG, M, L).
- [IMP] Optional ``supplier_id`` scope on ``punchout.uom.mapping``; both
scopes (backend, supplier) are now optional.
- [FIX] ``_get_browser_form_post_url`` now produces RFC-clean URLs (no
double slashes, no trailing slash before the query string).
- [IMP] Stored ``name`` field on ``punchout.session`` so Many2one
displays show "Backend / 2026-04-26 14:02" instead of
"punchout.session,42".
- [IMP] ``punchout.backend`` inherits ``mail.thread`` and tracks changes
to state, protocol, URL, callback URL and session duration.
- [IMP] Smart button on the backend form opens the filtered list of
sessions for that backend.
- [FIX] Session form's "Received" pane is hidden when
``setup_request_response`` is empty — only cXML actually fills it, so
the pane was permanently blank for OCI/IDS sessions.
- [IMP] ``session_retention_days`` field on backend (default 90) + daily
cron ``_gc_punchout_sessions`` that vacuums old sessions. Previous
behaviour: the table grew without bound.
- [IMP] ``max_response_size`` field on backend (default 1 MiB) +
``_check_response_size`` helper used by the protocol controllers to
reject oversized supplier payloads.
- [ADD] Dutch translation.

13.0.1.0.0 (2023-09-26)
-----------------------

- [ADD] First version.

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

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

* ACSONE SA/NV

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

- Thomas Binsfeld thomas.binsfeld@acsone.eu (ACSONE SA/NV)
- Benjamin Willig benjamin.willig@acsone.eu (ACSONE SA/NV)

Other credits
-------------

[ This file is optional and contains additional credits, other than
authors, contributors, and maintainers. ]

The development of this module has been financially supported by:

- ACSONE SA/NV

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

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
2 changes: 2 additions & 0 deletions punchout/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from . import controllers
from . import models
30 changes: 30 additions & 0 deletions punchout/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Copyright 2023 ACSONE SA/NV
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).

{
"name": "Punchout",
"version": "18.0.1.0.0",
"license": "AGPL-3",
"author": "Odoo Community Association (OCA), ACSONE SA/NV",
"website": "https://github.com/OCA/edi",
"depends": [
# odoo addons
"base",
"mail",
# OCA addons
"uom_unece", # For UNECE UoM codes
],
"data": [
"security/punchout_backend.xml",
"security/punchout_session.xml",
"security/punchout_uom_mapping.xml",
"data/uom_mapping_data.xml",
"data/ir_cron.xml",
"views/punchout_backend.xml",
"views/punchout_session.xml",
"views/punchout_uom_mapping.xml",
],
"demo": [
"demo/punchout_demo.xml",
],
}
6 changes: 6 additions & 0 deletions punchout/controllers/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Copyright 2023 ACSONE SA/NV
# Copyright 2025 Bosd
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).

# Base punchout module has no controllers.
# Protocol-specific controllers are in protocol modules (e.g., punchout_cxml).
14 changes: 14 additions & 0 deletions punchout/data/ir_cron.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8" ?>
<!-- Copyright 2025 Bosd
License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). -->
<odoo noupdate="1">
<record id="ir_cron_gc_punchout_sessions" model="ir.cron">
<field name="name">Punchout: garbage-collect old sessions</field>
<field name="model_id" ref="model_punchout_session" />
<field name="state">code</field>
<field name="code">model._gc_punchout_sessions()</field>
<field name="interval_number">1</field>
<field name="interval_type">days</field>
<field name="active" eval="True" />
</record>
</odoo>
67 changes: 67 additions & 0 deletions punchout/data/uom_mapping_data.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
<?xml version="1.0" encoding="utf-8" ?>
<!-- Copyright 2025 Bosd
License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). -->
<odoo noupdate="1">
<!--
Global default UoM mappings — applied when a supplier ships a code that
doesn't match a backend- or supplier-scoped mapping and isn't a
recognised UNECE code. Users can safely override or delete these at any
time; the noupdate="1" flag preserves customizations on module upgrade.

UNECE codes (C62, KGM, MTR, LTR, EA, PCE, …) are resolved automatically
via the uom_unece module and do not need explicit entries here.
-->

<!-- Pieces / Units (German and common abbreviations) -->
<record id="uom_mapping_stueck" model="punchout.uom.mapping">
<field name="supplier_code">STUECK</field>
<field name="uom_id" ref="uom.product_uom_unit" />
<field name="notes">German: Stück (piece)</field>
</record>
<record id="uom_mapping_st" model="punchout.uom.mapping">
<field name="supplier_code">ST</field>
<field name="uom_id" ref="uom.product_uom_unit" />
<field name="notes">German abbreviation for Stück</field>
</record>
<record id="uom_mapping_stk" model="punchout.uom.mapping">
<field name="supplier_code">STK</field>
<field name="uom_id" ref="uom.product_uom_unit" />
<field name="notes">German abbreviation for Stück</field>
</record>
<record id="uom_mapping_pc" model="punchout.uom.mapping">
<field name="supplier_code">PC</field>
<field name="uom_id" ref="uom.product_uom_unit" />
<field name="notes">Common abbreviation for piece</field>
</record>
<record id="uom_mapping_pcs" model="punchout.uom.mapping">
<field name="supplier_code">PCS</field>
<field name="uom_id" ref="uom.product_uom_unit" />
<field name="notes">Common abbreviation for pieces</field>
</record>
<record id="uom_mapping_each" model="punchout.uom.mapping">
<field name="supplier_code">EACH</field>
<field name="uom_id" ref="uom.product_uom_unit" />
<field name="notes">English word form for each/piece</field>
</record>

<!-- Weight (short forms; UNECE covers KGM) -->
<record id="uom_mapping_kg" model="punchout.uom.mapping">
<field name="supplier_code">KG</field>
<field name="uom_id" ref="uom.product_uom_kgm" />
<field name="notes">Short form for kilogram</field>
</record>

<!-- Length (short forms; UNECE covers MTR) -->
<record id="uom_mapping_m" model="punchout.uom.mapping">
<field name="supplier_code">M</field>
<field name="uom_id" ref="uom.product_uom_meter" />
<field name="notes">Short form for metre</field>
</record>

<!-- Volume (short forms; UNECE covers LTR) -->
<record id="uom_mapping_l" model="punchout.uom.mapping">
<field name="supplier_code">L</field>
<field name="uom_id" ref="uom.product_uom_litre" />
<field name="notes">Short form for litre</field>
</record>
</odoo>
37 changes: 37 additions & 0 deletions punchout/demo/punchout_demo.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?xml version="1.0" encoding="utf-8" ?>
<!-- Copyright 2025 Bosd
License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). -->
<odoo noupdate="1">
<!-- Demo Punchout Backend (base, will be extended by protocol modules) -->
<record id="demo_punchout_backend" model="punchout.backend">
<field name="name">Demo Supplier Catalog</field>
<field name="description">Demo punchout backend for testing</field>
<field name="protocol">cxml</field>
<field name="url">https://demo.supplier.example.com/punchout</field>
<field name="browser_form_post_url">/punchout/cxml/receive/</field>
<field name="session_duration">7200</field>
<field name="state">open</field>
</record>

<!-- Demo UoM Mappings for common supplier-specific codes -->
<record id="demo_uom_mapping_pce" model="punchout.uom.mapping">
<field name="backend_id" ref="demo_punchout_backend" />
<field name="supplier_code">PCE</field>
<field name="uom_id" ref="uom.product_uom_unit" />
<field name="notes">Common supplier code for pieces/units</field>
</record>

<record id="demo_uom_mapping_box" model="punchout.uom.mapping">
<field name="backend_id" ref="demo_punchout_backend" />
<field name="supplier_code">BOX</field>
<field name="uom_id" ref="uom.product_uom_dozen" />
<field name="notes">Supplier uses BOX for dozen quantities</field>
</record>

<record id="demo_uom_mapping_kg" model="punchout.uom.mapping">
<field name="backend_id" ref="demo_punchout_backend" />
<field name="supplier_code">KGM</field>
<field name="uom_id" ref="uom.product_uom_kgm" />
<field name="notes">UNECE code for kilogram</field>
</record>
</odoo>
Loading
Loading