Files
2026-07-01 14:41:49 +07:00

156 lines
6.1 KiB
Python

# -*- coding: utf-8 -*-
import random
import re
import secrets
import string
from datetime import timedelta
from odoo import api, fields, models, _
from odoo.exceptions import ValidationError
class SaasTrialRequest(models.Model):
"""Flow 1: User Registration & App Selection Flow
Visits domain-name.com/trial
-> Select Apps (Website, CRM, Accounting, ...)
-> Fill Form (Email, Company, Phone, Name, ...)
-> Create Account & Verify Email
-> 15-Day Free Trial Activated
-> User Clicks "Continue" --> triggers Flow 2 (provisioning)
"""
_name = 'saas.trial.request'
_description = 'SaaS Trial Signup Request'
_inherit = ['mail.thread', 'mail.activity.mixin']
_order = 'create_date desc'
# ---- Step: Fill Form (Email, Company, Phone, Name) ----
name = fields.Char('Full Name', required=True, tracking=True)
company_name = fields.Char('Company Name', tracking=True)
email = fields.Char('Email', required=True, tracking=True)
phone = fields.Char('Phone')
country_id = fields.Many2one('res.country', string='Country')
lang = fields.Selection(lambda self: self.env['res.lang'].get_installed(), string='Language')
company_size = fields.Selection([
('1-5', '1 - 5 employees'),
('6-20', '6 - 20 employees'),
('21-50', '21 - 50 employees'),
('51-200', '51 - 200 employees'),
('200+', '200+ employees'),
], string='Company Size')
primary_interest = fields.Selection([
('my_company', 'Use it in my company'),
('client', 'Use it for a client'),
('explore', 'Just exploring'),
], string='Primary Interest')
# ---- Step: Select Apps ----
app_ids = fields.Many2many('saas.app', string='Selected Apps', required=True)
# ---- Step: Create Account & Verify Email ----
state = fields.Selection([
('draft', 'Form Submitted'),
('pending_verification', 'Pending Email Verification'),
('verified', 'Email Verified'),
('trial_active', '15-Day Trial Activated'),
('provisioning', 'Provisioning Database'),
('ready', 'Instance Ready'),
('failed', 'Provisioning Failed'),
], default='draft', tracking=True, required=True)
verification_token = fields.Char(copy=False)
verification_sent_date = fields.Datetime()
verified_date = fields.Datetime()
# ---- Step: 15-Day Free Trial Activated ----
trial_start_date = fields.Datetime()
trial_end_date = fields.Datetime()
# Link forward to Flow 2
database_id = fields.Many2one('saas.database', string='Provisioned Database', copy=False)
subscription_id = fields.Many2one('saas.subscription', string='Subscription', copy=False)
_sql_constraints = [
('email_unique', 'unique(email)', 'A trial request with this email already exists.'),
]
@api.constrains('email')
def _check_email(self):
pattern = r"^[^@\s]+@[^@\s]+\.[^@\s]+$"
for rec in self:
if not re.match(pattern, rec.email or ''):
raise ValidationError(_("Please enter a valid email address."))
# ------------------------------------------------------------------
# Step: Create Account -> send verification email
# ------------------------------------------------------------------
def action_submit_and_send_verification(self):
"""Called right after the public form is submitted."""
for rec in self:
rec.verification_token = ''.join(
secrets.choice(string.ascii_letters + string.digits) for _ in range(32)
)
rec.verification_sent_date = fields.Datetime.now()
rec.state = 'pending_verification'
rec._send_verification_email()
return True
def _send_verification_email(self):
template = self.env.ref('saas_trial_portal.mail_template_verify_email')
for rec in self:
template.send_mail(rec.id, force_send=True)
# ------------------------------------------------------------------
# Step: Verify Email (user clicks link from email)
# ------------------------------------------------------------------
def action_verify_email(self, token):
"""Validates token coming from the verification link."""
self.ensure_one()
if token != self.verification_token:
raise ValidationError(_("Invalid or expired verification link."))
self.write({
'state': 'verified',
'verified_date': fields.Datetime.now(),
})
self._activate_trial()
return True
# ------------------------------------------------------------------
# Step: 15-Day Free Trial Activated
# ------------------------------------------------------------------
def _activate_trial(self):
self.ensure_one()
now = fields.Datetime.now()
self.write({
'state': 'trial_active',
'trial_start_date': now,
'trial_end_date': now + timedelta(days=15),
})
# Create the subscription record in trial mode (Flow 3 baseline state)
if not self.subscription_id:
self.subscription_id = self.env['saas.subscription'].create({
'trial_request_id': self.id,
'is_trial': True,
'is_premium': False,
'expiry_date': self.trial_end_date,
})
# ------------------------------------------------------------------
# Step: User Clicks "Continue" -> kicks off Flow 2 provisioning
# ------------------------------------------------------------------
def action_continue_to_provisioning(self):
self.ensure_one()
if self.state != 'trial_active':
raise ValidationError(_("Trial must be active before provisioning a database."))
self.state = 'provisioning'
database = self.env['saas.database'].create({
'trial_request_id': self.id,
'company_name': self.company_name,
'app_ids': [(6, 0, self.app_ids.ids)],
})
self.database_id = database.id
# Run the full provisioning pipeline (Flow 2)
database.action_provision()
return database