Skip to content

Commit

Permalink
[ADD] stock_negative_alert_by_email: manage negative in stock via ema…
Browse files Browse the repository at this point in the history
…il alert
  • Loading branch information
Borruso committed Feb 19, 2025
1 parent 7def877 commit 3b14c73
Show file tree
Hide file tree
Showing 15 changed files with 810 additions and 0 deletions.
6 changes: 6 additions & 0 deletions setup/alert_email_negative_stock/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,
)
80 changes: 80 additions & 0 deletions stock_negative_alert_by_email/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
=============================
Stock Negative Alert by Email
=============================

..
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:e332df0f9658b9291684a651820c53442974e320557806f51d7507b31b6d4be4
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
.. |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%2Fstock--logistics--reporting-lightgray.png?logo=github
:target: https://github.com/OCA/stock-logistics-reporting/tree/14.0/stock_negative_alert_by_email
:alt: OCA/stock-logistics-reporting
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
:target: https://translation.odoo-community.org/projects/stock-logistics-reporting-14-0/stock-logistics-reporting-14-0-stock_negative_alert_by_email
: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/stock-logistics-reporting&target_branch=14.0
:alt: Try me on Runboat

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

This module adds possibility to manage negative in stock via email alert.

On `Inventory` configuration, you can set a email alerts, alert intervals, and stock quantity type to check.

This helps ensure proper stock management by notifying relevant parties when stock levels fall below zero.

**Table of contents**

.. contents::
:local:

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

Bugs are tracked on `GitHub Issues <https://github.com/OCA/stock-logistics-reporting/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/stock-logistics-reporting/issues/new?body=module:%20stock_negative_alert_by_email%0Aversion:%2014.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
~~~~~~~

* Giuseppe Borruso - Dinamiche Aziendali srl

Contributors
~~~~~~~~~~~~

* Giuseppe Borruso - Dinamiche Aziendali srl <[email protected]>

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/stock-logistics-reporting <https://github.com/OCA/stock-logistics-reporting/tree/14.0/stock_negative_alert_by_email>`_ project on GitHub.

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
1 change: 1 addition & 0 deletions stock_negative_alert_by_email/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import models
24 changes: 24 additions & 0 deletions stock_negative_alert_by_email/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Copyright (C) 2025-Today:
# Dinamiche Aziendali Srl (<http://www.dinamicheaziendali.it/>)
# @author: Giuseppe Borruso <[email protected]>
# License GPL-3.0 or later (http://www.gnu.org/licenses/gpl.html).

{
"name": "Stock Negative Alert by Email",
"version": "14.0.1.0.0",
"summary": "Manage negative in stock via email alert",
"author": "Giuseppe Borruso - Dinamiche Aziendali srl, Odoo Community Association (OCA)",
"website": "https://github.com/OCA/stock-logistics-reporting",
"category": "Warehouse Management",
"license": "AGPL-3",
"depends": [
"base",
"stock",
],
"data": [
"data/ir_cron_data.xml",
"views/res_config_setting_view.xml",
],
"installable": True,
"images": ["static/description/icon.png"],
}
16 changes: 16 additions & 0 deletions stock_negative_alert_by_email/data/ir_cron_data.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<data noupdate="1">

<record id="ir_cron_action_negative_stock" model="ir.cron">
<field name="name">Action Negative Stock</field>
<field name="user_id" ref="base.user_admin" />
<field name="interval_type">hours</field>
<field name="interval_number">24</field>
<field name="numbercall">-1</field>
<field name="model_id" ref="stock.model_stock_move" />
<field name="code">model.action_negative_stock()</field>
</record>

</data>
</odoo>
3 changes: 3 additions & 0 deletions stock_negative_alert_by_email/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from . import res_company
from . import res_config_settings
from . import stock_move
29 changes: 29 additions & 0 deletions stock_negative_alert_by_email/models/res_company.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Copyright (C) 2025-Today:
# Dinamiche Aziendali Srl (<http://www.dinamicheaziendali.it/>)
# @author: Giuseppe Borruso <[email protected]>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

from odoo import fields, models


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

negative_stock_mail_from = fields.Char()
negative_stock_mail_to = fields.Char()
negative_stock_interval_number = fields.Integer()
negative_stock_interval_type = fields.Selection(
[
("minutes", "Minutes"),
("hours", "Hours"),
("days", "Days"),
("weeks", "Weeks"),
],
)
negative_stock_quantity_type = fields.Selection(
[
("qty_available", "Quantity On Hand"),
("virtual_available", "Forecast Quantity"),
],
default="qty_available",
)
31 changes: 31 additions & 0 deletions stock_negative_alert_by_email/models/res_config_settings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Copyright (C) 2025-Today:
# Dinamiche Aziendali Srl (<http://www.dinamicheaziendali.it/>)
# @author: Giuseppe Borruso <[email protected]>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

