Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
091f128
privacy_consent: Privacy explicit consent tracking tools (#11)
yajo Aug 8, 2018
1f9f0b4
Added translation using Weblate (Portuguese)
pedrocs-exo Sep 20, 2018
04865db
Added translation using Weblate (French)
gurneyalex Dec 11, 2018
cdc9755
Translated using Weblate (Portuguese)
alvarorib Dec 14, 2018
f64b0ad
privacy_consent: Test at install
yajo Jan 21, 2019
55eb048
privacy_consent: set partner lang on mail template
Mar 1, 2019
21fb42a
privacy_consent: Avoid race condition with mail queue cron
yajo Mar 6, 2019
d09589c
Added translation using Weblate (German)
malinkienke Mar 20, 2019
9ea3555
privacy_consent: Avoid race condition when sending emails
yajo May 7, 2019
abccf87
Translated using Weblate (German)
dw3gn3r Apr 11, 2019
abc4032
privacy_consent: Separate automated emails send process
yajo May 13, 2019
0f7d661
Migrate privacy_consent to v11
fkantelberg Jul 4, 2019
7915e8c
Change icon to handshake
fkantelberg Jul 5, 2019
e2c6075
[MIG] privacy_consent: Migrate to v12
yajo Jul 8, 2019
4ae9212
[FIX] privacy_consent: do not mark as sent if mail failed
Jun 23, 2020
ef2f2f7
[FIX] privacy_consent: make it work, basically
Oct 7, 2020
5ffee79
[IMP] pre-commit run
Jan 12, 2021
9e25968
[MIG] privacy_consent: Migration to 13.0
Jan 12, 2021
727da7d
[FIX] privacy_consent: ensure there's always a request user
Feb 26, 2021
289879d
[13.0][FIX] privacy consent function not recordset-aware
sergiocorato Mar 2, 2021
84c7ab7
[FIX] privacy_consent: dump auth=none for consents
Apr 1, 2021
e51ff30
[IMP] privacy_consent: black, isort, prettier
victoralmau Nov 10, 2021
1c17827
[MIG] privacy_consent: Migration to 14.0
valtterilattu Jul 27, 2021
99b2476
[FIX] privacy_consent: Avoid permissions error if the user does not h…
victoralmau Apr 11, 2022
da9eb43
Fix description rendering and state rendering
Rad0van Sep 5, 2022
467d107
Added translation using Weblate (Italian)
primes2h Oct 24, 2022
dd0fedb
[MIG] privacy_consent: migration to 16.0
tde-banana-odoo Oct 25, 2022
e434a1a
[FIX] privacy_consent: Markup controller (partner) name before render…
tde-banana-odoo Oct 25, 2022
993e2b3
[REF] privacy_content: add some security-related tests
tde-banana-odoo Oct 25, 2022
68e08ba
[IMP] privacy_consent: use consteq for tokens comparison
tde-banana-odoo Oct 24, 2022
55093df
Translated using Weblate (Spanish)
Ivorra78 Aug 2, 2023
0fa34c6
Translated using Weblate (Italian)
mymage Dec 27, 2023
49cf7a1
[MIG] privacy_consent: Migration to 17.0
JordiToledo Feb 10, 2025
3408bb5
[IMP] privacy_consent: pre-commit auto fixes
michelerusti Jun 10, 2025
9c4115b
[MIG] privacy_consent: Migration to 18.0
michelerusti Jun 10, 2025
44f8c64
[UPD] Update privacy_consent.pot
Jul 2, 2025
8ee8a0c
[BOT] post-merge updates
OCA-git-bot Jul 2, 2025
7130751
Added translation using Weblate (Slovenian)
sysadminmatmoz Jun 26, 2026
ee20c5c
Translated using Weblate (Slovenian)
sysadminmatmoz Jun 26, 2026
15b9f9c
Translated using Weblate (Slovenian)
sysadminmatmoz Jun 26, 2026
f3a319a
[MIG] privacy_consent: Migration to 19.0
FrancescoBellingeri Aug 3, 2026
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
187 changes: 187 additions & 0 deletions privacy_consent/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
.. image:: https://odoo-community.org/readme-banner-image
:target: https://odoo-community.org/get-involved?utm_source=readme
:alt: Odoo Community Association

=================
Privacy - Consent
=================

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

.. |badge1| image:: https://img.shields.io/badge/maturity-Production%2FStable-green.png
:target: https://odoo-community.org/page/development-status
:alt: Production/Stable
.. |badge2| image:: https://img.shields.io/badge/license-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%2Fdata--protection-lightgray.png?logo=github
:target: https://github.com/OCA/data-protection/tree/19.0/privacy_consent
:alt: OCA/data-protection
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
:target: https://translation.odoo-community.org/projects/data-protection-19-0/data-protection-19-0-privacy_consent
: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/data-protection&target_branch=19.0
:alt: Try me on Runboat

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

This module allows the user to define a set of subjects (partners)
affected by any data processing activity, and establish a process to ask
them for consent to include them in that activity.

For those that need explicit consent as a lawfulness base for personal
data processing, as required by GDPR (article 6.1.a), this module
provides the needed tools to automate it.

**Table of contents**

.. contents::
:local:

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

You may want to install, along with this module, one of OCA's
``mail_tracking`` module collection, such as ``mail_tracking_mailgun``,
so you can provide more undeniable proof that some consent request was
sent, and to whom.

However, the most important proof to provide is the answer itself (more
than the question), and this addon provides enough tooling for that.

Multi-database instances
------------------------

To enable multi-database support, you must load this addon as a
server-wide addon. Example command to boot Odoo:

::

odoo-bin --load=web,privacy_consent

Usage
=====

New options for data processing activities:

1. Go to *Privacy > Master Data > Activities* and create one.
2. Give it a name, such as *Sending mass mailings to customers*.
3. Go to tab *Consent* and choose one option in *Ask subjects for
consent*:

- *Manual* tells the activity that you will want to create and send
the consent requests manually, and only provides some helpers for
you to be able to batch-generate them.
- *Automatic* enables this module's full power: send all consent
requests to selected partners automatically, every day and under
your demand.

4. When you do this, all the consent-related options appear. Configure
them:

- A smart button tells you how many consents have been generated,
and lets you access them.

- Choose one *Email template* to send to subjects. This email itself
is what asks for consent, and it gets recorded, to serve as a
proof that it was sent. The module provides a default template
that should be good for most usage cases; and if you create one
directly from that field, some good defaults are provided for your
comfortability.

- *Subjects filter* defines what partners will be elegible for
inclusion in this data processing activity.

- You can enable *Accepted by default* if you want to assume
subjects accepted their data processing. You should possibly
consult your lawyer to use this.

- You can choose a *Server action* (developer mode only) that will
be executed whenever a new non-draft consent request is created,
or when its acceptance status changes.

This module supplies a server action by default, called *Update
partner's opt out*, that syncs the acceptance status with the
partner's *Elegible for mass mailings* option.

5. Click on *Generate consent requests* link to create new consent
requests.

- If you chose *Manual* mode, all missing consent request are
created as drafts, and nothing else is done now.
- If you chose *Automatic* mode, also those request e-mails are
enqueued and, when the mail queue is cleared, they will be set as
*Sent*.

6. You will be presented with the list of just-created consent requests.
See below.

New options for consent requests:

1. Access the consent requests by either:

- Generating new consent requests from a data processing activity.
- Pressing the *Consents* smart button in a data processing
activity.
- Going to *Privacy > Privacy > Consents*.

2. A consent will include the partner, the activity, the acceptance
status, and the request state.
3. You can manually ask for consent by pressing the button labeled as
*Ask for consent*.
4. All consent requests and responses are recorded in the mail thread
below.

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

Bugs are tracked on `GitHub Issues <https://github.com/OCA/data-protection/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/data-protection/issues/new?body=module:%20privacy_consent%0Aversion:%2019.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
-------

* Tecnativa
* initOS GmbH

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

- `Tecnativa <https://www.tecnativa.com>`__:

- Jairo Llopis

- `initOS GmbH <https://www.initos.com>`__:

- Florian Kantelberg

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/data-protection <https://github.com/OCA/data-protection/tree/19.0/privacy_consent>`_ 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 privacy_consent/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from . import controllers
from . import models
31 changes: 31 additions & 0 deletions privacy_consent/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Copyright 2018 Tecnativa - Jairo Llopis
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
{
"name": "Privacy - Consent",
"summary": "Allow people to explicitly accept or reject inclusion "
"in some activity, GDPR compliant",
"version": "19.0.1.0.0",
"development_status": "Production/Stable",
"category": "Privacy",
"website": "https://github.com/OCA/data-protection",
"author": "Tecnativa, initOS GmbH, Odoo Community Association (OCA)",
"license": "AGPL-3",
"application": False,
"installable": True,
"depends": ["privacy"],
"data": [
"security/ir.model.access.csv",
"data/ir_actions_server.xml",
"data/ir_cron.xml",
"data/mail.xml",
"templates/form.xml",
"views/privacy_consent.xml",
"views/privacy_activity.xml",
"views/res_partner.xml",
],
"assets": {
"web.assets_frontend": [
"/privacy_consent/static/src/css/privacy_consent.scss",
],
},
}
1 change: 1 addition & 0 deletions privacy_consent/controllers/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import main
52 changes: 52 additions & 0 deletions privacy_consent/controllers/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# Copyright 2018 Tecnativa - Jairo Llopis
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).

