bemade_purchase_warn_supplier_overdue
This commit is contained in:
parent
48aae5aa69
commit
9503f1febd
9 changed files with 342 additions and 0 deletions
1
bemade_purchase_warn_supplier_overdue/__init__.py
Normal file
1
bemade_purchase_warn_supplier_overdue/__init__.py
Normal file
|
|
@ -0,0 +1 @@
|
|||
from . import models
|
||||
29
bemade_purchase_warn_supplier_overdue/__manifest__.py
Normal file
29
bemade_purchase_warn_supplier_overdue/__manifest__.py
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
{
|
||||
'name': 'Bemade Warn Supplier Overdue',
|
||||
'version': '1.0',
|
||||
'summary': 'Warn supplier when overdue on purchase order confirmation',
|
||||
'description': """
|
||||
Warn supplier when overdue on purchase order confirmation
|
||||
===================================
|
||||
This module adds a mail.message to the purchase order confirmation form when the supplier is overdue.
|
||||
""",
|
||||
'author': 'Benoît Vézina',
|
||||
'website': 'https://www.bemade.com',
|
||||
'category': 'Purchases',
|
||||
'license': 'OPL-1',
|
||||
'depends': [
|
||||
'purchase',
|
||||
'account',
|
||||
'mail',
|
||||
],
|
||||
'data': [
|
||||
'views/res_config_settings_views.xml',
|
||||
# 'security/ir.model.access.csv',
|
||||
],
|
||||
'demo': [
|
||||
# List any demo data files here
|
||||
],
|
||||
'installable': True,
|
||||
'application': False,
|
||||
'auto_install': False,
|
||||
}
|
||||
3
bemade_purchase_warn_supplier_overdue/models/__init__.py
Normal file
3
bemade_purchase_warn_supplier_overdue/models/__init__.py
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
from . import res_company
|
||||
from . import res_config_settings
|
||||
from . import purchase_order
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
from odoo import models, fields, api, _
|
||||
|
||||
class PurchaseOrder(models.Model):
|
||||
_inherit = 'purchase.order'
|
||||
|
||||
@api.model
|
||||
def _check_supplier_overdue_invoices(self, partner):
|
||||
""" Vérifie si le fournisseur a des factures impayées en retard """
|
||||
overdue_invoices = self.env['account.move'].search([
|
||||
('partner_id', '=', partner.id),
|
||||
('move_type', '=', 'in_invoice'), # Facture fournisseur
|
||||
('invoice_date_due', '<', fields.Date.today()), # Date d'échéance dépassée
|
||||
('payment_state', '!=', 'paid') # Non payée
|
||||
])
|
||||
return len(overdue_invoices) > 0
|
||||
|
||||
def button_confirm(self):
|
||||
""" Surcharger la confirmation de commande pour intégrer l'avertissement """
|
||||
# Appel de la méthode standard de confirmation de commande
|
||||
res = super(PurchaseOrder, self).button_confirm()
|
||||
|
||||
for order in self:
|
||||
supplier = order.partner_id
|
||||
company = order.company_id
|
||||
|
||||
# Vérifier si la fonctionnalité d'avertissement est activée
|
||||
if company.warn_supplier_overdue:
|
||||
# Vérifier si l'avertissement s'applique à tous les fournisseurs ou seulement à certains
|
||||
if company.warn_supplier_scope == 'all' or (company.warn_supplier_scope == 'specific' and supplier in company.warn_supplier_specific_ids):
|
||||
if self._check_supplier_overdue_invoices(supplier):
|
||||
# Déterminer quel utilisateur doit être averti
|
||||
if company.warn_supplier_overdue_user_type == 'current':
|
||||
user_to_notify = self.env.user
|
||||
elif company.warn_supplier_overdue_user_type == 'specific':
|
||||
user_to_notify = company.warn_supplier_overdue_user_id
|
||||
else:
|
||||
user_to_notify = self.env.user # Par défaut, utilisateur courant
|
||||
|
||||
if user_to_notify:
|
||||
# Création de l'activité de type "To-Do" (mail.activity)
|
||||
activity_vals = {
|
||||
'res_model_id': self.env['ir.model'].search([('model', '=', 'purchase.order')], limit=1).id,
|
||||
'res_id': order.id, # L'ID du bon de commande
|
||||
'activity_type_id': self.env.ref('mail.mail_activity_data_todo').id, # Type d'activité "To-Do"
|
||||
'summary': _('Overdue Invoices for Supplier %s') % supplier.name,
|
||||
'note': _('The supplier %s has overdue invoices. Please follow up before proceeding with the order %s.') % (supplier.name, order.name),
|
||||
'user_id': user_to_notify.id, # Utilisateur assigné à l'activité
|
||||
'date_deadline': fields.Date.today() # La date limite de l'activité
|
||||
}
|
||||
|
||||
self.env['mail.activity'].create(activity_vals)
|
||||
return res
|
||||
45
bemade_purchase_warn_supplier_overdue/models/res_company.py
Normal file
45
bemade_purchase_warn_supplier_overdue/models/res_company.py
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
from odoo import models, fields, api
|
||||
|
||||
|
||||
class Company(models.Model):
|
||||
_inherit = 'res.company'
|
||||
|
||||
warn_supplier_overdue = fields.Boolean(
|
||||
string='Warn supplier when overdue',
|
||||
default=True,
|
||||
help='Warn user on purchase with overdue vendor',
|
||||
)
|
||||
|
||||
warn_supplier_overdue_user_type = fields.Selection(
|
||||
string='User Warned Type',
|
||||
selection=[
|
||||
('current', 'Current User'),
|
||||
('specific', 'Specific User'),
|
||||
],
|
||||
default='current',
|
||||
help='Type of user to warn when supplier is overdue',
|
||||
)
|
||||
|
||||
warn_supplier_overdue_user_id = fields.Many2one(
|
||||
string='User',
|
||||
comodel_name='res.users',
|
||||
help='Specific User to warn when supplier is overdue',
|
||||
)
|
||||
|
||||
warn_supplier_scope = fields.Selection(
|
||||
string='Warn Scope',
|
||||
selection=[
|
||||
('all', 'All Vendors'),
|
||||
('specific', 'Specific Vendors'),
|
||||
],
|
||||
default='all',
|
||||
help='Choose whether to apply overdue warnings to all vendors or only to specific vendors',
|
||||
)
|
||||
|
||||
warn_supplier_specific_ids = fields.Many2many(
|
||||
comodel_name='res.partner',
|
||||
domain=[('supplier_rank', '>', 0)],
|
||||
string='Specific Vendors',
|
||||
help='Select specific vendors to apply overdue invoice warnings',
|
||||
)
|
||||
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
from odoo import models, fields
|
||||
|
||||
class ResConfigSettings(models.TransientModel):
|
||||
_inherit = 'res.config.settings'
|
||||
|
||||
warn_supplier_overdue = fields.Boolean(
|
||||
string='Warn supplier when overdue',
|
||||
related='company_id.warn_supplier_overdue',
|
||||
readonly=False,
|
||||
)
|
||||
|
||||
warn_supplier_overdue_user_type = fields.Selection(
|
||||
string='User Warned Type',
|
||||
related='company_id.warn_supplier_overdue_user_type',
|
||||
readonly=False,
|
||||
)
|
||||
|
||||
warn_supplier_overdue_user_id = fields.Many2one(
|
||||
string='User to warn',
|
||||
comodel_name='res.users',
|
||||
related='company_id.warn_supplier_overdue_user_id',
|
||||
readonly=False,
|
||||
)
|
||||
|
||||
warn_supplier_scope = fields.Selection(
|
||||
string='Warn Scope',
|
||||
related='company_id.warn_supplier_scope',
|
||||
readonly=False,
|
||||
)
|
||||
|
||||
warn_supplier_specific_ids = fields.Many2many(
|
||||
string='Specific Vendors',
|
||||
comodel_name='res.partner',
|
||||
related='company_id.warn_supplier_specific_ids',
|
||||
readonly=False,
|
||||
)
|
||||
1
bemade_purchase_warn_supplier_overdue/tests/__init__.py
Normal file
1
bemade_purchase_warn_supplier_overdue/tests/__init__.py
Normal file
|
|
@ -0,0 +1 @@
|
|||
from . import test_purchase_order_overdue
|
||||
|
|
@ -0,0 +1,105 @@
|
|||
from odoo.tests import common
|
||||
from odoo import fields
|
||||
from odoo.exceptions import ValidationError
|
||||
|
||||
|
||||
class TestPurchaseOrderOverdue(common.TransactionCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestPurchaseOrderOverdue, self).setUp()
|
||||
|
||||
# Setup d'une société avec des paramètres personnalisés
|
||||
self.company = self.env['res.company'].create({
|
||||
'name': 'Test Company',
|
||||
'warn_supplier_overdue': True,
|
||||
'warn_supplier_overdue_user_type': 'specific',
|
||||
'warn_supplier_overdue_user_id': self.env.user.id, # L'utilisateur courant
|
||||
'warn_supplier_scope': 'specific',
|
||||
})
|
||||
|
||||
# Créer un partenaire fournisseur avec des factures impayées
|
||||
self.supplier = self.env['res.partner'].create({
|
||||
'name': 'Test Supplier',
|
||||
'supplier_rank': 1,
|
||||
})
|
||||
|
||||
# Créer une facture fournisseur impayée pour ce fournisseur
|
||||
self.invoice = self.env['account.move'].create({
|
||||
'partner_id': self.supplier.id,
|
||||
'move_type': 'in_invoice',
|
||||
'invoice_date_due': fields.Date.today(),
|
||||
'company_id': self.company.id,
|
||||
})
|
||||
|
||||
# Créer un bon de commande pour ce fournisseur
|
||||
self.purchase_order = self.env['purchase.order'].create({
|
||||
'partner_id': self.supplier.id,
|
||||
'company_id': self.company.id,
|
||||
})
|
||||
|
||||
def test_supplier_overdue_invoice_activity_created(self):
|
||||
""" Teste la création d'une activité 'To-Do' lorsque le fournisseur a des factures en retard """
|
||||
# Confirmer la commande d'achat et vérifier la création de l'activité
|
||||
self.purchase_order.button_confirm()
|
||||
|
||||
activities = self.env['mail.activity'].search([
|
||||
('res_model', '=', 'purchase.order'),
|
||||
('res_id', '=', self.purchase_order.id),
|
||||
('user_id', '=', self.env.user.id)
|
||||
])
|
||||
|
||||
# Vérifier qu'une activité a été créée
|
||||
self.assertEqual(len(activities), 1, 'No activity was created for the overdue supplier.')
|
||||
|
||||
# Vérifier le contenu de l'activité
|
||||
activity = activities[0]
|
||||
self.assertEqual(activity.summary, 'Overdue Invoices for Supplier %s' % self.supplier.name)
|
||||
self.assertIn(self.supplier.name, activity.note)
|
||||
self.assertIn(self.purchase_order.name, activity.note)
|
||||
|
||||
def test_no_activity_for_non_overdue_suppliers(self):
|
||||
""" Teste qu'aucune activité n'est créée si le fournisseur n'a pas de factures impayées """
|
||||
# Marquer la facture comme payée pour annuler l'état de retard
|
||||
self.invoice.action_post()
|
||||
self.invoice.button_mark_as_paid()
|
||||
|
||||
# Confirmer la commande d'achat
|
||||
self.purchase_order.button_confirm()
|
||||
|
||||
# Vérifier qu'aucune activité n'a été créée
|
||||
activities = self.env['mail.activity'].search([
|
||||
('res_model', '=', 'purchase.order'),
|
||||
('res_id', '=', self.purchase_order.id),
|
||||
])
|
||||
self.assertEqual(len(activities), 0, 'An activity was created despite no overdue invoices.')
|
||||
|
||||
def test_activity_for_specific_vendors_only(self):
|
||||
""" Teste que l'activité est créée seulement pour les fournisseurs spécifiques """
|
||||
# Ajouter le fournisseur à la liste des fournisseurs spécifiques
|
||||
self.company.write({'warn_supplier_specific_ids': [(4, self.supplier.id)]})
|
||||
|
||||
# Confirmer la commande d'achat
|
||||
self.purchase_order.button_confirm()
|
||||
|
||||
# Vérifier que l'activité a été créée
|
||||
activities = self.env['mail.activity'].search([
|
||||
('res_model', '=', 'purchase.order'),
|
||||
('res_id', '=', self.purchase_order.id),
|
||||
('user_id', '=', self.env.user.id)
|
||||
])
|
||||
self.assertEqual(len(activities), 1, 'No activity was created for the overdue supplier.')
|
||||
|
||||
def test_no_activity_for_non_specific_vendors(self):
|
||||
""" Teste qu'aucune activité n'est créée si le fournisseur n'est pas dans la liste des fournisseurs spécifiques """
|
||||
# Ne pas inclure le fournisseur dans la liste des fournisseurs spécifiques
|
||||
self.company.write({'warn_supplier_specific_ids': [(3, self.supplier.id)]}) # Retirer le fournisseur
|
||||
|
||||
# Confirmer la commande d'achat
|
||||
self.purchase_order.button_confirm()
|
||||
|
||||
# Vérifier qu'aucune activité n'a été créée
|
||||
activities = self.env['mail.activity'].search([
|
||||
('res_model', '=', 'purchase.order'),
|
||||
('res_id', '=', self.purchase_order.id),
|
||||
])
|
||||
self.assertEqual(len(activities), 0, 'An activity was created for a non-specific supplier.')
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<odoo>
|
||||
<record id="res_config_settings_view_form_purchase_supplier_overdue" model="ir.ui.view">
|
||||
<field name="name">res.config.settings.view.form.inherit.purchase.overdue</field>
|
||||
<field name="model">res.config.settings</field>
|
||||
<field name="priority" eval="30"/>
|
||||
<field name="inherit_id" ref="purchase.res_config_settings_view_form_purchase"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//app[@name='purchase']" position="inside">
|
||||
<block title="Supplier Overdue" name="vendor_setting_overdue">
|
||||
<!-- Avertissement lors de la confirmation d'un bon de commande pour les fournisseurs en retard -->
|
||||
<setting
|
||||
id="warn_supplier_overdue_settings"
|
||||
help="Warn a user on purchase order confirmation for overdue suppliers"
|
||||
>
|
||||
<field name="warn_supplier_overdue"/>
|
||||
</setting>
|
||||
|
||||
<!-- Type d'utilisateur à avertir (utilisateur courant ou spécifique) -->
|
||||
<setting
|
||||
id="warn_supplier_overdue_user_type_settings"
|
||||
help="Assign the To Do to a specific user or the current user"
|
||||
invisible="not warn_supplier_overdue"
|
||||
>
|
||||
<field name="warn_supplier_overdue_user_type"/>
|
||||
</setting>
|
||||
|
||||
<!-- Utilisateur spécifique à avertir (apparaît si 'specific' est sélectionné) -->
|
||||
<setting
|
||||
id="warn_supplier_overdue_user_settings"
|
||||
help="Specific user to warn when PO is confirmed with a supplier who has overdue bills"
|
||||
invisible="warn_supplier_overdue_user_type != 'specific' or not warn_supplier_overdue"
|
||||
>
|
||||
<field name="warn_supplier_overdue_user_id"/>
|
||||
</setting>
|
||||
|
||||
<!-- Nouveau champ : Portée de l'avertissement (tous les fournisseurs ou fournisseurs spécifiques) -->
|
||||
<setting
|
||||
id="warn_supplier_scope_settings"
|
||||
help="Choose whether to apply the overdue warning to all vendors or specific vendors"
|
||||
invisible="not warn_supplier_overdue"
|
||||
>
|
||||
<field name="warn_supplier_scope"/>
|
||||
</setting>
|
||||
|
||||
<!-- Nouveau champ : Sélection de fournisseurs spécifiques (apparaît si 'specific' est sélectionné) -->
|
||||
<setting
|
||||
id="warn_supplier_specific_ids_settings"
|
||||
help="Select specific vendors to apply overdue warnings if applicable"
|
||||
invisible="warn_supplier_scope != 'specific' or not warn_supplier_overdue"
|
||||
>
|
||||
<field name="warn_supplier_specific_ids" widget="many2many_tags"/>
|
||||
</setting>
|
||||
</block>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- Ajout de l'entrée dans le menu Configuration -->
|
||||
<record id="res_config_settings_action_supplier_overdue" model="ir.actions.act_window">
|
||||
<field name="name">Supplier Overdue Settings</field>
|
||||
<field name="res_model">res.config.settings</field>
|
||||
<field name="view_mode">form</field>
|
||||
<field name="target">new</field>
|
||||
</record>
|
||||
|
||||
<menuitem id="menu_supplier_overdue_settings" name="Supplier Overdue Settings"
|
||||
parent="purchase.menu_purchase_config" action="res_config_settings_action_supplier_overdue"
|
||||
sequence="10"/>
|
||||
</odoo>
|
||||
Loading…
Reference in a new issue