fase 3.5: licenciamiento por plan (clave firmada, planes basico/profesional/enterprise/custom, modulos incluidos y limites de usuarios/sucursales/citas/almacenamiento, modo aviso o bloqueo, panel de licencia) + API keys gestionables con alcances y revocacion (arregla la API publica que estaba rota) + webhooks salientes firmados con outbox, reintentos y bitacora; herramientas de vendedor y docs
This commit is contained in:
125
odoo-addons/skeen_plataforma/models/limites.py
Normal file
125
odoo-addons/skeen_plataforma/models/limites.py
Normal file
@@ -0,0 +1,125 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Aplicación de los límites de la licencia.
|
||||
|
||||
Solo actúan con `modo='bloqueo'` y un límite > 0. Con `aviso` (default) o sin
|
||||
licencia no bloquean nada. Nunca afectan tareas internas: se pueden saltar con
|
||||
el contexto `skeen_licencia_skip=True` (cron, migraciones, importaciones).
|
||||
"""
|
||||
|
||||
import logging
|
||||
from datetime import date
|
||||
|
||||
from odoo import _, api, fields, models
|
||||
from odoo.exceptions import UserError
|
||||
|
||||
from odoo.addons.skeen_citas.clinic_settings import (
|
||||
licencia_de, limite_licencia, licencia_bloqueo_activo,
|
||||
)
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
MENSAJES = {
|
||||
'usuarios_max': 'usuarios',
|
||||
'sucursales_max': 'sucursales',
|
||||
'citas_mes_max': 'citas este mes',
|
||||
'almacenamiento_mb': 'MB de almacenamiento',
|
||||
}
|
||||
|
||||
|
||||
def _skip(env):
|
||||
return bool(env.context.get('skeen_licencia_skip')
|
||||
or env.context.get('install_mode'))
|
||||
|
||||
|
||||
def exigir_limite(env, clave, etiqueta, actual, agregando=1):
|
||||
"""Lanza UserError si `actual + agregando` supera el límite de `clave`.
|
||||
|
||||
Con modo=aviso solo deja una advertencia en el log. Devuelve False si no
|
||||
aplica (sin licencia, sin límite o contexto de salto).
|
||||
"""
|
||||
limite = limite_licencia(env, clave)
|
||||
if limite <= 0 or _skip(env):
|
||||
return False
|
||||
licencia = licencia_de(env)
|
||||
if not licencia_bloqueo_activo(licencia):
|
||||
if actual + agregando > limite:
|
||||
_logger.info('SKEEN licencia (aviso): %s excede el límite de %s '
|
||||
'(%s + %s > %s)', etiqueta, limite, actual, agregando, limite)
|
||||
return False
|
||||
if actual + agregando > limite:
|
||||
raise UserError(_(
|
||||
'Tu plan permite hasta %(limite)s %(etiqueta)s y ya tienes '
|
||||
'%(actual)s. Para ampliar el límite, contacta a tu proveedor y '
|
||||
'solicita una licencia con un tope mayor.'
|
||||
) % {'limite': limite, 'etiqueta': etiqueta, 'actual': actual})
|
||||
return True
|
||||
|
||||
|
||||
def _contar_usuarios(env):
|
||||
return env['skeen.frontend.user'].sudo().search_count([('active', '=', True)])
|
||||
|
||||
|
||||
def _contar_sucursales(env):
|
||||
return env['skeen.sucursal'].sudo().search_count([('active', '=', True)])
|
||||
|
||||
|
||||
def _contar_citas_mes(env):
|
||||
hoy = date.today()
|
||||
inicio = hoy.replace(day=1)
|
||||
if hoy.month == 12:
|
||||
fin = hoy.replace(year=hoy.year + 1, month=1, day=1)
|
||||
else:
|
||||
fin = hoy.replace(month=hoy.month + 1, day=1)
|
||||
return env['skeen.cita'].sudo().search_count([
|
||||
('date', '>=', inicio), ('date', '<', fin),
|
||||
])
|
||||
|
||||
|
||||
def _mb_almacenamiento(env):
|
||||
env.cr.execute('SELECT COALESCE(SUM(file_size), 0) FROM ir_attachment')
|
||||
total = env.cr.fetchone()[0] or 0
|
||||
return total / (1024.0 * 1024.0)
|
||||
|
||||
|
||||
class SkeenFrontendUser(models.Model):
|
||||
_inherit = 'skeen.frontend.user'
|
||||
|
||||
@api.model_create_multi
|
||||
def create(self, vals_list):
|
||||
exigir_limite(self.env, 'usuarios_max', MENSAJES['usuarios_max'],
|
||||
_contar_usuarios(self.env), len(vals_list))
|
||||
return super().create(vals_list)
|
||||
|
||||
|
||||
class SkeenSucursal(models.Model):
|
||||
_inherit = 'skeen.sucursal'
|
||||
|
||||
@api.model_create_multi
|
||||
def create(self, vals_list):
|
||||
exigir_limite(self.env, 'sucursales_max', MENSAJES['sucursales_max'],
|
||||
_contar_sucursales(self.env), len(vals_list))
|
||||
return super().create(vals_list)
|
||||
|
||||
|
||||
class SkeenCita(models.Model):
|
||||
_inherit = 'skeen.cita'
|
||||
|
||||
@api.model_create_multi
|
||||
def create(self, vals_list):
|
||||
exigir_limite(self.env, 'citas_mes_max', MENSAJES['citas_mes_max'],
|
||||
_contar_citas_mes(self.env), len(vals_list))
|
||||
return super().create(vals_list)
|
||||
|
||||
|
||||
class IrAttachment(models.Model):
|
||||
_inherit = 'ir.attachment'
|
||||
|
||||
@api.model_create_multi
|
||||
def create(self, vals_list):
|
||||
if limite_licencia(self.env, 'almacenamiento_mb') > 0 and not _skip(self.env):
|
||||
nuevos_mb = sum(max(0, int(v.get('file_size') or 0)) for v in vals_list) \
|
||||
/ (1024.0 * 1024.0)
|
||||
exigir_limite(self.env, 'almacenamiento_mb',
|
||||
MENSAJES['almacenamiento_mb'],
|
||||
_mb_almacenamiento(self.env), nuevos_mb)
|
||||
return super().create(vals_list)
|
||||
Reference in New Issue
Block a user