from datetime import datetime

from markupsafe import Markup
from werkzeug.exceptions import NotFound

from odoo.http import Controller, request, route
from odoo.tools import consteq


class ConsentController(Controller):
@route(
"/privacy/consent/<any(accept,reject):choice>/<int:consent_id>/<token>",
type="http",
auth="public",
website=True,
)
def consent(self, choice, consent_id, token, *args, **kwargs):
"""Process user's consent acceptance or rejection."""
consent = (
request.env["privacy.consent"]
.with_context(subject_answering=True)
.sudo()
.browse(consent_id)
)
if not (consent.exists() and consteq(consent._token(), token)):
raise NotFound
if consent.partner_id.lang:
request.update_context(lang=consent.partner_id.lang)
consent.action_answer(choice == "accept", self._metadata())
return request.render(
"privacy_consent.form",
{
"consent": consent,
"controller_name_html": Markup(
consent.activity_id.controller_id.with_context(
show_address=True, html_format=True
).display_name
),
},
)

def _metadata(self):
return (
"User agent: {}\nRemote IP: {}\nDate and time: {:%Y-%m-%d %H:%M:%S}"
).format(
request.httprequest.environ.get("HTTP_USER_AGENT"),
request.httprequest.environ.get("REMOTE_ADDR"),
datetime.now(),
)
26 changes: 26 additions & 0 deletions privacy_consent/data/ir_actions_server.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8" ?>
<!-- Copyright 2018 Tecnativa - Jairo Llopis
Copyright 2019 initOS GmbH - Florian Kantelberg
License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). -->
<data>
<record id="sync_blacklist" model="ir.actions.server">
<field name="name">Sync partner's email blacklist status</field>
<field name="model_id" ref="model_privacy_consent" />
<field name="crud_model_id" ref="base.model_res_partner" />
<field name="state">code</field>
<field name="code">
for consent in records:
email = consent.partner_id.email
# Skip records without email, although highly improbable
if not email:
continue
# Choose method to sync acceptance and blacklisting
if consent.accepted:
method = env["mail.blacklist"]._remove
else:
method = env["mail.blacklist"]._add
# Apply user desire
method(email)
</field>
</record>
</data>
14 changes: 14 additions & 0 deletions privacy_consent/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 2018 Tecnativa - Jairo Llopis
Copyright 2019 initOS GmbH - Florian Kantelberg
License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). -->
<data>
<record id="cron_auto_consent" model="ir.cron">
<field name="name">Request automatic data processing consents</field>
<field name="model_id" ref="model_privacy_activity" />
<field name="state">code</field>
<field name="code">model._cron_new_consents()</field>
<field name="interval_number">1</field>
<field name="interval_type">days</field>
</record>
</data>
Loading
Loading