[FIX] otp_sms_auth_custom

This commit is contained in:
Samir Ladoui 2025-07-15 15:33:12 +01:00
parent 3836301242
commit e4ee09e9c1
6 changed files with 131 additions and 11 deletions

View File

@ -8,6 +8,7 @@
'data': [
'security/ir.model.access.csv',
'views/res_users_view.xml',
'views/res_config_settings_views.xml',
],
'installable': True,
'application': False,

View File

@ -15,7 +15,8 @@ class AuthSignInHome(Home):
if user:
data['status'] = True
data['msg'] = 'OTP is generated and sent successfully'
data['otp'] = user.generate_otp() if user else ""
# data['otp'] = user.generate_otp() if user else ""
user.generate_otp()
return json.dumps(data)

View File

@ -1,2 +1,3 @@
# -*- coding: utf-8 -*-
from . import res_config_settings
from . import res_users

View File

@ -0,0 +1,47 @@
import random
import requests
import logging
from odoo import models, fields, api
from odoo.exceptions import ValidationError
_logger = logging.getLogger(__name__)
class ResCompany(models.Model):
_inherit = 'res.company'
# -- SMS API Settings --
sms_mode = fields.Selection(
[('test', 'Test Mode (Disable SMS Sending)'), ('prod', 'Production')],
string="SMS Mode",
default='test',
required=True,
help="In Test Mode, OTP will be generated and logged but no SMS will be sent."
)
sms_api_url = fields.Char(string="SMS API URL")
sms_api_token = fields.Char(string="SMS API Token", help="Your Bearer token for the SMS API.")
sms_sender_name = fields.Char(string="SMS Sender Name")
class ResConfigSettings(models.TransientModel):
_inherit = 'res.config.settings'
# -- SMS API Settings --
# The 'related' attribute links these transient fields to the fields on res.company
sms_mode = fields.Selection(
related='company_id.sms_mode',
readonly=False,
required=True
)
sms_api_url = fields.Char(
related='company_id.sms_api_url',
readonly=False
)
sms_api_token = fields.Char(
related='company_id.sms_api_token',
readonly=False
)
sms_sender_name = fields.Char(
related='company_id.sms_sender_name',
readonly=False
)

View File

@ -15,45 +15,57 @@ class ResUsersInherit(models.Model):
otp_mobile_phone = fields.Char(string="Mobile Phone")
otp_code = fields.Char(string="OTP Code", readonly=True)
def generate_otp(self):
"""Generate a random OTP and send via SMS."""
"""
Generate a random OTP, save it, and send it via SMS based on company settings.
"""
self.ensure_one()
if not self.otp_mobile_phone:
raise ValidationError("No mobile phone number provided for OTP.")
# Get the configuration from the user's current company
company = self.env.company
# Generate OTP regardless of mode
otp = str(random.randint(1000, 9999))
self.otp_code = otp
# Taqnyat SMS API details
api_url = "https://api.taqnyat.sa/v1/messages"
api_token = "0d275c8a69ae5f1a6fd0fca19137db57"
sender_name = "ENSAN"
# Check the SMS mode
if company.sms_mode == 'test':
_logger.info(f"--- SMS TEST MODE --- OTP for user {self.name} is {otp}. SMS not sent.")
# In test mode, we can return the OTP directly for testing purposes
return otp
# --- Production Mode Logic ---
# Validate that all configuration fields are set
if not all([company.sms_api_url, company.sms_api_token, company.sms_sender_name]):
raise ValidationError("SMS API settings (URL, Token, Sender Name) are not fully configured in General Settings.")
# SMS payload
payload = {
"recipients": [self.otp_mobile_phone],
"body": f"Your OTP code is: {otp}",
"sender": sender_name,
"sender": company.sms_sender_name,
}
# Headers for authentication
headers = {
"Authorization": f"Bearer {api_token}",
"Authorization": f"Bearer {company.sms_api_token}",
"Content-Type": "application/json",
}
# Send the SMS
try:
response = requests.post(api_url, json=payload, headers=headers)
response = requests.post(company.sms_api_url, json=payload, headers=headers, timeout=10)
response.raise_for_status() # Raise an error for non-2xx responses
_logger.info(f"OTP {otp} sent successfully to {self.otp_mobile_phone}")
except requests.exceptions.RequestException as e:
_logger.error(f"Failed to send OTP to {self.otp_mobile_phone}: {e}")
raise ValueError("Failed to send OTP. Please try again later.")
raise ValidationError("Failed to send OTP. Please check API configuration or try again later.")
return otp
def validate_otp(self, otp):
"""Validate the OTP."""
self.ensure_one()

View File

@ -0,0 +1,58 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<record id="res_config_settings_view_form_sms_api" model="ir.ui.view">
<field name="name">res.config.settings.view.form.inherit.sms.api</field>
<field name="model">res.config.settings</field>
<field name="inherit_id" ref="base.res_config_settings_view_form"/>
<field name="arch" type="xml">
<xpath expr="//div[@id='business_documents']" position="after">
<h2>SMS API Configuration</h2>
<div class="row mt16 o_settings_container" id="sms_api_settings">
<div class="col-12 col-lg-6 o_setting_box">
<div class="o_setting_right_pane">
<span class="o_form_label">Mode</span>
<div class="text-muted">
Enable or disable SMS sending
</div>
<div class="content-group">
<div class="row mt16">
<label for="sms_mode" class="col-lg-3 o_light_label"/>
<field name="sms_mode"/>
</div>
</div>
</div>
</div>
<div class="col-12 col-lg-6 o_setting_box" attrs="{'invisible': [('sms_mode', '=', 'test')]}">
<div class="o_setting_right_pane">
<span class="o_form_label">API Credentials</span>
<div class="text-muted">
Credentials for your SMS provider
</div>
<div class="content-group">
<div class="row mt16">
<label for="sms_api_url" string="API URL" class="col-lg-4 o_light_label"/>
<field name="sms_api_url" attrs="{'required': [('sms_mode', '=', 'prod')]}" placeholder="e.g. https://api.twilio.com/2010-04-01/Accounts/AC.../Messages.json"/>
</div>
<div class="row mt16">
<label for="sms_api_token" string="API Token" class="col-lg-4 o_light_label"/>
<field name="sms_api_token" attrs="{'required': [('sms_mode', '=', 'prod')]}" password="True"/>
</div>
<div class="row mt16">
<label for="sms_sender_name" string="Sender Name" class="col-lg-4 o_light_label"/>
<field name="sms_sender_name" attrs="{'required': [('sms_mode', '=', 'prod')]}"/>
</div>
</div>
</div>
</div>
</div>
</xpath>
</field>
</record>
</data>
</odoo>