from odoo import fields, models


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

negative_stock_mail_from = fields.Char(
related="company_id.negative_stock_mail_from",
readonly=False,
)
negative_stock_mail_to = fields.Char(
related="company_id.negative_stock_mail_to",
readonly=False,
)
negative_stock_interval_number = fields.Integer(
related="company_id.negative_stock_interval_number",
readonly=False,
)
negative_stock_interval_type = fields.Selection(
related="company_id.negative_stock_interval_type",
readonly=False,
)
negative_stock_quantity_type = fields.Selection(
related="company_id.negative_stock_quantity_type",
readonly=False,
)
127 changes: 127 additions & 0 deletions stock_negative_alert_by_email/models/stock_move.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
# Copyright (C) 2025-Today:
# Dinamiche Aziendali Srl (<http://www.dinamicheaziendali.it/>)
# @author: Giuseppe Borruso <[email protected]>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

from datetime import timedelta

from odoo import _, api, fields, models


class StockMoveInherit(models.Model):
_inherit = "stock.move"

@api.model
def _compute_domain_negative_stock(self, company):
interval_number = company.negative_stock_interval_number
interval_type = company.negative_stock_interval_type
if not (interval_number or interval_type):
return False

date_end = fields.Datetime.now()
if interval_type == "minutes":
date_start = fields.Datetime.now() - timedelta(minutes=interval_number)
elif interval_type == "hours":
date_start = fields.Datetime.now() - timedelta(hours=interval_number)
elif interval_type == "days":
date_start = fields.Datetime.now() - timedelta(days=interval_number)
else:
date_start = fields.Datetime.now() - timedelta(weeks=interval_number)
return [
("write_date", ">", date_start),
("write_date", "<", date_end),
("company_id", "=", company.id),
]

@api.model
def _compute_table_products_negative_stock(self, products, company):
quantity_type = company.negative_stock_quantity_type
table_products = _(
"""
<table class="table table-sm">
<thead>
<tr>
<th name="th_product">Product</th>
<th name="th_%(quantity_type)s">%(quantity_type_name)s</th>
</tr>
</thead>
<tbody>
"""
% {
"quantity_type": quantity_type,
"quantity_type_name": (
"Quantity On Hand"
if quantity_type == "qty_available"
else "Forecast Quantity"
),
}
)
for product in products:
if quantity_type == "qty_available":
product_quantity = product.with_company(company.id).qty_available
else:
product_quantity = product.with_company(company.id).virtual_available
if product_quantity >= 0:
continue
table_products += f"""
<tr>
<td name="product">{product.display_name}</td>
<td name="{quantity_type}">{product_quantity}</td>
</tr>
"""
table_products += """
</tbody>
</table>
"""
return table_products

@api.model
def _compute_body_htm_negative_stock(self, products, company):
return _(
"""
<p>Dear</p>
<br />
<p>
We would like to inform you that negative quantities have been
detected in the warehouse for the following items:
</p>
<br />
%(table_products)s
<p>
We kindly ask you to take immediate action to rectify this situation
in order to ensure proper stock management and avoid any
inconveniences.
</p>
<br /><br />
<p>Kind regards,</p>
<p>%(company)s</p>
"""
% {
"table_products": self._compute_table_products_negative_stock(
products, company
),
"company": company.display_name,
}
)

def action_negative_stock(self):
for company in self.env["res.company"].search([]):
record = self.sudo().with_company(company.id)
domain = record._compute_domain_negative_stock(company)
if isinstance(domain, bool):
continue
products = record.search(domain).mapped("product_id")
mail_from = company.negative_stock_mail_from or self.env.user.email
mail_to = company.negative_stock_mail_to
if products and mail_from and mail_to:
mail = self.env["mail.mail"].create(
{
"subject": "Negative Stock Alert",
"email_from": mail_from,
"email_to": mail_to,
"body_html": record._compute_body_htm_negative_stock(
products, company
),
}
)
mail.send()
1 change: 1 addition & 0 deletions stock_negative_alert_by_email/readme/CONTRIBUTORS.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* Giuseppe Borruso - Dinamiche Aziendali srl <[email protected]>
5 changes: 5 additions & 0 deletions stock_negative_alert_by_email/readme/DESCRIPTION.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
This module adds possibility to manage negative in stock via email alert.

On `Inventory` configuration, you can set a email alerts, alert intervals, and stock quantity type to check.

This helps ensure proper stock management by notifying relevant parties when stock levels fall below zero.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 3b14c73

Please sign in to comment.