Enviar correo
This commit is contained in:
parent
28bb6a74d1
commit
8ff857ec73
|
@ -14,8 +14,15 @@
|
|||
#~ https://github.com/kennethreitz/requests/blob/v1.2.3/requests/structures.py#L37
|
||||
|
||||
import re
|
||||
import smtplib
|
||||
import collections
|
||||
|
||||
from collections import OrderedDict
|
||||
from email.mime.multipart import MIMEMultipart
|
||||
from email.mime.base import MIMEBase
|
||||
from email.mime.text import MIMEText
|
||||
from email import encoders
|
||||
from email.utils import formatdate
|
||||
|
||||
|
||||
class CaseInsensitiveDict(collections.MutableMapping):
|
||||
|
@ -223,3 +230,80 @@ class NumLet(object):
|
|||
return re.sub('$', 's', palabra)
|
||||
else:
|
||||
return palabra + 'es'
|
||||
|
||||
|
||||
class SendMail(object):
|
||||
|
||||
def __init__(self, config):
|
||||
self._config = config
|
||||
self._server = None
|
||||
self._error = ''
|
||||
self._is_connect = self._login()
|
||||
|
||||
@property
|
||||
def is_connect(self):
|
||||
return self._is_connect
|
||||
|
||||
@property
|
||||
def error(self):
|
||||
return self._error
|
||||
|
||||
def _login(self):
|
||||
try:
|
||||
if self._config['ssl']:
|
||||
self._server = smtplib.SMTP_SSL(
|
||||
self._config['servidor'],
|
||||
self._config['puerto'], timeout=10)
|
||||
else:
|
||||
self._server = smtplib.SMTP(
|
||||
self._config['servidor'],
|
||||
self._config['puerto'], timeout=10)
|
||||
self._server.login(self._config['usuario'], self._config['contra'])
|
||||
return True
|
||||
except smtplib.SMTPAuthenticationError as e:
|
||||
if '535' in str(e):
|
||||
self._error = 'Nombre de usuario o contraseña inválidos'
|
||||
return False
|
||||
if '534' in str(e) and 'gmail' in self._config['servidor']:
|
||||
self._error = 'Necesitas activar el acceso a otras ' \
|
||||
'aplicaciones en tu cuenta de GMail'
|
||||
return False
|
||||
except smtplib.SMTPException as e:
|
||||
self._error = str(e)
|
||||
return False
|
||||
except Exception as e:
|
||||
self._error = str(e)
|
||||
return False
|
||||
return
|
||||
|
||||
def send(self, options):
|
||||
try:
|
||||
message = MIMEMultipart()
|
||||
message['From'] = self._config['usuario']
|
||||
message['To'] = options['para']
|
||||
message['CC'] = options['copia']
|
||||
message['Subject'] = options['asunto']
|
||||
message['Date'] = formatdate(localtime=True)
|
||||
message.attach(MIMEText(options['mensaje'], 'html'))
|
||||
for f in options['files']:
|
||||
part = MIMEBase('application', 'octet-stream')
|
||||
part.set_payload(f[0])
|
||||
encoders.encode_base64(part)
|
||||
part.add_header(
|
||||
'Content-Disposition',
|
||||
"attachment; filename={}".format(f[1]))
|
||||
message.attach(part)
|
||||
|
||||
receivers = options['para'].split(',') + options['copia'].split(',')
|
||||
self._server.sendmail(
|
||||
self._config['usuario'], receivers, message.as_string())
|
||||
return ''
|
||||
except Exception as e:
|
||||
return str(e)
|
||||
|
||||
def close(self):
|
||||
try:
|
||||
self._server.quit()
|
||||
except:
|
||||
pass
|
||||
return
|
||||
|
|
|
@ -76,12 +76,33 @@ class AppValues(object):
|
|||
if file_object is None:
|
||||
session = req.env['beaker.session']
|
||||
values = req.params
|
||||
req.context['result'] = self._db.validate_cert(values, session)
|
||||
if table == 'correo':
|
||||
req.context['result'] = self._db.validate_email(values)
|
||||
elif table == 'sendmail':
|
||||
req.context['result'] = self._db.send_email(values, session)
|
||||
else:
|
||||
req.context['result'] = self._db.validate_cert(values, session)
|
||||
else:
|
||||
req.context['result'] = self._db.add_cert(file_object)
|
||||
resp.status = falcon.HTTP_200
|
||||
|
||||
|
||||
class AppConfig(object):
|
||||
|
||||
def __init__(self, db):
|
||||
self._db = db
|
||||
|
||||
def on_get(self, req, resp):
|
||||
values = req.params
|
||||
req.context['result'] = self._db.get_config(values)
|
||||
resp.status = falcon.HTTP_200
|
||||
|
||||
def on_post(self, req, resp):
|
||||
values = req.params
|
||||
req.context['result'] = self._db.add_config(values)
|
||||
resp.status = falcon.HTTP_200
|
||||
|
||||
|
||||
|
||||
class AppPartners(object):
|
||||
|
||||
|
|
|
@ -15,7 +15,9 @@ import time
|
|||
import unicodedata
|
||||
import uuid
|
||||
import zipfile
|
||||
|
||||
from io import BytesIO
|
||||
from smtplib import SMTPException, SMTPAuthenticationError
|
||||
from xml.etree import ElementTree as ET
|
||||
|
||||
import uno
|
||||
|
@ -25,7 +27,7 @@ from com.sun.star.awt import Size
|
|||
import pyqrcode
|
||||
from dateutil import parser
|
||||
|
||||
from .helper import CaseInsensitiveDict, NumLet
|
||||
from .helper import CaseInsensitiveDict, NumLet, SendMail
|
||||
from settings import DEBUG, log, template_lookup, COMPANIES, DB_SAT, \
|
||||
PATH_XSLT, PATH_XSLTPROC, PATH_OPENSSL, PATH_TEMPLATES, PRE
|
||||
|
||||
|
@ -926,3 +928,33 @@ def to_zip(*files):
|
|||
|
||||
return zip_buffer.getvalue()
|
||||
|
||||
|
||||
def make_fields(xml):
|
||||
doc = ET.fromstring(xml)
|
||||
data = CaseInsensitiveDict(doc.attrib.copy())
|
||||
data.pop('certificado')
|
||||
data.pop('sello')
|
||||
version = data['version']
|
||||
receptor = doc.find('{}Receptor'.format(PRE[version]))
|
||||
receptor = CaseInsensitiveDict(receptor.attrib.copy())
|
||||
data['receptor_nombre'] = receptor['nombre']
|
||||
data['receptor_rfc'] = receptor['rfc']
|
||||
data = {k.lower(): v for k, v in data.items()}
|
||||
return data
|
||||
|
||||
|
||||
def make_info_mail(data, fields):
|
||||
return data.format(**fields).replace('\n', '<br/>')
|
||||
|
||||
|
||||
def send_mail(data):
|
||||
msg = ''
|
||||
server = SendMail(data['server'])
|
||||
is_connect = server.is_connect
|
||||
if is_connect:
|
||||
msg = server.send(data['options'])
|
||||
else:
|
||||
msg = server.error
|
||||
server.close()
|
||||
return {'ok': is_connect, 'msg': msg}
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ from middleware import (
|
|||
)
|
||||
from models.db import StorageEngine
|
||||
from controllers.main import (
|
||||
AppLogin, AppLogout, AppAdmin, AppEmisor,
|
||||
AppLogin, AppLogout, AppAdmin, AppEmisor, AppConfig,
|
||||
AppMain, AppValues, AppPartners, AppProducts, AppInvoices, AppFolios,
|
||||
AppDocumentos
|
||||
)
|
||||
|
@ -38,6 +38,7 @@ api.add_route('/emisor', AppEmisor(db))
|
|||
api.add_route('/folios', AppFolios(db))
|
||||
api.add_route('/main', AppMain(db))
|
||||
api.add_route('/values/{table}', AppValues(db))
|
||||
api.add_route('/config', AppConfig(db))
|
||||
api.add_route('/doc/{type_doc}/{id_doc}', AppDocumentos(db))
|
||||
api.add_route('/partners', AppPartners(db))
|
||||
api.add_route('/products', AppProducts(db))
|
||||
|
|
|
@ -14,12 +14,24 @@ class StorageEngine(object):
|
|||
def get_values(self, table, values=None):
|
||||
return getattr(self, '_get_{}'.format(table))(values)
|
||||
|
||||
def get_config(self, values):
|
||||
return main.Configuracion.get_(values)
|
||||
|
||||
def add_config(self, values):
|
||||
return main.Configuracion.add(values)
|
||||
|
||||
def add_cert(self, file_object):
|
||||
return main.Certificado.add(file_object)
|
||||
|
||||
def validate_cert(self, values, session):
|
||||
return main.Certificado.validate(values, session)
|
||||
|
||||
def validate_email(self, values):
|
||||
return main.test_correo(values)
|
||||
|
||||
def send_email(self, values, session):
|
||||
return main.Facturas.send(values['id'], session['rfc'])
|
||||
|
||||
def _get_cert(self, values):
|
||||
return main.Certificado.get_data()
|
||||
|
||||
|
|
|
@ -59,9 +59,34 @@ def desconectar():
|
|||
|
||||
|
||||
class Configuracion(BaseModel):
|
||||
clave = TextField()
|
||||
clave = TextField(unique=True)
|
||||
valor = TextField(default='')
|
||||
|
||||
@classmethod
|
||||
def get_(cls, keys):
|
||||
if keys['fields'] == 'correo':
|
||||
fields = ('correo_servidor', 'correo_puerto', 'correo_ssl',
|
||||
'correo_usuario', 'correo_contra', 'correo_copia',
|
||||
'correo_asunto', 'correo_mensaje', 'correo_directo')
|
||||
data = (Configuracion
|
||||
.select()
|
||||
.where(Configuracion.clave.in_(fields))
|
||||
)
|
||||
values = {r.clave: r.valor for r in data}
|
||||
return values
|
||||
|
||||
@classmethod
|
||||
def add(cls, values):
|
||||
try:
|
||||
for k, v in values.items():
|
||||
obj, created = Configuracion.get_or_create(clave=k)
|
||||
obj.valor = v
|
||||
obj.save()
|
||||
return {'ok': True}
|
||||
except Exception as e:
|
||||
log.error(str(e))
|
||||
return {'ok': False, 'msg': str(e)}
|
||||
|
||||
class Meta:
|
||||
order_by = ('clave',)
|
||||
indexes = (
|
||||
|
@ -1059,6 +1084,47 @@ class Facturas(BaseModel):
|
|||
|
||||
return file_zip, name_zip
|
||||
|
||||
@classmethod
|
||||
def send(cls, id, rfc):
|
||||
values = Configuracion.get_({'fields': 'correo'})
|
||||
#~ print (server)
|
||||
obj = Facturas.get(Facturas.id==id)
|
||||
if obj.uuid is None:
|
||||
msg = 'La factura no esta timbrada'
|
||||
return {'ok': False, 'msg': msg}
|
||||
|
||||
if not obj.cliente.correo_facturas:
|
||||
msg = 'El cliente no tiene configurado el correo para facturas'
|
||||
return {'ok': False, 'msg': msg}
|
||||
|
||||
files = (cls.get_zip(id, rfc),)
|
||||
|
||||
fields = util.make_fields(obj.xml)
|
||||
server = {
|
||||
'servidor': values['correo_servidor'],
|
||||
'puerto': values['correo_puerto'],
|
||||
'ssl': bool(int(values['correo_ssl'])),
|
||||
'usuario': values['correo_usuario'],
|
||||
'contra': values['correo_contra'],
|
||||
}
|
||||
options = {
|
||||
'para': obj.cliente.correo_facturas,
|
||||
'copia': values['correo_copia'],
|
||||
'asunto': util.make_info_mail(values['correo_asunto'], fields),
|
||||
'mensaje': util.make_info_mail(values['correo_mensaje'], fields),
|
||||
'files': files,
|
||||
}
|
||||
data= {
|
||||
'server': server,
|
||||
'options': options,
|
||||
}
|
||||
result = util.send_mail(data)
|
||||
if not result['ok'] or result['msg']:
|
||||
return {'ok': False, 'msg': result['msg']}
|
||||
|
||||
msg = 'Factura enviada correctamente'
|
||||
return {'ok': True, 'msg': msg}
|
||||
|
||||
@classmethod
|
||||
def get_(cls, values):
|
||||
rows = tuple(Facturas
|
||||
|
@ -1450,6 +1516,28 @@ def get_sat_key(key):
|
|||
return util.get_sat_key('products', key)
|
||||
|
||||
|
||||
def test_correo(values):
|
||||
server = {
|
||||
'servidor': values['correo_servidor'],
|
||||
'puerto': values['correo_puerto'],
|
||||
'ssl': bool(values['correo_ssl'].replace('0', '')),
|
||||
'usuario': values['correo_usuario'],
|
||||
'contra': values['correo_contra'],
|
||||
}
|
||||
options = {
|
||||
'para': values['correo_usuario'],
|
||||
'copia': values['correo_copia'],
|
||||
'asunto': values['correo_asunto'],
|
||||
'mensaje': values['correo_mensaje'].replace('\n', '<br/>'),
|
||||
'files': [],
|
||||
}
|
||||
data= {
|
||||
'server': server,
|
||||
'options': options,
|
||||
}
|
||||
return util.send_mail(data)
|
||||
|
||||
|
||||
def _init_values():
|
||||
data = (
|
||||
{'key': 'version', 'value': VERSION},
|
||||
|
|
|
@ -16,6 +16,8 @@ var controllers = {
|
|||
$$('up_cert').attachEvent('onUploadComplete', up_cert_upload_complete)
|
||||
$$('cmd_agregar_serie').attachEvent('onItemClick', cmd_agregar_serie_click)
|
||||
$$('grid_folios').attachEvent('onItemClick', grid_folios_click)
|
||||
$$('cmd_probar_correo').attachEvent('onItemClick', cmd_probar_correo_click)
|
||||
$$('cmd_guardar_correo').attachEvent('onItemClick', cmd_guardar_correo_click)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -175,6 +177,24 @@ function get_table_folios(){
|
|||
}
|
||||
|
||||
|
||||
function get_config_correo(){
|
||||
var form = $$('form_correo')
|
||||
var fields = form.getValues()
|
||||
|
||||
webix.ajax().get('/config', {'fields': 'correo'}, {
|
||||
error: function(text, data, xhr) {
|
||||
msg = 'Error al consultar'
|
||||
msg_error(msg)
|
||||
},
|
||||
success: function(text, data, xhr) {
|
||||
var values = data.json()
|
||||
form.setValues(values)
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
|
||||
function multi_admin_change(prevID, nextID){
|
||||
//~ webix.message(nextID)
|
||||
if(nextID == 'app_emisor'){
|
||||
|
@ -188,6 +208,11 @@ function multi_admin_change(prevID, nextID){
|
|||
get_table_folios()
|
||||
return
|
||||
}
|
||||
|
||||
if(nextID == 'app_correo'){
|
||||
get_config_correo()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -442,10 +467,120 @@ function grid_folios_click(id, e, node){
|
|||
msg_error(msg)
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
function validar_correo(values){
|
||||
|
||||
if(!values.correo_servidor.trim()){
|
||||
msg = 'El servidor de salida no puede estar vacío'
|
||||
msg_error(msg)
|
||||
return false
|
||||
}
|
||||
if(!values.correo_puerto){
|
||||
msg = 'El puerto no puede ser cero'
|
||||
msg_error(msg)
|
||||
return false
|
||||
}
|
||||
if(!values.correo_usuario.trim()){
|
||||
msg = 'El nombre de usuario no puede estar vacío'
|
||||
msg_error(msg)
|
||||
return false
|
||||
}
|
||||
if(!values.correo_contra.trim()){
|
||||
msg = 'La contraseña no puede estar vacía'
|
||||
msg_error(msg)
|
||||
return false
|
||||
}
|
||||
if(!values.correo_asunto.trim()){
|
||||
msg = 'El asunto del correo no puede estar vacío'
|
||||
msg_error(msg)
|
||||
return false
|
||||
}
|
||||
if(!values.correo_mensaje.trim()){
|
||||
msg = 'El mensaje del correo no puede estar vacío'
|
||||
msg_error(msg)
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
function cmd_probar_correo_click(){
|
||||
var form = $$('form_correo')
|
||||
var values = form.getValues()
|
||||
|
||||
if(!validar_correo(values)){
|
||||
return
|
||||
}
|
||||
|
||||
webix.ajax().sync().post('/values/correo', values, {
|
||||
error: function(text, data, xhr) {
|
||||
msg = 'Error al probar el correo'
|
||||
msg_error(msg)
|
||||
},
|
||||
success: function(text, data, xhr) {
|
||||
var values = data.json();
|
||||
if (values.ok){
|
||||
msg = 'Correo de prueba enviado correctamente. Ya puedes \
|
||||
guardar esta configuración'
|
||||
msg_sucess(msg)
|
||||
}else{
|
||||
msg_error(values.msg)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
|
||||
function save_config_mail(values){
|
||||
|
||||
webix.ajax().sync().post('/config', values, {
|
||||
error: function(text, data, xhr) {
|
||||
msg = 'Error al guardar la configuración'
|
||||
msg_error(msg)
|
||||
},
|
||||
success: function(text, data, xhr) {
|
||||
var values = data.json();
|
||||
if (values.ok){
|
||||
msg = 'Configuración guardada correctamente'
|
||||
msg_sucess(msg)
|
||||
}else{
|
||||
msg_error(values.msg)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
|
||||
function cmd_guardar_correo_click(){
|
||||
var form = $$('form_correo')
|
||||
var values = form.getValues()
|
||||
|
||||
if(!validar_correo(values)){
|
||||
return
|
||||
}
|
||||
|
||||
msg = 'Asegurate de haber probado la configuración<BR><BR>\
|
||||
¿Estás seguro de guardar estos datos?'
|
||||
webix.confirm({
|
||||
title: 'Configuración de correo',
|
||||
ok: 'Si',
|
||||
cancel: 'No',
|
||||
type: 'confirm-error',
|
||||
text: msg,
|
||||
callback:function(result){
|
||||
if(result){
|
||||
save_config_mail(values)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -642,6 +642,41 @@ function cmd_invoice_timbrar_click(){
|
|||
}
|
||||
|
||||
|
||||
function enviar_correo(row){
|
||||
if(!row.uuid){
|
||||
msg_error('La factura no esta timbrada')
|
||||
return
|
||||
}
|
||||
|
||||
msg = '¿Estás seguro de enviar por correo esta factura?'
|
||||
webix.confirm({
|
||||
title: 'Enviar Factura',
|
||||
ok: 'Si',
|
||||
cancel: 'No',
|
||||
type: 'confirm-error',
|
||||
text: msg,
|
||||
callback:function(result){
|
||||
if(result){
|
||||
webix.ajax().post('/values/sendmail', {'id': row.id}, {
|
||||
error:function(text, data, XmlHttpRequest){
|
||||
msg = 'Ocurrio un error, consulta a soporte técnico'
|
||||
msg_error(msg)
|
||||
},
|
||||
success:function(text, data, XmlHttpRequest){
|
||||
values = data.json();
|
||||
if(values.ok){
|
||||
msg_sucess(values.msg)
|
||||
}else{
|
||||
msg_error(values.msg)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
function grid_invoices_click(id, e, node){
|
||||
var row = this.getItem(id)
|
||||
|
||||
|
@ -652,7 +687,7 @@ function grid_invoices_click(id, e, node){
|
|||
}else if(id.column == 'zip'){
|
||||
location = '/doc/zip/' + row.id
|
||||
}else if(id.column == 'email'){
|
||||
show('Correo')
|
||||
enviar_correo(row)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ var menu_data = [
|
|||
{id: 'app_home', icon: 'dashboard', value: 'Inicio'},
|
||||
{id: 'app_emisor', icon: 'user-circle', value: 'Emisor'},
|
||||
{id: 'app_folios', icon: 'sort-numeric-asc', value: 'Folios'},
|
||||
{id: 'app_correo', icon: 'envelope-o', value: 'Correo'},
|
||||
]
|
||||
|
||||
|
||||
|
@ -205,6 +206,55 @@ var emisor_folios = [
|
|||
]
|
||||
|
||||
|
||||
var emisor_correo = [
|
||||
{template: 'Servidor de Salida', type: 'section'},
|
||||
{cols: [
|
||||
{view: 'text', id: 'correo_servidor', name: 'correo_servidor',
|
||||
label: 'Servidor SMTP: '},
|
||||
{}]},
|
||||
{cols: [
|
||||
{view: 'counter', id: 'correo_puerto', name: 'correo_puerto',
|
||||
label: 'Puerto: ', value: 26, step: 1},
|
||||
{}]},
|
||||
{cols: [
|
||||
{view: 'checkbox', id: 'correo_ssl', name: 'correo_ssl',
|
||||
label: 'Usar TLS/SSL: '},
|
||||
{}]},
|
||||
{cols: [
|
||||
{view: 'text', id: 'correo_usuario', name: 'correo_usuario',
|
||||
label: 'Usuario: '},
|
||||
{}]},
|
||||
{cols: [
|
||||
{view: 'text', id: 'correo_contra', name: 'correo_contra',
|
||||
label: 'Contraseña: ', type: 'password'},
|
||||
{}]},
|
||||
{cols: [
|
||||
{view: 'text', id: 'correo_copia', name: 'correo_copia',
|
||||
label: 'Con copia a: '}
|
||||
]},
|
||||
{cols: [
|
||||
{view: 'text', id: 'correo_asunto', name: 'correo_asunto',
|
||||
label: 'Asunto del correo: '}
|
||||
]},
|
||||
{cols: [
|
||||
{view: 'textarea', id: 'correo_mensaje', name: 'correo_mensaje',
|
||||
label: 'Mensaje del correo: ', height: 200}
|
||||
]},
|
||||
{cols: [
|
||||
{view: 'checkbox', id: 'correo_directo', name: 'correo_directo',
|
||||
label: 'Enviar directamente: '},
|
||||
{}]},
|
||||
{minHeight: 25},
|
||||
{cols: [{},
|
||||
{view: 'button', id: 'cmd_probar_correo', label: 'Probar Configuración',
|
||||
autowidth: true, type: 'form'},
|
||||
{maxWidth: 100},
|
||||
{view: 'button', id: 'cmd_guardar_correo', label: 'Guardar Configuración',
|
||||
autowidth: true, type: 'form'},
|
||||
{}]}
|
||||
]
|
||||
|
||||
|
||||
var controls_folios = [
|
||||
{
|
||||
view: 'tabview',
|
||||
|
@ -219,6 +269,20 @@ var controls_folios = [
|
|||
]
|
||||
|
||||
|
||||
var controls_correo = [
|
||||
{
|
||||
view: 'tabview',
|
||||
id: 'tab_correo',
|
||||
tabbar: {options: ['Correo Electrónico']},
|
||||
animate: true,
|
||||
cells: [
|
||||
{id: 'Correo Electrónico', rows: emisor_correo},
|
||||
{},
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
var form_folios = {
|
||||
type: 'space',
|
||||
cols: [{
|
||||
|
@ -239,6 +303,22 @@ var form_folios = {
|
|||
}
|
||||
|
||||
|
||||
var form_correo = {
|
||||
type: 'space',
|
||||
cols: [{
|
||||
view: 'form',
|
||||
id: 'form_correo',
|
||||
complexData: true,
|
||||
elements: controls_correo,
|
||||
elementsConfig: {
|
||||
labelWidth: 150,
|
||||
labelAlign: 'right'
|
||||
},
|
||||
autoheight: true
|
||||
}]
|
||||
}
|
||||
|
||||
|
||||
var app_emisor = {
|
||||
id: 'app_emisor',
|
||||
rows:[
|
||||
|
@ -267,6 +347,17 @@ var app_folios = {
|
|||
}
|
||||
|
||||
|
||||
var app_correo = {
|
||||
id: 'app_correo',
|
||||
rows:[
|
||||
{view: 'template', id: 'th_correo', type: 'header',
|
||||
template: 'Configuración de correo'},
|
||||
form_correo,
|
||||
{},
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
var multi_admin = {
|
||||
id: 'multi_admin',
|
||||
animate: true,
|
||||
|
@ -278,6 +369,7 @@ var multi_admin = {
|
|||
},
|
||||
app_emisor,
|
||||
app_folios,
|
||||
app_correo,
|
||||
]
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue