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
112 changes: 112 additions & 0 deletions sign_oca_reminder/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
===============================
Sign OCA Reminders & Expiration
===============================

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

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

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

This module adds DocuSign-like reminder, resend, and expiration
functionality to OCA's sign_oca module.

**Table of contents**

.. contents::
:local:

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

Go to Settings > Sign OCA to configure:

- **Automatic Reminders**: Enable/disable reminders by default on new
requests
- **Reminder Interval**: Days between automatic reminders (default: 3)
- **Validity Days**: Default expiration period for new requests (0 = no
expiration)

These defaults can be overridden on each individual sign request.

Usage
=====

After installing:

1. **Manual Resend**: Open a sent sign request and click "Resend" to
re-notify unsigned signers
2. **Automatic Reminders**: Enable on a request to have the daily cron
send reminders at the configured interval
3. **Expiration**: Set a validity date; requests past this date are
automatically cancelled by the daily cron
4. **Filters**: Use "Expiring Soon" and "Expired" search filters in the
sign request list

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

- Support per-signer reminder preferences
- Add reminder history log accessible from the request form

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

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

* Keboola

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

- `Keboola <https://www.keboola.com>`__:

- Jiri Manas

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

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
48 changes: 48 additions & 0 deletions sign_oca_reminder/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Copyright 2025 Keboola
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
# -*- coding: utf-8 -*-
import logging

from . import models

_logger = logging.getLogger(__name__)


def post_init_hook(env):
"""Backfill sent_date for existing sent requests on fresh install."""
_backfill_sent_date(env)


def _backfill_sent_date(env):
"""Set sent_date = create_date for requests already in '0_sent' state.

When the module is installed on a database with existing sign requests
that were sent before the module was available, sent_date is NULL.
Without sent_date, next_reminder_date cannot be computed and reminders
never fire.
"""
env.cr.execute("""
UPDATE sign_oca_request
SET sent_date = create_date
WHERE state = '0_sent'
AND sent_date IS NULL
""")
updated = env.cr.rowcount
if updated:
_logger.info(
"Backfilled sent_date for %d existing sign request(s).",
updated,
)
# Trigger recomputation of stored next_reminder_date
requests = env["sign.oca.request"].search(
[
("state", "=", "0_sent"),
("reminder_enabled", "=", True),
]
)
if requests:
requests._compute_next_reminder_date()
_logger.info(
"Recomputed next_reminder_date for %d request(s).",
len(requests),
)
23 changes: 23 additions & 0 deletions sign_oca_reminder/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Copyright 2025 Keboola
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
{
"name": "Sign OCA Reminders & Expiration",
"version": "18.0.1.0.0",
"category": "Sign",
"summary": "Automatic reminders, manual resend, and expiration for sign requests",
"author": "Keboola, Odoo Community Association (OCA)",
"website": "https://github.com/OCA/sign",
"license": "AGPL-3",
"depends": ["sign_oca"],
"development_status": "Beta",
"data": [
"data/cron_data.xml",
"data/mail_template_data.xml",
"views/sign_oca_request_views.xml",
"views/res_config_settings_views.xml",
],
"post_init_hook": "post_init_hook",
"installable": True,
"application": False,
"auto_install": False,
}
15 changes: 15 additions & 0 deletions sign_oca_reminder/data/cron_data.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8" ?>
<!-- Copyright 2025 Keboola
License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). -->
<odoo noupdate="1">
<record id="ir_cron_sign_oca_send_reminders" model="ir.cron">
<field name="name">Sign OCA: Send Reminders and Expire Requests</field>
<field name="model_id" ref="sign_oca.model_sign_oca_request" />
<field name="state">code</field>
<field name="code">model._cron_send_reminders()</field>
<field name="interval_number">1</field>
<field name="interval_type">days</field>
<field name="priority">10</field>
<field name="active" eval="True" />
</record>
</odoo>
53 changes: 53 additions & 0 deletions sign_oca_reminder/data/mail_template_data.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<?xml version="1.0" encoding="utf-8" ?>
<!-- Copyright 2025 Keboola
License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). -->
<odoo>
<template id="sign_oca_reminder_mail">
<div>
<span>
This is a reminder that
<t t-out="record.create_uid.name" />
(<a
t-att-href="'mailto:%s?subject=%s' % (record.create_uid.email, 'Re: %s' % record.name)"
style="color:#428BCA; text-decoration:none;"
target="_blank"
>
<t t-out="record.create_uid.email" />
</a>)
has requested your signature on
<strong>
<t t-out="record.name" />
</strong>.
</span>
<t t-if="record.validity_date">
<div
style="margin: 16px 0px; padding: 12px 16px; background-color: #FFF3CD; border: 1px solid #FFEEBA; border-radius: 4px;"
>
<strong>Deadline:</strong> This request expires on
<strong>
<t t-out="record.validity_date" />
</strong>.
Please sign before this date.
</div>
</t>
<span>
<div style="margin:16px auto; text-align:center;">
<a
t-att-href="link"
style="padding: 8px 16px; border-radius: 3px; background-color:#875A7B; text-align:center; text-decoration:none; color: #FFFFFF;"
>
Sign document
</a>
</div>
</span>
<span>
<div style="opacity: 0.7;">
<strong
>Warning</strong> do not forward this email to other people!<br />
They will be able to access this document and sign it as yourself.<br
/>
</div>
</span>
</div>
</template>
</odoo>
6 changes: 6 additions & 0 deletions sign_oca_reminder/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Copyright 2025 Keboola
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
# -*- coding: utf-8 -*-
from . import res_company
from . import res_config_settings
from . import sign_oca_request
25 changes: 25 additions & 0 deletions sign_oca_reminder/models/res_company.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Copyright 2025 Keboola
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
# -*- coding: utf-8 -*-
from odoo import fields, models


class ResCompany(models.Model):
_inherit = "res.company"

sign_oca_reminder_enabled = fields.Boolean(
string="Enable Automatic Reminders",
default=False,
help="Enable automatic email reminders for pending sign requests.",
)
sign_oca_reminder_interval_days = fields.Integer(
string="Reminder Interval (Days)",
default=3,
help="Number of days between automatic reminders for pending sign requests.",
)
sign_oca_validity_days = fields.Integer(
string="Default Validity (Days)",
default=0,
help="Default number of days before a sign request expires. "
"0 means no expiration.",
)
21 changes: 21 additions & 0 deletions sign_oca_reminder/models/res_config_settings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Copyright 2025 Keboola
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
# -*- coding: utf-8 -*-
from odoo import fields, models


class ResConfigSettings(models.TransientModel):
_inherit = "res.config.settings"

sign_oca_reminder_enabled = fields.Boolean(
related="company_id.sign_oca_reminder_enabled",
readonly=False,
)
sign_oca_reminder_interval_days = fields.Integer(
related="company_id.sign_oca_reminder_interval_days",
readonly=False,
)
sign_oca_validity_days = fields.Integer(
related="company_id.sign_oca_validity_days",
readonly=False,
)
Loading
Loading