Files

323 lines
16 KiB
Python
Raw Permalink Normal View History

2026-07-01 14:41:49 +07:00
# -*- coding: utf-8 -*-
import logging
import random
import base64
from io import BytesIO
from odoo import api, fields, models, _, tools
from odoo.exceptions import UserError, AccessError
from odoo.addons.base.models.ir_mail_server import MailDeliveryException
from odoo.addons.auth_signup.models.res_partner import SignupError
_logger = logging.getLogger(__name__)
try:
import qrcode
from qrcode.constants import ERROR_CORRECT_L
except ImportError:
qrcode = None
ERROR_CORRECT_L = None
class MainPin(models.Model):
_name = 'main.pin'
_description = 'SHS Code'
_rec_name = 'custom_group_label'
_inherit = ['mail.thread', 'mail.activity.mixin']
data_term = fields.Integer(string="ចំនួនប្រតិបត្ដិការ", default=1)
name = fields.Char(string="ផ្សេងៗ")
pin_code_id = fields.One2many('pin.spm', 'main_id', string="តារាងលេខកូដ", readonly=True)
reviewer_code = fields.Many2one('res.users', string="អ្នកទទួលលេខកូដ", domain="[('active', '=', True)]")
groups_id = fields.Many2many('res.groups', string="Access Rights")
custom_group_label = fields.Char(string="Group Label")
link_qr = fields.Binary("QR Link", compute='_generate_qrcode', store=False)
file_name = fields.Char(string="ឈ្មោះឯកសារយោង")
files = fields.Binary(string="ឯកសារយោង")
check_con = fields.Boolean(string="បន្ដការសិក្សាថ្នាក់ឧត្ដមសិក្សា")
selection_data = fields.Selection([
('0', 'ថ្នាក់មហាវិទ្យាល័យ'),
('01', 'ថ្នាក់មត្ដេយ្យ, មធ្យម និងមធ្យមសិក្សាទុតិយភូមិ'),
('1', 'សិស្សបន្ដការសិក្សា(មកពីប្រទេសថៃ)')
], string="កម្រិតអាហារូបករណ៍", default='0')
school_kind = fields.Many2one('univer.univer', string="សាលាដែលទទួលបាន",
domain="[('grade_study','in',('01','1','2','3','4'))]")
pass_percent = fields.Float(string="%")
requester = fields.Char(string="គោលដៅ")
# ✅ FIXED: Removed 'id' dependency. Empty depends() means "compute on access"
@api.depends()
def _generate_qrcode(self):
login_url = self.env['ir.config_parameter'].sudo().get_param('web.base.url',
'https://reg.amt.live') + '/web/login'
for record in self:
record.link_qr = False
if not (qrcode and ERROR_CORRECT_L):
continue
try:
qr = qrcode.QRCode(version=1, error_correction=ERROR_CORRECT_L, box_size=4, border=2)
qr.add_data(login_url)
qr.make(fit=True)
img = qr.make_image(fill_color="black", back_color="white")
buffered = BytesIO()
img.save(buffered, format="PNG")
record.link_qr = base64.b64encode(buffered.getvalue())
except Exception as e:
_logger.error(f"QR generation failed for {record.id}: {e}")
@api.onchange('data_term')
def _onchange_data_term(self):
default_group = self.env.ref("register_v15.groups_amt", raise_if_not_found=False)
if default_group:
self.groups_id = [(6, 0, [default_group.id])]
self.custom_group_label = 'ផ្គូផ្គង'
@api.constrains('data_term')
def _check_data_term(self):
for rec in self:
if rec.data_term < 1:
raise UserError(_("ចំនួនប្រតិបត្ដិការត្រូវតែយ៉ាងតិច 1"))
if rec.data_term > 1000:
raise UserError(_("ចំនួនប្រតិបត្ដិការអតិបរមាគឺ 1000"))
def compute_code(self):
self.ensure_one()
if not self.data_term or self.data_term < 1:
raise UserError(_("សូមបញ្ចូលចំនួនប្រតិបត្ដិការ"))
target_group = self.env.ref("register_v15.groups_amt", raise_if_not_found=False)
pin_records = []
created_users = []
failed_count = 0
for i in range(1, self.data_term + 1):
pin_code = str(random.randint(100000, 999999))
identifier = f"{self.name or 'PIN'}{pin_code}{i:03d}"
valid_email = f"{identifier.lower()}@reg.amt.live"
pin_records.append((0, 0, {
'name': identifier,
'data_rec': self.name or '',
'main_id': self.id,
}))
user_vals = {
'name': identifier,
'login': identifier,
'email': valid_email,
'password': identifier,
'active': True,
'first_login': True,
'kind': self.selection_data,
'pin_code': pin_code,
'groups': self.custom_group_label or 'ប្រឡង',
}
# ✅ Use standard Odoo field name: groups_id
if target_group:
user_vals['group_ids'] = [(4, target_group.id)]
elif self.groups_id:
user_vals['group_ids'] = [(6, 0, self.groups_id.ids)]
try:
# 🔒 Prevent password reset emails from triggering
user = self.env['res.users'].sudo().with_context(no_reset_password=True).create(user_vals)
# 📝 Optional: Log for verification
_logger.info(f"✅ Created: {user.login} | Password={pin_code} | Groups={user.group_ids.mapped('name')}")
created_users.append(user)
except Exception as e:
failed_count += 1
_logger.error(f"❌ Failed to create '{identifier}': {e}", exc_info=True)
continue
if pin_records:
self.pin_code_id = pin_records
if failed_count > 0:
return {
'type': 'ir.actions.client', 'tag': 'display_notification',
'params': {
'title': _('បញ្ហា!'),
'message': _('បានបង្កើត %d, បរាជ័យ %d។ ពិនិត្យ log!') % (len(created_users), failed_count),
'type': 'warning', 'sticky': True,
}
}
return {
'type': 'ir.actions.client', 'tag': 'display_notification',
'params': {
'title': _('ជោគជ័យ!'),
'message': _('បានបង្កើត %d លេខកូដ និងគណនីដោយជោគជ័យ') % len(created_users),
'type': 'success', 'sticky': False,
}
}
def get_spm_code(self):
target_group = self.env.ref("register_v15.groups_amt", raise_if_not_found=False)
for code in self.pin_code_id.filtered(lambda c: not c.status):
user = self.env['res.users'].sudo().search([('login', '=', code.name), ('active', '=', False)], limit=1)
if not user:
continue
update_vals = {'active': True, 'first_login': True, 'kind': self.selection_data}
if target_group and target_group not in user.groups_id:
update_vals['groups_id'] = [(4, target_group.id)]
user.write(update_vals)
code.write({'status': True})
return True
class PinSPM(models.Model):
_name = "pin.spm"
_description = "Pin Code for Scholarship"
_inherit = ['mail.thread']
name = fields.Char(index=True, default=lambda self: _('New'), string="លេខកូដ", readonly=True)
data = fields.Integer(string="ចំនួនប្រតិបត្ដិការ")
status = fields.Boolean(default=False, string="Used")
reviewer_ids = fields.Many2many('res.users', string="អ្នកត្រួតពិនិត្យលើ")
main_id = fields.Many2one('main.pin', string="Main Reference", ondelete='cascade', required=True)
data_rec = fields.Char(string="ឈ្មោះខេត្ដ/ក្រុង ផ្សេងៗ")
reviewer = fields.Many2one("res.users", string="អ្នកទទួលលេខកូដ")
user_use_code = fields.Many2one('res.users', string="ឈ្មោះអ្នកប្រើប្រាស់", readonly=True)
info_name = fields.Many2one('info.info', string="ឈ្មោះក្នុងទំរងបំពេញពាក្យ")
name_info = fields.Char(related="info_name.name", store=True, readonly=False, string="Name")
email = fields.Char(related="info_name.email", store=True, readonly=True, string="Email")
gender = fields.Many2one(related="info_name.gender", store=True, readonly=False, string="Gender")
eng_name = fields.Char(related="info_name.eng_name", store=True, readonly=False, string="English Name")
dob = fields.Date(related="info_name.dob", store=True, readonly=False, string="DOB")
id_card = fields.Char(related="info_name.id_card", store=True, readonly=False, string="ID Card")
phone = fields.Char(related="info_name.phone", store=True, readonly=False, string="Phone")
study_level = fields.Selection(related="info_name.grade_study", store=True, readonly=False, string="Study Level")
create_uids = fields.Many2one(related="info_name.create_uid", store=True, readonly=False, string="Created By")
group_register = fields.Char(related="info_name.groups", store=True, readonly=False, string="Registration Group")
def check_use_code(self):
active_ids = self.env.context.get('active_ids')
if not active_ids:
return False
success = 0
for code in self.browse(active_ids):
if code.status:
continue
user = self.env['res.users'].sudo().search([('login', '=', code.name), ('active', '=', False)], limit=1)
if not user:
continue
user.write({'active': True, 'x_pin_code_spm': code.id, 'first_login': True})
info_user = self.env['info.info'].sudo().search(
['|', ('create_uid', '=', user.id), ('email', '=', user.login)], limit=1)
code.write({'info_name': info_user.id if info_user else False, 'user_use_code': user.id, 'status': True})
if info_user and not info_user.x_manager and code.reviewer:
info_user.x_manager = code.reviewer
success += 1
return {
'type': 'ir.actions.client', 'tag': 'display_notification',
'params': {'title': _('ជោគជ័យ'), 'message': _('បានផ្ទៀងផ្ទាត់ %d លេខកូដ') % success, 'type': 'success'}
} if success else False
class ReviewGroups(models.Model):
_name = 'rev.groups'
_description = 'Groups Review'
name = fields.Char(string="SHS Name", required=True, translate=True)
active = fields.Boolean(default=True)
class ResUsers(models.Model):
_name = "res.users"
_inherit = ["res.users", "mail.thread", "mail.activity.mixin"]
_description = "Signup User"
pin_code = fields.Char(string='PIN Code', size=6)
is_sending_email = fields.Boolean(string="Email Sent", default=False)
count_request_pin_code = fields.Integer(string="PIN Request Count", default=0)
groups = fields.Char(string="Group Label")
kind = fields.Char(string="Kind/Type")
first_login = fields.Boolean(default=True)
x_pin_code_spm = fields.Many2one('pin.spm', string="Pin Code SPM",
domain="[('name','=',login), ('status','=',False)]")
x_code_name = fields.Char(related='x_pin_code_spm.name', string='Code Name', store=True, readonly=True)
@api.model_create_multi
def create(self, vals_list):
users = super(ResUsers, self).create(vals_list)
template = self.env.ref('ck_signup.sending_pin_code_template', raise_if_not_found=False)
for user in users:
if self._context.get('skip_email_sending') or not template or not user.email:
continue
pin = str(random.randint(100000, 999999))
user.write({'pin_code': pin, 'active': False, 'first_login': True, 'groups': 'ប្រឡង',
'is_sending_email': True})
self._send_pin_email(user, pin, template)
return users
def _send_pin_email(self, user, pin_code, template):
try:
company = self.env.company
mail_from = company.email or tools.config.get('email_from') or 'noreply@localhost'
body = f"""
<div style="font-family: Arial, sans-serif; max-width: 600px; margin: auto; padding: 20px; border: 1px solid #eee; border-radius: 8px;">
<h3 style="color: #2c3e50;">{_('Pin Code ដើម្បីផ្ទៀងផ្ទាត់គណនី')}</h3>
<p>{template.body_html or ''}</p>
<div style="background: #f8f9fa; padding: 15px; border-left: 4px solid #3498db; margin: 20px 0; text-align: center;">
<strong style="font-size: 18px;">លេខកូដផ្ទៀងផ្ទាត់:</strong><br>
<span style="font-size: 28px; letter-spacing: 6px; font-weight: bold; color: #e74c3c;">{pin_code}</span>
</div>
<p>{_('សូមបញ្ចូលលេខកូដខាងលើ រួចចុច ផ្ទៀងផ្ទាត់!')}</p>
</div>"""
self.env['mail.mail'].sudo().create({
'subject': _('Pin Code ដើម្បីផ្ទៀងផ្ទាត់ - %s') % company.name,
'email_from': mail_from, 'email_to': user.email, 'body_html': body, 'auto_delete': True,
}).send()
except Exception as e:
_logger.error(f"Failed to send PIN to {user.email}: {e}")
@api.model
def signup(self, values, token=None):
if token:
partner = self.env['res.partner'].sudo()._signup_retrieve_partner(token, check_validity=True,
raise_exception=True)
partner.write({'signup_token': False, 'signup_type': False, 'signup_expiration': False})
user = self.env['res.users'].sudo().search([('partner_id', '=', partner.id), ('active', '=', False)],
limit=1)
for f in ['city', 'country_id', 'lang', 'tz']: values.pop(f, None)
if user:
values.pop('login', None);
values.pop('name', None)
user.write(values)
return self.env.cr.dbname, user.login, values.get('password')
else:
values.update({'name': partner.name, 'partner_id': partner.id,
'email': values.get('email') or values.get('login')})
if partner.company_id:
values['company_id'] = partner.company_id.id
values['company_ids'] = [(6, 0, [partner.company_id.id])]
return self._signup_create_user(values)
else:
values['email'] = values.get('email') or values.get('login')
return self._signup_create_user(values)
def _signup_create_user(self, values):
values.setdefault('active', False)
values.setdefault('first_login', True)
user = self.with_context(skip_email_sending=True).sudo().create(values)
return self.env.cr.dbname, user.login, values.get('password')
def generate_and_send_new_pin_code(self, login=None):
user = self.env['res.users'].sudo().search([('login', '=', login), ('active', '=', False)],
limit=1) if login else self
if not user or user.count_request_pin_code >= 5: return False
template = self.env.ref('ck_signup.sending_pin_code_template', raise_if_not_found=False)
if not template: return False
new_pin = str(random.randint(100000, 999999))
user.write({'pin_code': new_pin, 'count_request_pin_code': user.count_request_pin_code + 1})
self._send_pin_email(user, new_pin, template)
return True
def verify_pin_code(self, pin_input):
self.ensure_one()
if not self.pin_code or pin_input != self.pin_code:
return False
self.write({'active': True, 'pin_code': False, 'first_login': True})
return True