Merge branch 'tickets' into develop

Generar tickets
This commit is contained in:
Mauricio Baeza 2017-12-25 23:51:28 -06:00
commit 28146ed47a
20 changed files with 1693 additions and 90 deletions

View File

@ -269,6 +269,22 @@ class AppPreInvoices(object):
resp.status = falcon.HTTP_204 resp.status = falcon.HTTP_204
class AppTickets(object):
def __init__(self, db):
self._db = db
def on_get(self, req, resp):
values = req.params
req.context['result'] = self._db.get_tickets(values)
resp.status = falcon.HTTP_200
def on_post(self, req, resp):
values = req.params
req.context['result'] = self._db.tickets(values)
resp.status = falcon.HTTP_200
class AppEmisor(object): class AppEmisor(object):
def __init__(self, db): def __init__(self, db):

View File

@ -235,6 +235,10 @@ def now():
return datetime.datetime.now().replace(microsecond=0) return datetime.datetime.now().replace(microsecond=0)
def today():
return datetime.date.today()
def get_token(): def get_token():
return _get_hash(uuid.uuid4().hex) return _get_hash(uuid.uuid4().hex)

View File

@ -16,7 +16,7 @@ from controllers.main import (AppEmpresas,
AppLogin, AppLogout, AppAdmin, AppEmisor, AppConfig, AppLogin, AppLogout, AppAdmin, AppEmisor, AppConfig,
AppMain, AppValues, AppPartners, AppProducts, AppInvoices, AppFolios, AppMain, AppValues, AppPartners, AppProducts, AppInvoices, AppFolios,
AppDocumentos, AppFiles, AppPreInvoices, AppCuentasBanco, AppDocumentos, AppFiles, AppPreInvoices, AppCuentasBanco,
AppMovimientosBanco AppMovimientosBanco, AppTickets
) )
@ -46,11 +46,12 @@ api.add_route('/partners', AppPartners(db))
api.add_route('/products', AppProducts(db)) api.add_route('/products', AppProducts(db))
api.add_route('/invoices', AppInvoices(db)) api.add_route('/invoices', AppInvoices(db))
api.add_route('/preinvoices', AppPreInvoices(db)) api.add_route('/preinvoices', AppPreInvoices(db))
api.add_route('/tickets', AppTickets(db))
api.add_route('/cuentasbanco', AppCuentasBanco(db)) api.add_route('/cuentasbanco', AppCuentasBanco(db))
api.add_route('/movbanco', AppMovimientosBanco(db)) api.add_route('/movbanco', AppMovimientosBanco(db))
# ~ Activa si usas waitress # ~ Activa si usas waitress y NO estas usando servidor web
# ~ api.add_sink(static, '/static') # ~ api.add_sink(static, '/static')

View File

@ -77,6 +77,9 @@ class StorageEngine(object):
years2 = main.PreFacturas.filter_years() years2 = main.PreFacturas.filter_years()
return [years1, years2] return [years1, years2]
def _get_filteryearsticket(self, values):
return main.Tickets.filter_years()
def _get_cuentayears(self, values): def _get_cuentayears(self, values):
return main.CuentasBanco.get_years() return main.CuentasBanco.get_years()
@ -221,6 +224,9 @@ class StorageEngine(object):
def _get_product(self, values): def _get_product(self, values):
return main.Productos.get_by(values) return main.Productos.get_by(values)
def _get_productokey(self, values):
return main.Productos.get_by_key(values)
def get_partners(self, values): def get_partners(self, values):
return main.Socios.get_(values) return main.Socios.get_(values)
@ -251,6 +257,18 @@ class StorageEngine(object):
#~ return main.PreFacturas.actualizar(values, id) #~ return main.PreFacturas.actualizar(values, id)
return main.PreFacturas.add(values) return main.PreFacturas.add(values)
def tickets(self, values):
opt = values.pop('opt')
if opt == 'add':
return main.Tickets.add(values)
if opt == 'cancel':
return main.Tickets.cancel(values)
if opt == 'invoice':
return main.Tickets.invoice(values)
def get_tickets(self, values):
return main.Tickets.get_by(values)
def get_invoices(self, values): def get_invoices(self, values):
return main.Facturas.get_(values) return main.Facturas.get_(values)

View File

@ -110,8 +110,11 @@ def config_main():
except IndexError: except IndexError:
obj = None obj = None
punto_de_venta = util.get_bool(Configuracion.get_('chk_usar_punto_de_venta'))
data = { data = {
'empresa': 'Empresa Libre', 'empresa': 'Empresa Libre',
'punto_de_venta': punto_de_venta
} }
if not obj is None: if not obj is None:
titulo = 'Empresa Libre - <b><font color="#610B0B">{}</font></b>' titulo = 'Empresa Libre - <b><font color="#610B0B">{}</font></b>'
@ -126,10 +129,15 @@ def config_timbrar():
except IndexError: except IndexError:
return {'cfdi_donativo': False} return {'cfdi_donativo': False}
mp = not util.get_bool(Configuracion.get_('chk_config_ocultar_metodo_pago'))
cp = not util.get_bool(
Configuracion.get_('chk_config_ocultar_condiciones_pago'))
conf = { conf = {
'cfdi_anticipo': Configuracion.get_('chk_config_anticipo'),
'cfdi_donativo': obj.es_ong, 'cfdi_donativo': obj.es_ong,
'cfdi_anticipo': Configuracion.get_('chk_config_anticipo'),
'cfdi_ine': Configuracion.get_('chk_config_ine'), 'cfdi_ine': Configuracion.get_('chk_config_ine'),
'cfdi_metodo_pago': mp,
'cfdi_condicion_pago': cp,
} }
return conf return conf
@ -153,6 +161,21 @@ class Configuracion(BaseModel):
return data[0].valor return data[0].valor
return '' return ''
if keys['fields'] == 'productos':
fields = (
'chk_config_cuenta_predial',
'chk_config_codigo_barras',
'chk_config_precio_con_impuestos',
)
data = (Configuracion
.select()
.where(Configuracion.clave.in_(fields))
)
values = {r.clave: r.valor for r in data}
values['default_tax'] = SATImpuestos.select()[0].id
values['default_unidad'] = SATUnidades.get_default()
return values
if keys['fields'] == 'correo': if keys['fields'] == 'correo':
fields = ('correo_servidor', 'correo_puerto', 'correo_ssl', fields = ('correo_servidor', 'correo_puerto', 'correo_ssl',
'correo_usuario', 'correo_contra', 'correo_copia', 'correo_usuario', 'correo_contra', 'correo_copia',
@ -180,17 +203,25 @@ class Configuracion(BaseModel):
) )
elif keys['fields'] == 'configotros': elif keys['fields'] == 'configotros':
fields = ( fields = (
'chk_config_ocultar_metodo_pago',
'chk_config_ocultar_condiciones_pago',
'chk_config_anticipo', 'chk_config_anticipo',
'chk_config_cuenta_predial', 'chk_config_cuenta_predial',
'chk_config_codigo_barras',
'chk_config_precio_con_impuestos',
'chk_config_ine', 'chk_config_ine',
'chk_usar_punto_de_venta',
) )
data = (Configuracion data = (Configuracion
.select() .select()
.where(Configuracion.clave.in_(fields)) .where(Configuracion.clave.in_(fields))
) )
elif keys['fields'] == 'productos': elif keys['fields'] == 'timbrar':
fields = ( fields = (
'chk_config_cuenta_predial', 'chk_config_ocultar_metodo_pago',
'chk_config_ocultar_condiciones_pago',
'chk_config_anticipo',
'chk_config_ine',
) )
data = (Configuracion data = (Configuracion
.select() .select()
@ -844,6 +875,13 @@ class SATUnidades(BaseModel):
return {'ok': result} return {'ok': result}
@classmethod
def get_default(cls):
obj = SATUnidades.select()[0]
if obj.default:
return obj.id
return 0
@classmethod @classmethod
def get_activos(cls): def get_activos(cls):
rows = (SATUnidades rows = (SATUnidades
@ -1752,7 +1790,7 @@ class Socios(BaseModel):
fields = cls._clean(cls, values) fields = cls._clean(cls, values)
try: try:
obj = Socios.create(**fields) obj = Socios.create(**fields)
except IntegrityError: except IntegrityError as e:
msg = 'Ya existe el RFC y Razón Social' msg = 'Ya existe el RFC y Razón Social'
data = {'ok': False, 'row': {}, 'new': True, 'msg': msg} data = {'ok': False, 'row': {}, 'new': True, 'msg': msg}
return data return data
@ -1973,6 +2011,34 @@ class Productos(BaseModel):
value += 1 value += 1
return {'value': value} return {'value': value}
@classmethod
def get_by_key(cls, values):
clave = values.get('key', '')
row = (Productos
.select(
Productos.id,
Productos.clave,
Productos.clave_sat,
Productos.descripcion,
SATUnidades.name.alias('unidad'),
Productos.valor_unitario,
Productos.descuento)
.join(SATUnidades).switch(Productos)
.where((Productos.clave==clave) | (Productos.codigo_barras==clave))
.dicts()
)
if len(row):
id = row[0]['id']
model_pt = Productos.impuestos.get_through_model()
taxes = tuple(model_pt
.select(
model_pt.productos_id.alias('product'),
model_pt.satimpuestos_id.alias('tax'))
.where(model_pt.productos_id==id).dicts())
return {'ok': True, 'row': row[0], 'taxes': taxes}
return {'ok': False}
@classmethod @classmethod
def get_by(cls, values): def get_by(cls, values):
clave = values.get('id', '') clave = values.get('id', '')
@ -2723,17 +2789,9 @@ class Facturas(BaseModel):
totals_tax[tax.id] = tax totals_tax[tax.id] = tax
for tax in totals_tax.values(): for tax in totals_tax.values():
# ~ if tax.tipo == 'E' or tax.tipo == 'R':
if tax.tipo == 'E': if tax.tipo == 'E':
continue continue
# ~ import_tax = round(float(tax.tasa) * tax.importe, DECIMALES)
# ~ if tax.key == '000':
# ~ locales_traslados += import_tax
# ~ else:
# ~ total_trasladados = (total_trasladados or 0) + import_tax
# ~ if tax.name == 'IVA':
# ~ total_iva += import_tax
invoice_tax = { invoice_tax = {
'factura': invoice.id, 'factura': invoice.id,
@ -2743,26 +2801,6 @@ class Facturas(BaseModel):
} }
FacturasImpuestos.create(**invoice_tax) FacturasImpuestos.create(**invoice_tax)
# ~ for tax in totals_tax.values():
# ~ if tax.tipo == 'E' or tax.tipo == 'T':
# ~ continue
# ~ if tax.tasa == round(Decimal(2/3), 6):
# ~ import_tax = round(float(tax.tasa) * total_iva, DECIMALES)
# ~ else:
# ~ import_tax = round(float(tax.tasa) * tax.importe, DECIMALES)
# ~ if tax.key == '000':
# ~ locales_retenciones += import_tax
# ~ else:
# ~ total_retenciones = (total_retenciones or 0) + import_tax
# ~ invoice_tax = {
# ~ 'factura': invoice.id,
# ~ 'impuesto': tax.id,
# ~ 'base': tax.base,
# ~ 'importe': tax.suma_impuestos,
# ~ }
# ~ FacturasImpuestos.create(**invoice_tax)
total = subtotal - descuento_cfdi + \ total = subtotal - descuento_cfdi + \
(total_trasladados or 0) - (total_retenciones or 0) \ (total_trasladados or 0) - (total_retenciones or 0) \
+ locales_traslados - locales_retenciones + locales_traslados - locales_retenciones
@ -3834,6 +3872,339 @@ class Tickets(BaseModel):
class Meta: class Meta:
order_by = ('fecha',) order_by = ('fecha',)
def _get_folio(self, serie):
inicio = (Tickets
.select(fn.Max(Tickets.folio).alias('mf'))
.where(Tickets.serie==serie)
.order_by(SQL('mf'))
.scalar())
if inicio is None:
inicio = 1
else:
inicio += 1
return inicio
def _calcular_totales(self, ticket, productos):
subtotal = 0
descuento_cfdi = 0
totals_tax = {}
total_trasladados = None
for producto in productos:
id_producto = producto.pop('id')
p = Productos.get(Productos.id==id_producto)
producto['descripcion'] = p.descripcion
producto['ticket'] = ticket.id
producto['producto'] = id_producto
cantidad = float(producto['cantidad'])
valor_unitario = float(p.valor_unitario)
descuento = float(producto['descuento'])
precio_final = valor_unitario - descuento
importe = round(cantidad * precio_final, DECIMALES)
producto['cantidad'] = cantidad
producto['valor_unitario'] = valor_unitario
producto['descuento'] = descuento
producto['precio_final'] = precio_final
producto['importe'] = importe
descuento_cfdi += descuento
subtotal += importe
TicketsDetalle.create(**producto)
base = producto['importe']
for tax in p.impuestos:
impuesto_producto = round(float(tax.tasa) * base, DECIMALES)
if tax.tipo == 'T' and tax.key != '000':
total_trasladados = (total_trasladados or 0) + impuesto_producto
if tax.id in totals_tax:
totals_tax[tax.id].base += base
totals_tax[tax.id].importe += impuesto_producto
else:
tax.base = base
tax.importe = impuesto_producto
totals_tax[tax.id] = tax
for tax in totals_tax.values():
if tax.tipo == 'E':
continue
ticket_tax = {
'ticket': ticket.id,
'impuesto': tax.id,
'base': tax.base,
'importe': tax.importe,
}
TicketsImpuestos.create(**ticket_tax)
total = subtotal + (total_trasladados or 0)
data = {
'subtotal': subtotal + descuento,
'descuento': descuento_cfdi,
'total': total,
'total_trasladados': total_trasladados,
}
return data
@classmethod
def add(cls, values):
productos = util.loads(values.pop('productos'))
values['serie'] = 'T'
values['folio'] = cls._get_folio(cls, values['serie'])
with database_proxy.atomic() as txn:
obj = Tickets.create(**values)
totals = cls._calcular_totales(cls, obj, productos)
obj.subtotal = totals['subtotal']
obj.descuento = totals['descuento']
obj.total_trasladados = totals['total_trasladados']
obj.total = totals['total']
obj.save()
row = {
'id': obj.id,
'folio': obj.folio,
'fecha': obj.fecha,
'estatus': obj.estatus,
'total': obj.total,
}
data = {'ok': True, 'row': row}
return data
def _get_folio_invoice(self, serie):
inicio = (Facturas
.select(fn.Max(Facturas.folio).alias('mf'))
.where(Facturas.serie==serie)
.order_by(SQL('mf'))
.scalar())
if inicio is None:
inicio = 1
else:
inicio += 1
return inicio
def _cancel_tickets(self, invoice, tickets):
query = (Tickets
.update(estatus='Facturado', cancelado=True, factura=invoice)
.where(Tickets.id.in_(tickets))
)
result = query.execute()
print (result)
return
def _calculate_totals_invoice(self, invoice, tickets):
subtotal = 0
descuento_cfdi = 0
totals_tax = {}
total_trasladados = None
total_retenciones = None
details = TicketsDetalle.select().where(TicketsDetalle.ticket.in_(tickets))
for detail in details:
product = {}
p = detail.producto
product['unidad'] = p.unidad.key
product['clave'] = p.clave
product['clave_sat'] = p.clave_sat
product['factura'] = invoice.id
product['producto'] = p.id
product['descripcion'] = detail.descripcion
cantidad = float(detail.cantidad)
valor_unitario = float(detail.valor_unitario)
descuento = float(detail.descuento)
precio_final = valor_unitario - descuento
importe = round(cantidad * precio_final, DECIMALES)
product['cantidad'] = cantidad
product['valor_unitario'] = valor_unitario
product['descuento'] = round(descuento * cantidad, DECIMALES)
product['precio_final'] = precio_final
product['importe'] = round(cantidad * valor_unitario, DECIMALES)
descuento_cfdi += product['descuento']
subtotal += product['importe']
FacturasDetalle.create(**product)
base = product['importe'] - product['descuento']
for tax in p.impuestos:
impuesto_producto = round(float(tax.tasa) * base, DECIMALES)
if tax.tipo == 'T' and tax.key != '000':
total_trasladados = (total_trasladados or 0) + impuesto_producto
elif tax.tipo == 'R' and tax.key != '000':
total_retenciones = (total_retenciones or 0) + impuesto_producto
elif tax.tipo == 'T' and tax.key == '000':
locales_traslados += impuesto_producto
elif tax.tipo == 'R' and tax.key == '000':
locales_retenciones += impuesto_producto
if tax.id in totals_tax:
totals_tax[tax.id].base += base
totals_tax[tax.id].suma_impuestos += impuesto_producto
else:
tax.base = base
tax.suma_impuestos = impuesto_producto
totals_tax[tax.id] = tax
for tax in totals_tax.values():
if tax.tipo == 'E':
continue
invoice_tax = {
'factura': invoice.id,
'impuesto': tax.id,
'base': tax.base,
'importe': tax.suma_impuestos,
}
FacturasImpuestos.create(**invoice_tax)
total = subtotal - descuento_cfdi + \
(total_trasladados or 0) - (total_retenciones or 0)
total_mn = round(total * invoice.tipo_cambio, DECIMALES)
data = {
'subtotal': subtotal,
'descuento': descuento_cfdi,
'total': total,
'total_mn': total_mn,
'total_trasladados': total_trasladados,
'total_retenciones': total_retenciones,
}
return data
@classmethod
def invoice(cls, values):
is_invoice_day = util.get_bool(values['is_invoice_day'])
id_client = int(values['client'])
tickets = util.loads(values['tickets'])
if is_invoice_day:
filters = (
Socios.rfc == 'XAXX010101000' and
Socios.slug == 'publico_en_general')
try:
client = Socios.get(filters)
except Socios.DoesNotExist:
msg = 'No existe el cliente Público en General. Agregalo primero.'
data = {'ok': False, 'msg': msg}
return data
else:
client = Socios.get(Socios.id==id_client)
emisor = Emisor.select()[0]
data = {}
data['cliente'] = client
data['serie'] = 'T'
data['folio'] = cls._get_folio_invoice(cls, data['serie'])
data['forma_pago'] = client.forma_pago.key
data['tipo_cambio'] = 1.00
data['lugar_expedicion'] = emisor.cp_expedicion or emisor.codigo_postal
if client.uso_cfdi is None:
data['uso_cfdi'] = 'P01'
else:
data['uso_cfdi'] = client.uso_cfdi.key
data['regimen_fiscal'] = emisor.regimenes[0].key
with database_proxy.atomic() as txn:
obj = Facturas.create(**data)
totals = cls._calculate_totals_invoice(cls, obj, tickets)
obj.subtotal = totals['subtotal']
obj.descuento = totals['descuento']
obj.total_trasladados = totals['total_trasladados']
obj.total_retenciones = totals['total_retenciones']
obj.total = totals['total']
obj.saldo = totals['total']
obj.total_mn = totals['total_mn']
obj.save()
cls._cancel_tickets(cls, obj, tickets)
msg = 'Factura generada correctamente.<BR><BR>Enviando a timbrar'
data = {'ok': True, 'msg': msg, 'id': obj.id}
return data
@classmethod
def cancel(cls, values):
id = int(values['id'])
msg = 'Ticket cancelado correctamente'
u = {'cancelado': True, 'estatus': 'Cancelado'}
obj = Tickets.update(**u).where(Tickets.id==id)
result = bool(obj.execute())
row = {'estatus': 'Cancelado'}
return {'ok': result, 'row': row, 'msg': msg}
def _get_filters(self, values):
opt = values.get('opt', '')
if not opt:
return
if opt == 'active':
filters = (Tickets.cancelado==False)
return filters
if opt == 'today':
t = util.today()
filters = (
(Tickets.fecha.day == t.day) &
(Tickets.fecha.month == t.month) &
(Tickets.fecha.year == t.year)
)
return filters
if opt == 'yearmonth':
if values['year'] == '-1':
fy = (Tickets.fecha.year > 0)
else:
fy = (Tickets.fecha.year == int(values['year']))
if values['month'] == '-1':
fm = (Tickets.fecha.month > 0)
else:
fm = (Tickets.fecha.month == int(values['month']))
filters = (fy & fm)
return filters
return
@classmethod
def get_by(cls, values):
filters = cls._get_filters(cls, values)
rows = tuple(Tickets
.select(
Tickets.id,
Tickets.serie,
Tickets.folio,
Tickets.fecha,
Tickets.estatus,
Tickets.total)
.where(filters)
.dicts()
)
return {'ok': True, 'rows': rows}
@classmethod
def filter_years(cls):
data = [{'id': -1, 'value': 'Todos'}]
rows = (Tickets
.select(Tickets.fecha.year.alias('year'))
.group_by(Tickets.fecha.year)
.order_by(Tickets.fecha.year)
)
if not rows is None:
data += [{'id': int(r.year), 'value': int(r.year)} for r in rows]
return tuple(data)
class TicketsDetalle(BaseModel): class TicketsDetalle(BaseModel):
ticket = ForeignKeyField(Tickets) ticket = ForeignKeyField(Tickets)

View File

@ -24,6 +24,23 @@
font-size: 125%; font-size: 125%;
color: DarkRed; color: DarkRed;
} }
.right_footer2 {
text-align: right;
font-weight: bold;
font-size: 150%;
color: DarkRed;
}
.right_footer3 {
text-align: right;
font-weight: bold;
font-size: 120%;
color: DarkBlue;
}
.footer3 {
font-weight: bold;
font-size: 120%;
color: DarkBlue;
}
.lbl_partner { .lbl_partner {
font-weight: bold; font-weight: bold;
@ -37,6 +54,10 @@
color: red; color: red;
} }
.cancel {
color: red;
}
.cmd_close_partner div button { .cmd_close_partner div button {
background-color: red !important; background-color: red !important;

View File

@ -50,9 +50,14 @@ var controllers = {
$$('txt_plantilla_factura_33').attachEvent('onItemClick', txt_plantilla_factura_33_click) $$('txt_plantilla_factura_33').attachEvent('onItemClick', txt_plantilla_factura_33_click)
$$('txt_plantilla_factura_33j').attachEvent('onItemClick', txt_plantilla_factura_33j_click) $$('txt_plantilla_factura_33j').attachEvent('onItemClick', txt_plantilla_factura_33j_click)
$$('txt_plantilla_donataria').attachEvent('onItemClick', txt_plantilla_donataria_click) $$('txt_plantilla_donataria').attachEvent('onItemClick', txt_plantilla_donataria_click)
$$('chk_config_ocultar_metodo_pago').attachEvent('onItemClick', chk_config_item_click)
$$('chk_config_ocultar_condiciones_pago').attachEvent('onItemClick', chk_config_item_click)
$$('chk_config_anticipo').attachEvent('onItemClick', chk_config_item_click) $$('chk_config_anticipo').attachEvent('onItemClick', chk_config_item_click)
$$('chk_config_ine').attachEvent('onItemClick', chk_config_item_click) $$('chk_config_ine').attachEvent('onItemClick', chk_config_item_click)
$$('chk_config_cuenta_predial').attachEvent('onItemClick', chk_config_item_click) $$('chk_config_cuenta_predial').attachEvent('onItemClick', chk_config_item_click)
$$('chk_config_codigo_barras').attachEvent('onItemClick', chk_config_item_click)
$$('chk_config_precio_con_impuestos').attachEvent('onItemClick', chk_config_item_click)
$$('chk_usar_punto_de_venta').attachEvent('onItemClick', chk_config_item_click)
$$('cmd_subir_bdfl').attachEvent('onItemClick', cmd_subir_bdfl_click) $$('cmd_subir_bdfl').attachEvent('onItemClick', cmd_subir_bdfl_click)
$$('up_bdfl').attachEvent('onUploadComplete', up_bdfl_upload_complete) $$('up_bdfl').attachEvent('onUploadComplete', up_bdfl_upload_complete)

View File

@ -41,14 +41,6 @@ function get_series(){
} }
function get_forma_pago(){
webix.ajax().get('/values/formapago', {key: true}, function(text, data){
var values = data.json()
$$('lst_forma_pago').getList().parse(values)
})
}
function get_monedas(){ function get_monedas(){
webix.ajax().get('/values/monedas', function(text, data){ webix.ajax().get('/values/monedas', function(text, data){
var values = data.json() var values = data.json()
@ -105,7 +97,7 @@ function default_config(){
table_taxes.insert(values) table_taxes.insert(values)
}) })
get_series() get_series()
get_forma_pago() get_forma_pago('lst_forma_pago')
get_monedas() get_monedas()
get_uso_cfdi() get_uso_cfdi()
get_regimen_fiscal() get_regimen_fiscal()
@ -116,8 +108,11 @@ function default_config(){
webix.ajax().sync().get('/values/configtimbrar', function(text, data){ webix.ajax().sync().get('/values/configtimbrar', function(text, data){
var values = data.json() var values = data.json()
//~ showvar(values)
show('chk_cfdi_anticipo', values.cfdi_anticipo) show('chk_cfdi_anticipo', values.cfdi_anticipo)
show('chk_cfdi_donativo', values.cfdi_donativo) show('chk_cfdi_donativo', values.cfdi_donativo)
show('lst_metodo_pago', values.cfdi_metodo_pago)
show('txt_condicion_pago', values.cfdi_condicion_pago)
if(!values.cfdi_ine || values.cfdi_ine == '0'){ if(!values.cfdi_ine || values.cfdi_ine == '0'){
$$('tv_invoice').getTabbar().hideOption('INE') $$('tv_invoice').getTabbar().hideOption('INE')
}else{ }else{

View File

@ -9,7 +9,16 @@ function configuracion_inicial(){
webix.ajax().get('/values/main', function(text, data){ webix.ajax().get('/values/main', function(text, data){
var values = data.json() var values = data.json()
$$('lbl_title_main').setValue(values.empresa) $$('lbl_title_main').setValue(values.empresa)
//~ showvar()
if(values.punto_de_venta){
var node = {
id: 'app_tickets',
icon: 'money',
value: 'Punto de venta'}
$$('main_sidebar').add(node, 4)
}
}) })
} }
@ -40,15 +49,7 @@ var controllers = {
$$("es_proveedor").attachEvent( "onChange", is_supplier_change) $$("es_proveedor").attachEvent( "onChange", is_supplier_change)
$$("rfc").attachEvent( "onBlur", rfc_lost_focus) $$("rfc").attachEvent( "onBlur", rfc_lost_focus)
$$('multi').attachEvent('onViewChange', multi_change) $$('multi').attachEvent('onViewChange', multi_change)
//~ Products
$$("cmd_new_product").attachEvent("onItemClick", cmd_new_product_click)
$$("cmd_edit_product").attachEvent("onItemClick", cmd_edit_product_click)
$$("cmd_delete_product").attachEvent("onItemClick", cmd_delete_product_click)
$$("cmd_save_product").attachEvent("onItemClick", cmd_save_product_click)
$$("cmd_cancel_product").attachEvent("onItemClick", cmd_cancel_product_click)
$$("chk_automatica").attachEvent("onChange", chk_automatica_change)
$$("valor_unitario").attachEvent("onChange", valor_unitario_change)
$$("clave_sat").attachEvent('onSearchIconClick', clave_sat_icon_click)
//~ Invoices //~ Invoices
$$('cmd_new_invoice').attachEvent("onItemClick", cmd_new_invoice_click) $$('cmd_new_invoice').attachEvent("onItemClick", cmd_new_invoice_click)
$$('cmd_refacturar').attachEvent("onItemClick", cmd_refacturar_click) $$('cmd_refacturar').attachEvent("onItemClick", cmd_refacturar_click)
@ -86,7 +87,9 @@ var controllers = {
webix.extend($$('grid_invoices'), webix.ProgressBar) webix.extend($$('grid_invoices'), webix.ProgressBar)
products_controllers.init()
bancos_controllers.init() bancos_controllers.init()
tickets_controllers.init()
} }
} }
@ -195,6 +198,14 @@ function multi_change(prevID, nextID){
return return
} }
if(nextID == 'app_tickets'){
active = $$('multi_tickets').getActiveId()
if(active == 'tickets_home'){
configuracion_inicial_ticket()
}
return
}
if(nextID == 'app_invoices'){ if(nextID == 'app_invoices'){
active = $$('multi_invoices').getActiveId() active = $$('multi_invoices').getActiveId()
if(active == 'invoices_home'){ if(active == 'invoices_home'){

View File

@ -155,7 +155,7 @@ function cmd_save_partner_click(id, e, node){
if (values.ok) { if (values.ok) {
update_grid_partner(values) update_grid_partner(values)
} else { } else {
msg_error(msg) msg_error(values.msg)
} }
} }
}) })

View File

@ -1,5 +1,21 @@
var products_controllers = {
init: function(){
$$('cmd_new_product').attachEvent('onItemClick', cmd_new_product_click)
$$("cmd_edit_product").attachEvent("onItemClick", cmd_edit_product_click)
$$("cmd_delete_product").attachEvent("onItemClick", cmd_delete_product_click)
$$("cmd_save_product").attachEvent("onItemClick", cmd_save_product_click)
$$("cmd_cancel_product").attachEvent("onItemClick", cmd_cancel_product_click)
$$("chk_automatica").attachEvent("onChange", chk_automatica_change)
$$("valor_unitario").attachEvent("onChange", valor_unitario_change)
//~ $$("clave_sat").attachEvent('onSearchIconClick', clave_sat_icon_click)
$$('precio_con_impuestos').attachEvent('onChange', precio_con_impuestos_change)
$$('precio_con_impuestos').attachEvent('onTimedKeyPress', precio_con_impuestos_key_up);
}
}
function configurar_productos(){ function configurar_productos(){
webix.ajax().get('/config', {'fields': 'productos'}, { webix.ajax().get('/config', {'fields': 'productos'}, {
error: function(text, data, xhr) { error: function(text, data, xhr) {
@ -10,6 +26,10 @@ function configurar_productos(){
var values = data.json() var values = data.json()
//~ showvar(values) //~ showvar(values)
show('cuenta_predial', values.chk_config_cuenta_predial) show('cuenta_predial', values.chk_config_cuenta_predial)
show('codigo_barras', values.chk_config_codigo_barras)
show('precio_con_impuestos', values.chk_config_precio_con_impuestos)
$$('unidad').setValue(values.default_unidad)
$$('grid_product_taxes').select(values.default_tax)
} }
}) })
} }
@ -228,6 +248,49 @@ function valor_unitario_change(new_value, old_value){
} }
function clave_sat_icon_click(){ function precio_con_impuestos_change(new_value, old_value){
show('Buscar SAT') if(!isFinite(new_value)){
this.config.value = old_value
this.refresh()
}
}
//~ function clave_sat_icon_click(){
//~ show('Buscar SAT')
//~ }
function calcular_sin_impuestos(value, taxes){
var vu = $$('valor_unitario')
var precio = value
taxes.forEach(function(tax){
var tasa = 1.00 + tax.tasa.to_float()
if(tax.tipo == 'T' && tax.name == 'IVA'){
precio = (value / tasa).round(DECIMALES)
}
})
vu.setValue(precio)
}
function precio_con_impuestos_key_up(){
var value = this.getValue()
if(!value){
return
}
var taxes = $$('grid_product_taxes').getSelectedItem(true)
if (taxes.length == 0){
msg = 'Selecciona al menos un impuesto'
msg_error(msg)
return
}
if(!isFinite(value)){
msg = 'Captura un valor válido'
msg_error(msg)
return
}
calcular_sin_impuestos(parseFloat(value), taxes)
} }

View File

@ -0,0 +1,617 @@
var query = []
var msg = ''
var tickets_controllers = {
init: function(){
$$('cmd_nuevo_ticket').attachEvent('onItemClick', cmd_nuevo_ticket_click)
$$('cmd_ticket_to_invoice').attachEvent('onItemClick', cmd_ticket_to_invoice_click)
$$('cmd_generar_ticket').attachEvent('onItemClick', cmd_generar_ticket_click)
$$('cmd_cerrar_ticket').attachEvent('onItemClick', cmd_cerrar_ticket_click)
$$('cmd_new_invoice_from_ticket').attachEvent('onItemClick', cmd_new_invoice_from_ticket_click)
$$('cmd_close_ticket_invoice').attachEvent('onItemClick', cmd_cerrar_ticket_click)
$$('cmd_cancelar_ticket').attachEvent('onItemClick', cmd_cancelar_ticket_click)
$$('cmd_move_tickets_right').attachEvent('onItemClick', cmd_move_tickets_right_click)
$$('cmd_move_tickets_left').attachEvent('onItemClick', cmd_move_tickets_left_click)
$$('tsearch_product_key').attachEvent('onKeyPress', tsearch_product_key_press)
$$('grid_tdetails').attachEvent('onItemClick', grid_ticket_details_click)
$$('grid_tdetails').attachEvent('onBeforeEditStop', grid_tickets_details_before_edit_stop)
$$('gt_productos_found').attachEvent('onValueSuggest', gt_productos_found_click)
$$('filter_year_ticket').attachEvent('onChange', filter_year_ticket_change)
$$('filter_month_ticket').attachEvent('onChange', filter_month_ticket_change)
$$('chk_is_invoice_day').attachEvent('onChange', chk_is_invoice_day_change)
$$('grid_tickets_active').attachEvent('onItemDblClick', grid_tickets_active_double_click)
$$('grid_tickets_invoice').attachEvent('onItemDblClick', grid_tickets_invoice_double_click)
$$('tsearch_client_key').attachEvent('onKeyPress', tsearch_client_key_press)
$$('grid_ticket_clients_found').attachEvent('onValueSuggest', grid_ticket_clients_found_click)
webix.extend($$('grid_tickets'), webix.ProgressBar)
webix.extend($$('grid_tickets_active'), webix.ProgressBar)
}
}
function current_dates_tickets(){
var fy = $$('filter_year_ticket')
var fm = $$('filter_month_ticket')
var d = new Date()
fy.blockEvent()
fm.blockEvent()
fm.setValue(d.getMonth() + 1)
webix.ajax().sync().get('/values/filteryearsticket', function(text, data){
var values = data.json()
fy.getList().parse(values)
fy.setValue(d.getFullYear())
})
fy.unblockEvent()
fm.unblockEvent()
}
function get_tickets(filters){
if(filters == undefined){
filters = {'opt': 'today'}
}
var grid = $$('grid_tickets')
grid.showProgress({type: 'icon'})
webix.ajax().get('/tickets', filters, {
error: function(text, data, xhr) {
msg_error('Error al consultar')
},
success: function(text, data, xhr) {
var values = data.json();
grid.clearAll();
if (values.ok){
grid.parse(values.rows, 'json')
}
}
})
}
function filter_year_ticket_change(nv, ov){
var fm = $$('filter_month_ticket')
filters = {'opt': 'yearmonth','year': nv, 'month': fm.getValue()}
get_tickets(filters)
}
function filter_month_ticket_change(nv, ov){
var fy = $$('filter_year_ticket')
filters = {'opt': 'yearmonth','year': fy.getValue(), 'month': nv}
get_tickets(filters)
}
function configuracion_inicial_ticket(){
current_dates_tickets()
get_tickets()
}
function get_active_tickets(grid){
filters = {'opt': 'active'}
grid.showProgress({type: 'icon'})
webix.ajax().get('/tickets', filters, {
error: function(text, data, xhr) {
msg_error('Error al consultar')
},
success: function(text, data, xhr) {
var values = data.json();
grid.clearAll();
if (values.ok){
grid.parse(values.rows, 'json')
}
}
})
}
function configuracion_inicial_ticket_to_invoice(){
var grid = $$('grid_tickets_active')
var gridt = $$('grid_tickets_invoice')
var form = $$('form_ticket_invoice')
get_active_tickets(grid)
form.setValues({id_partner: 0, lbl_tclient: 'Ninguno'})
gridt.attachEvent('onAfterAdd', function(id, index){
gridt.adjustColumn('index')
gridt.adjustColumn('folio', 'all')
gridt.adjustColumn('fecha', 'all')
});
gridt.clearAll()
}
function configuracion_inicial_nuevo_ticket(){
var grid = $$('grid_tdetails')
webix.ajax().sync().get('/values/taxes', function(text, data){
var values = data.json()
table_taxes.clear()
table_taxes.insert(values)
})
get_forma_pago('lst_ticket_forma_pago')
grid.clearAll()
table_pt.clear()
table_totals.clear()
}
function cmd_nuevo_ticket_click(){
configuracion_inicial_nuevo_ticket()
$$('multi_tickets').setValue('tickets_new')
}
function cmd_ticket_to_invoice_click(){
configuracion_inicial_ticket_to_invoice()
$$('multi_tickets').setValue('tickets_invoice')
}
function validar_ticket(){
var grid = $$('grid_tdetails')
if(!grid.count()){
webix.UIManager.setFocus('tsearch_product_key')
msg = 'Agrega al menos un producto'
msg_error(msg)
return false
}
return true
}
function guardar_ticket(values){
var gd = $$('grid_tdetails')
var grid = $$('grid_tickets')
var rows = gd.data.getRange()
for (i = 0; i < rows.length; i++) {
delete rows[i]['delete']
delete rows[i]['clave']
delete rows[i]['clave_sat']
delete rows[i]['unidad']
delete rows[i]['importe']
rows[i]['valor_unitario'] = parseFloat(rows[i]['valor_unitario'])
rows[i]['descuento'] = parseFloat(rows[i]['descuento'])
}
var data = new Object()
data['opt'] = 'add'
data['productos'] = rows
data['forma_pago'] = values.forma_pago
webix.ajax().sync().post('tickets', data, {
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_ok('Ticket generado correctamente')
$$('form_new_ticket').setValues({})
gd.clearAll()
grid.add(values.row)
$$('multi_tickets').setValue('tickets_home')
}else{
msg_error(values.msg)
}
}
})
}
function cmd_generar_ticket_click(){
var form = this.getFormView();
if(!form.validate()) {
msg_error('Valores inválidos')
return
}
var values = form.getValues()
if(!validar_ticket()){
return
}
msg = '¿Todos los datos son correctos?<BR><BR>'
msg += '¿Estás seguro de generar este Ticket?'
webix.confirm({
title: 'Generar Ticket',
ok: 'Si',
cancel: 'No',
type: 'confirm-error',
text: msg,
callback:function(result){
if(result){
guardar_ticket(values)
}
}
})
}
function cmd_cerrar_ticket_click(){
$$('multi_tickets').setValue('tickets_home')
}
function calcular_precio_con_impuestos(precio, taxes){
var precio_final = precio
for(var tax of taxes){
impuesto = table_taxes.findOne({'id': tax.tax})
if(impuesto.tipo == 'E'){
continue
}
var base = precio
if(impuesto.tipo == 'R'){
base = (precio * -1).round(DECIMALES)
}
precio_final += (impuesto.tasa * base).round(DECIMALES)
}
return precio_final
}
function agregar_producto(values){
var taxes = values.taxes
var producto = values.row
var form = $$('form_new_ticket')
var grid = $$('grid_tdetails')
var row = grid.getItem(producto.id)
var precio_final = 0.0
if(row == undefined){
producto['cantidad'] = 1
producto['valor_unitario'] = calcular_precio_con_impuestos(
parseFloat(producto['valor_unitario']), taxes)
producto['importe'] = producto['valor_unitario']
grid.add(producto)
}else{
producto['cantidad'] = parseFloat(row.cantidad) + 1
producto['descuento'] = parseFloat(row.descuento)
producto['valor_unitario'] = parseFloat(row.valor_unitario)
precio_final = producto['valor_unitario'] - producto['descuento']
producto['importe'] = (precio_final * producto['cantidad']).round(DECIMALES)
grid.updateItem(row.id, producto)
}
form.setValues({tsearch_product_key: '', tsearch_product_name: ''}, true)
}
function buscar_producto_key(key){
webix.ajax().get('/values/productokey', {'key': key}, {
error: function(text, data, xhr) {
msg_error('Error al consultar')
},
success: function(text, data, xhr){
var values = data.json()
if (values.ok){
agregar_producto(values)
} else {
msg = 'No se encontró la clave<BR><BR>' + key
msg_error(msg)
}
}
})
}
function tsearch_product_key_press(code, e){
var value = this.getValue()
if(code == 13 && value.trim().length > 0){
buscar_producto_key(value.trim())
}
}
function grid_ticket_details_click(id, e, node){
if(id.column != 'delete'){
return
}
var grid = $$('grid_tdetails')
grid.remove(id.row)
}
function grid_tickets_details_before_edit_stop(state, editor){
var grid = $$('grid_tdetails')
var row = grid.getItem(editor.row)
if(editor.column == 'cantidad'){
var cantidad = parseFloat(state.value)
if(isNaN(cantidad)){
msg = 'La cantidad debe ser un número'
msg_error(msg)
grid.blockEvent()
state.value = state.old
grid.editCancel()
grid.unblockEvent()
return true
}
var valor_unitario = parseFloat(row['valor_unitario'])
var descuento = parseFloat(row['descuento'])
}
if(editor.column == 'valor_unitario'){
var valor_unitario = parseFloat(state.value)
if(isNaN(valor_unitario)){
msg = 'El valor unitario debe ser un número'
msg_error(msg)
grid.blockEvent()
state.value = state.old
grid.editCancel()
grid.unblockEvent()
return true
}
var cantidad = parseFloat(row['cantidad'])
var descuento = parseFloat(row['descuento'])
}
if(editor.column == 'descuento'){
var descuento = parseFloat(state.value)
if(isNaN(descuento)){
msg = 'El descuento debe ser un número'
msg_error(msg)
grid.blockEvent()
state.value = state.old
grid.editCancel()
grid.unblockEvent()
return true
}
var cantidad = parseFloat(row['cantidad'])
var valor_unitario = parseFloat(row['valor_unitario'])
}
var precio_final = valor_unitario - descuento
row['importe'] = (cantidad * precio_final).round(DECIMALES)
grid.refresh()
}
function gt_productos_found_click(obj){
buscar_producto_key(obj.clave)
}
function cancel_ticket(id){
var grid = $$('grid_tickets')
var data = new Object()
data['opt'] = 'cancel'
data['id'] = id
webix.ajax().sync().post('tickets', data, {
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){
grid.updateItem(id, values.row)
msg_ok(values.msg)
}else{
msg_error(values.msg)
}
}
})
}
function cmd_cancelar_ticket_click(){
var grid = $$('grid_tickets')
if(grid.count() == 0){
return
}
var row = grid.getSelectedItem()
if (row == undefined){
msg_error('Selecciona un ticket')
return
}
msg = '¿Estás seguro de cancelar el siguiente Ticket?<BR><BR>'
msg += 'Folio: ' + row['folio']
msg += '<BR><BR>ESTA ACCIÓN NO SE PUEDE DESHACER'
webix.confirm({
title:'Cancelar Ticket',
ok:'Si',
cancel:'No',
type:'confirm-error',
text:msg,
callback:function(result){
if (result){
cancel_ticket(row['id'])
}
}
})
}
function chk_is_invoice_day_change(new_value, old_value){
var value = Boolean(new_value)
show('fs_ticket_search_client', !value)
}
function send_timbrar_invoice(id){
webix.ajax().get('/values/timbrar', {id: id}, function(text, data){
var values = data.json()
if(values.ok){
msg_ok(values.msg)
}else{
webix.alert({
title: 'Error al Timbrar',
text: values.msg,
type: 'alert-error'
})
}
})
}
function save_ticket_to_invoice(data){
webix.ajax().sync().post('tickets', data, {
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_ok(values.msg)
send_timbrar_invoice(values.id)
$$('multi_tickets').setValue('tickets_home')
}else{
msg_error(values.msg)
}
}
})
}
function cmd_new_invoice_from_ticket_click(){
var form = this.getFormView();
var chk = $$('chk_is_invoice_day')
var grid = $$('grid_tickets_invoice')
var values = form.getValues()
var tickets = []
if(!chk.getValue()){
if(values.id_partner == 0){
webix.UIManager.setFocus('tsearch_client_name')
msg = 'Selecciona un cliente'
msg_error(msg)
return false
}
}
if(!grid.count()){
msg = 'Agrega al menos un ticket a facturar'
msg_error(msg)
return false
}
grid.eachRow(function(row){
tickets.push(row)
})
var data = new Object()
data['client'] = values.id_partner
data['tickets'] = tickets
data['is_invoice_day'] = chk.getValue()
data['opt'] = 'invoice'
msg = 'Todos los datos son correctos.<BR><BR>¿Estás seguro de generar esta factura?'
webix.confirm({
title: 'Generar Factura',
ok: 'Si',
cancel: 'No',
type: 'confirm-error',
text: msg,
callback:function(result){
if(result){
save_ticket_to_invoice(data)
}
}
})
}
function grid_tickets_active_double_click(id, e, node){
this.move(id.row, -1, $$('grid_tickets_invoice'))
}
function grid_tickets_invoice_double_click(id, e, node){
this.move(id.row, -1, $$('grid_tickets_active'))
}
function cmd_move_tickets_right_click(){
$$('grid_tickets_active').eachRow(
function(row){
this.copy(row, -1, $$('grid_tickets_invoice'))
}
)
$$('grid_tickets_active').clearAll()
}
function cmd_move_tickets_left_click(){
$$('grid_tickets_invoice').eachRow(
function(row){
this.copy(row, -1, $$('grid_tickets_active'))
}
)
$$('grid_tickets_invoice').clearAll()
}
function ticket_set_client(row){
var form = $$('form_ticket_invoice')
var html = '<span class="webix_icon fa-user"></span><span class="lbl_partner">'
form.setValues({
id_partner: row.id,
tsearch_client_key: '',
tsearch_client_name: ''}, true)
html += row.nombre + ' (' + row.rfc + ')</span>'
$$('lbl_tclient').setValue(html)
}
function ticket_search_client_by_id(id){
webix.ajax().get('/values/client', {'id': id}, {
error: function(text, data, xhr) {
msg_error('Error al consultar')
},
success: function(text, data, xhr){
var values = data.json()
if (values.ok){
ticket_set_client(values.row)
}else{
msg = 'No se encontró un cliente con la clave: ' + id
msg_error(msg)
}
}
})
}
function tsearch_client_key_press(code, e){
var value = this.getValue()
if(code == 13 && value.length > 0){
var id = parseInt(value, 10)
if (isNaN(id)){
msg_error('Captura una clave válida')
}else{
ticket_search_client_by_id(id)
}
}
}
function grid_ticket_clients_found_click(obj){
ticket_set_client(obj)
}

View File

@ -33,6 +33,18 @@ var months = [
] ]
function get_icon(tipo){
icons = {
xml: 'fa-file-code-o',
pdf: 'fa-file-pdf-o',
zip: 'fa-file-zip-o',
email: 'fa-envelope-o',
print: 'fa-print',
}
return "<span class='webix_icon " + icons[tipo] + "'></span>"
}
function focus(name){ function focus(name){
webix.UIManager.setFocus(name) webix.UIManager.setFocus(name)
} }
@ -43,6 +55,11 @@ function showvar(values){
} }
function showtype(values){
webix.message(typeof(values))
}
function show(nombre, value){ function show(nombre, value){
if(value == '0'){ if(value == '0'){
value = false value = false
@ -84,7 +101,7 @@ String.prototype.to_float = function(){
function get_float(value){ function get_float(value){
return parseFloat(value.replace('$', '').replace(',', '').trim()).round(2) return parseFloat(value.replace('$', '').replace(',', '').trim()).round(DECIMALES)
} }
@ -112,12 +129,17 @@ webix.protoUI({
}, webix.ui.text) }, webix.ui.text)
webix.ui.datafilter.rowCount = webix.extend({ //~ webix.ui.datafilter.rowCount = webix.extend({
//~ refresh:function(master, node, value){
//~ node.firstChild.innerHTML = master.count();
//~ }
//~ }, webix.ui.datafilter.summColumn)
webix.ui.datafilter.countRows = webix.extend({
refresh:function(master, node, value){ refresh:function(master, node, value){
node.firstChild.innerHTML = master.count(); node.firstChild.innerHTML = master.count();
} }
}, webix.ui.datafilter.summColumn) }, webix.ui.datafilter.summColumn);
function validate_rfc(value){ function validate_rfc(value){
rfc = value.trim().toUpperCase(); rfc = value.trim().toUpperCase();
@ -279,3 +301,11 @@ webix.DataDriver.plainjs = webix.extend({
return this.hash2tree(hash, 0); return this.hash2tree(hash, 0);
} }
}, webix.DataDriver.json) }, webix.DataDriver.json)
function get_forma_pago(control){
webix.ajax().get('/values/formapago', {key: true}, function(text, data){
var values = data.json()
$$(control).getList().parse(values)
})
}

View File

@ -426,7 +426,7 @@ var controls_correo = [
var form_folios = { var form_folios = {
type: 'space', type: 'space',
responsive: true, //~ responsive: true,
cols: [{ cols: [{
view: 'form', view: 'form',
id: 'form_folios', id: 'form_folios',
@ -451,12 +451,13 @@ var form_correo = {
view: 'form', view: 'form',
id: 'form_correo', id: 'form_correo',
complexData: true, complexData: true,
scroll: true,
elements: controls_correo, elements: controls_correo,
elementsConfig: { elementsConfig: {
labelWidth: 150, labelWidth: 150,
labelAlign: 'right' labelAlign: 'right'
}, },
autoheight: true //~ autoheight: true
}], }],
} }
@ -486,6 +487,14 @@ var options_templates = [
var options_admin_otros = [ var options_admin_otros = [
{maxHeight: 15}, {maxHeight: 15},
{template: 'Facturación', type: 'section'},
{cols: [{maxWidth: 15},
{view: 'checkbox', id: 'chk_config_ocultar_metodo_pago', labelWidth: 0,
labelRight: 'Ocultar método de pago'},
{view: 'checkbox', id: 'chk_config_ocultar_condiciones_pago', labelWidth: 0,
labelRight: 'Ocultar condiciones de pago'},
{}]},
{maxHeight: 20},
{template: 'Ayudas varias', type: 'section'}, {template: 'Ayudas varias', type: 'section'},
{cols: [{maxWidth: 15}, {cols: [{maxWidth: 15},
{view: 'checkbox', id: 'chk_config_anticipo', labelWidth: 0, {view: 'checkbox', id: 'chk_config_anticipo', labelWidth: 0,
@ -496,7 +505,11 @@ var options_admin_otros = [
{cols: [{maxWidth: 15}, {cols: [{maxWidth: 15},
{view: 'checkbox', id: 'chk_config_cuenta_predial', labelWidth: 0, {view: 'checkbox', id: 'chk_config_cuenta_predial', labelWidth: 0,
labelRight: 'Mostrar cuenta predial'}, labelRight: 'Mostrar cuenta predial'},
{}]}, {view: 'checkbox', id: 'chk_config_codigo_barras', labelWidth: 0,
labelRight: 'Mostrar código de barras'},
{view: 'checkbox', id: 'chk_config_precio_con_impuestos', labelWidth: 0,
labelRight: 'Mostrar precio con impuestos'},
]},
{maxHeight: 20}, {maxHeight: 20},
{template: 'Complementos', type: 'section'}, {template: 'Complementos', type: 'section'},
{cols: [{maxWidth: 15}, {cols: [{maxWidth: 15},
@ -504,13 +517,24 @@ var options_admin_otros = [
labelRight: 'Mostrar el complemento INE al facturar'}, labelRight: 'Mostrar el complemento INE al facturar'},
{}]}, {}]},
{maxHeight: 20}, {maxHeight: 20},
{template: 'Punto de venta', type: 'section'},
{cols: [{maxWidth: 15},
{view: 'checkbox', id: 'chk_usar_punto_de_venta', labelWidth: 0,
labelRight: 'Usar punto de venta'},
{}]},
{maxHeight: 20},
{}] {}]
var body_admin_otros = {
view: 'scrollview', body: {rows: options_admin_otros},
}
var tab_options = { var tab_options = {
view: 'tabview', view: 'tabview',
id: 'tab_options', id: 'tab_options',
multiview: true,
animate: true, animate: true,
cells: [ cells: [
{id: 'Plantillas', rows: options_templates}, {id: 'Plantillas', rows: options_templates},
@ -935,7 +959,6 @@ var app_correo = {
{view: 'template', id: 'th_correo', type: 'header', {view: 'template', id: 'th_correo', type: 'header',
template: 'Configuración de correo'}, template: 'Configuración de correo'},
form_correo, form_correo,
{},
], ],
} }

View File

@ -2,7 +2,7 @@
var grid_cfdi_cliente_cols = [ var grid_cfdi_cliente_cols = [
{id: 'index', header: '#', adjust: 'data', css: 'right', {id: 'index', header: '#', adjust: 'data', css: 'right',
footer: {content: 'rowCount', colspan: 3, css: 'right'}}, footer: {content: 'countRows', colspan: 3, css: 'right'}},
{id: "id", header:"ID", hidden:true}, {id: "id", header:"ID", hidden:true},
{id: "serie", header: ["Serie", {content: "selectFilter"}], adjust: "header", {id: "serie", header: ["Serie", {content: "selectFilter"}], adjust: "header",
sort:"string"}, sort:"string"},
@ -180,20 +180,9 @@ var toolbar_invoices_filter = [
] ]
function get_icon(tipo){
icons = {
xml: 'fa-file-code-o',
pdf: 'fa-file-pdf-o',
zip: 'fa-file-zip-o',
email: 'fa-envelope-o',
}
return "<span class='webix_icon " + icons[tipo] + "'></span>"
}
var grid_invoices_cols = [ var grid_invoices_cols = [
{id: 'index', header: '#', adjust: 'data', css: 'right', {id: 'index', header: '#', adjust: 'data', css: 'right',
footer: {content: 'rowCount', colspan: 3, css: 'right'}}, footer: {content: 'countRows', colspan: 3, css: 'right'}},
{id: "id", header:"ID", hidden:true}, {id: "id", header:"ID", hidden:true},
{id: "serie", header: ["Serie", {content: "selectFilter"}], adjust: "data", {id: "serie", header: ["Serie", {content: "selectFilter"}], adjust: "data",
sort:"string"}, sort:"string"},
@ -227,6 +216,13 @@ var grid_invoices = {
resizeColumn: true, resizeColumn: true,
headermenu: true, headermenu: true,
columns: grid_invoices_cols, columns: grid_invoices_cols,
scheme:{
$change:function(item){
if (item.estatus == 'Cancelada'){
item.$css = 'cancel'
}
}
},
on:{ on:{
'data->onStoreUpdated':function(){ 'data->onStoreUpdated':function(){
this.data.each(function(obj, i){ this.data.each(function(obj, i){
@ -317,6 +313,7 @@ var suggest_partners = {
} }
} }
var suggest_products = { var suggest_products = {
view: 'gridsuggest', view: 'gridsuggest',
id: 'grid_products_found', id: 'grid_products_found',

View File

@ -5,12 +5,13 @@ var menu_data = [
{id: 'app_partners', icon: 'users', value: 'Clientes y Proveedores'}, {id: 'app_partners', icon: 'users', value: 'Clientes y Proveedores'},
{id: 'app_products', icon: 'server', value: 'Productos y Servicios'}, {id: 'app_products', icon: 'server', value: 'Productos y Servicios'},
{id: 'app_bancos', icon: 'university', value: 'Bancos'}, {id: 'app_bancos', icon: 'university', value: 'Bancos'},
{id: 'app_invoices', icon: 'cart-plus', value: 'Facturas'}, {id: 'app_invoices', icon: 'file-code-o', value: 'Facturas'},
] ]
var sidebar = { var sidebar = {
view: 'sidebar', view: 'sidebar',
id: 'main_sidebar',
data: menu_data, data: menu_data,
ready: function(){ ready: function(){
this.select('app_home'); this.select('app_home');
@ -36,6 +37,7 @@ var multi_main = {
app_partners, app_partners,
app_products, app_products,
app_bancos, app_bancos,
app_tickets,
app_invoices, app_invoices,
], ],
} }
@ -60,7 +62,7 @@ var ui_main = {
{view: 'toolbar', padding: 3, elements: [ {view: 'toolbar', padding: 3, elements: [
{view: 'button', type: 'icon', icon: 'bars', {view: 'button', type: 'icon', icon: 'bars',
width: 37, align: 'left', css: 'app_button', click: function(){ width: 37, align: 'left', css: 'app_button', click: function(){
$$('$sidebar1').toggle() $$('main_sidebar').toggle()
} }
}, },
{view: 'label', id: 'lbl_title_main', label: '<b>Empresa Libre</b>'}, {view: 'label', id: 'lbl_title_main', label: '<b>Empresa Libre</b>'},

View File

@ -12,7 +12,7 @@ var toolbar_partners = [
var grid_partners_cols = [ var grid_partners_cols = [
{id: 'index', header:'#', css: 'right', {id: 'index', header:'#', css: 'right',
footer: {content: 'rowCount', colspan: 2, css: 'right'}}, footer: {content: 'countRows', colspan: 2, css: 'right'}},
{id: 'id', header: 'Clave', sort: 'int', css: 'right'}, {id: 'id', header: 'Clave', sort: 'int', css: 'right'},
{id: 'rfc', header: ['RFC', {content: 'textFilter'}], adjust: 'data', {id: 'rfc', header: ['RFC', {content: 'textFilter'}], adjust: 'data',
sort: 'string', footer: {text: 'Clientes y Proveedores', colspan: 2}}, sort: 'string', footer: {text: 'Clientes y Proveedores', colspan: 2}},
@ -143,7 +143,7 @@ var toolbar_contacts = [
var grid_contacts_cols = [ var grid_contacts_cols = [
{id: 'index', header: '#', adjust:'data', css:'right', {id: 'index', header: '#', adjust:'data', css:'right',
footer: {content: 'rowCount'}}, footer: {content: 'countRows'}},
{id: 'id', header: '', hidden: true}, {id: 'id', header: '', hidden: true},
{id: 'title', header: 'Título', adjust:'data', sort: 'string', {id: 'title', header: 'Título', adjust:'data', sort: 'string',
footer: 'Contactos'}, footer: 'Contactos'},

View File

@ -13,7 +13,7 @@ var toolbar_products = [
var grid_products_cols = [ var grid_products_cols = [
{ id: "id", header: "ID", width: 75, hidden: true}, { id: "id", header: "ID", width: 75, hidden: true},
{ id: "clave", header: ["Clave", {content: "textFilter"}], width: 100, { id: "clave", header: ["Clave", {content: "textFilter"}], width: 100,
sort: 'string', footer: {content: 'rowCount', css: 'right'}}, sort: 'string', footer: {content: 'countRows', css: 'right'}},
{ id: "descripcion", header: ["Descripción", {content: "textFilter"}], { id: "descripcion", header: ["Descripción", {content: "textFilter"}],
fillspace:true, sort: 'string', footer: 'Productos y Servicios'}, fillspace:true, sort: 'string', footer: 'Productos y Servicios'},
{ id: "unidad", header: ["Unidad", {content: "selectFilter"}], width: 150, { id: "unidad", header: ["Unidad", {content: "selectFilter"}], width: 150,
@ -123,10 +123,16 @@ var controls_generals = [
labelAlign: 'right', label: 'Etiquetas', labelAlign: 'right', label: 'Etiquetas',
placeholder: 'Separadas por comas'} placeholder: 'Separadas por comas'}
]}, ]},
{cols: [{view: "currency", type: "text", id: "valor_unitario", {cols: [
{view: "currency", type: "text", id: "valor_unitario",
name: "valor_unitario", label: "Valor Unitario", width: 300, name: "valor_unitario", label: "Valor Unitario", width: 300,
labelWidth: 130, labelAlign: "right", required: true, labelWidth: 130, labelAlign: "right", required: true,
invalidMessage: "Captura un valor númerico", inputAlign: "right" },{}]}, invalidMessage: "Captura un valor númerico", inputAlign: "right"},
{view: 'currency', type: 'text', id: 'precio_con_impuestos',
name: 'precio_con_impuestos', label: 'Con Impuestos', width: 300,
labelWidth: 115, labelAlign: 'right', required: false,
invalidMessage: 'Captura un valor númerico', inputAlign: 'right'},
{},]},
{cols: [ {cols: [
{view: 'checkbox', id: 'inventario', name: 'inventario', hidden: true, {view: 'checkbox', id: 'inventario', name: 'inventario', hidden: true,
label: 'Inventario', labelAlign: 'right', labelWidth: 130}, label: 'Inventario', labelAlign: 'right', labelWidth: 130},

View File

@ -0,0 +1,421 @@
var toolbar_tickets = [
{view: 'button', id: 'cmd_nuevo_ticket', label: 'Nuevo', type: 'iconButton',
autowidth: true, icon: 'plus'},
{view: 'button', id: 'cmd_ticket_to_invoice', label: 'Facturar',
type: 'iconButton', autowidth: true, icon: 'file-code-o'},
{},
{view: 'button', id: 'cmd_cancelar_ticket', label: 'Cancelar',
type: 'iconButton', autowidth: true, icon: 'ban'},
]
var toolbar_tickets_filter = [
{view: 'richselect', id: 'filter_year_ticket', label: 'Año',
labelAlign: 'right', labelWidth: 50, width: 150, options: []},
{view: 'richselect', id: 'filter_month_ticket', label: 'Mes',
labelAlign: 'right', labelWidth: 50, width: 200, options: months},
{view: 'daterangepicker', id: 'filter_dates_ticket', label: 'Fechas',
labelAlign: 'right', width: 300},
]
var grid_tickets_cols = [
{id: 'index', header: '#', adjust: 'data', css: 'right',
footer: {content: 'countRows', colspan: 3, css: 'right'}},
{id: "id", header:"ID", hidden:true},
{id: "serie", header: ["Serie", {content: "selectFilter"}], adjust: "data",
sort:"string", hidden: true},
{id: 'folio', header: ['Folio', {content: 'numberFilter'}], adjust: 'header',
sort: 'int', css: 'right', footer: {text: 'Tickets', colspan: 3}},
{id: "fecha", header: ["Fecha y Hora"],
adjust: "data", sort: "date"},
{id: "estatus", header: ["Estatus", {content: "selectFilter"}],
adjust: "data", sort:"string"},
{id: 'total', header: ['Total', {content: 'numberFilter'}], width: 150,
sort: 'int', format: webix.i18n.priceFormat, css: 'right'},
{id: "cliente", header: ["Razón Social", {content: "selectFilter"}],
fillspace:true, sort:"string", hidden: true},
{id: 'pdf', header: 'PDF', adjust: 'data', template: get_icon('pdf')},
{id: 'print', header: 'I', adjust: 'data', template: get_icon('print')},
]
var grid_tickets = {
view: 'datatable',
id: 'grid_tickets',
select: 'row',
adjust: true,
footer: true,
resizeColumn: true,
headermenu: true,
columns: grid_tickets_cols,
scheme:{
$change:function(item){
if (item.estatus == 'Cancelado'){
item.$css = 'cancel'
}
}
},
on:{
'data->onStoreUpdated':function(){
this.data.each(function(obj, i){
obj.index = i + 1
})
}
},
}
var rows_tickets_home = [
{view: 'toolbar', elements: toolbar_tickets},
{view: 'toolbar', elements: toolbar_tickets_filter},
grid_tickets,
]
var ticket_suggest_products = {
view: 'gridsuggest',
id: 'gt_productos_found',
name: 'gt_productos_found',
body: {
autoConfig: false,
header: true,
columns: [
{id: 'id', hidden: true},
{id: 'clave', header: 'Clave', adjust: 'data'},
{id: 'descripcion', header: 'Descripción', adjust: 'data'},
{id: 'unidad', header: 'Unidad', adjust: 'data'},
{id: 'valor_unitario', header: 'Valor Unitario', adjust: 'data',
format: webix.i18n.priceFormat}
],
dataFeed:function(text){
if (text.length > 2){
this.load('/values/product?name=' + text)
}else{
this.hide()
}
}
},
}
var tbody_buscar_producto = {rows: [
{cols: [
{view: 'search', id: 'tsearch_product_key', name: 'tsearch_product_key',
label: 'por Clave', labelPosition:'top', maxWidth: 250,
placeholder: 'Presiona ENTER para buscar'},
{view: 'search', id: 'tsearch_product_name', name: 'tsearch_product_name',
label: 'por Descripción', labelPosition:'top',
suggest: ticket_suggest_products, placeholder: 'Captura al menos tres letras'},
]},
]}
var grid_tdetails_cols = [
{id: "id", header:"ID", hidden: true},
{id: 'delete', header: '', width: 30, css: 'delete'},
{id: "clave", header:{text: 'Clave', css: 'center'}, width: 100,
footer: {text: 'Artículos', css: 'right_footer3'}},
{id: "clave_sat", hidden: true},
{id: "descripcion", header:{text: 'Descripción', css: 'center'},
fillspace: true, footer: {content: 'countRows', css: 'footer3'}},
{id: "unidad", header:{text: 'Unidad', css: 'center'}, width: 100},
{id: 'cantidad', header: {text: 'Cantidad', css: 'center'}, width: 100,
format: webix.i18n.numberFormat, css: 'right', editor: 'text',
footer: {content: 'summColumn', css: 'right_footer3'}},
{id: "valor_unitario", header:{text: 'Valor Unitario', css: 'center'},
width: 100, format: webix.i18n.priceFormat, css: 'right',
footer: {text: 'Total ', css: 'right_footer2'}},
{id: 'descuento', header: {text: 'Descuento', css: 'center'}, hidden: true,
width: 80, format: webix.i18n.priceFormat, css: 'right', editor: 'text'},
{id: 'precio_final', hidden: true, header: 'precio_final', width: 80,
format: webix.i18n.priceFormat, css: 'right'},
{id: "importe", header:{text: 'Importe', css: 'center'}, width: 150,
format: webix.i18n.priceFormat, css: 'right',
footer: {content: 'summColumn', css: 'right_footer2'}},
]
var grid_tdetails = {
view: 'datatable',
id: 'grid_tdetails',
select: 'row',
adjust: true,
autoheight: true,
editable: true,
footer: true,
columns: grid_tdetails_cols,
on:{
'data->onStoreUpdated':function(){
this.data.each(function(obj, i){
obj.delete = '-'
})
}
},
data: [],
}
var body_ticket_informacion = {rows: [
{view: 'richselect', id: 'lst_ticket_forma_pago', name: 'forma_pago',
label: 'Forma de Pago', labelPosition: 'top', required: true,
options: []},
],}
var controls_generate_ticket = [
{minHeight: 10, maxHeight: 10},
{cols: [{rows: [
{view: 'fieldset', label: 'Buscar Producto', body: tbody_buscar_producto},
]},
{maxWidth: 10},
{maxWidth: 300, rows: [
{view: 'fieldset', label: 'Información', body: body_ticket_informacion},
]},
]},
{view: 'label', label: 'Detalle', height: 30, align: 'left'},
grid_tdetails,
{minHeight: 20, maxHeight: 20},
{margin: 20, cols: [{},
{view: 'button', id: 'cmd_generar_ticket', label: 'Generar',
icon: 'ticket', type: 'iconButton', autowidth: true, align: 'center'},
{}]
},
{rows: [
{template: '', type: 'section'},
{margin: 10, cols: [{},
{view: 'button', id: 'cmd_cerrar_ticket', label: 'Cerrar',
type: 'danger', autowidth: true, align: 'center'}
]
},
]}
]
var toolbar_ticket_invoice = {view: 'toolbar', elements: [{},
{view: 'checkbox', id: 'chk_is_invoice_day', labelWidth: 0, width: 150,
labelRight: 'Es factura del día'},
{}]}
var ticket_suggest_partners = {
view: 'gridsuggest',
id: 'grid_ticket_clients_found',
name: 'grid_ticket_clients_found',
body: {
autoConfig: false,
header: false,
columns: [
{id: 'id', hidden: true},
{id: 'nombre', adjust: 'data'},
{id: 'rfc', adjust: 'data'},
{id: 'forma_pago', hidden: true},
{id: 'uso_cfdi', hidden: true},
],
dataFeed:function(text){
if (text.length > 2){
this.load('/values/client?name=' + text)
}else{
this.hide()
}
}
}
}
var ticket_search_client = {cols: [{rows: [
{view: 'fieldset', id: 'fs_ticket_search_client', label: 'Buscar Cliente', body: {rows: [
{cols: [
{view: 'search', id: 'tsearch_client_key', name: 'tsearch_client_key',
label: 'por Clave', labelPosition: 'top', maxWidth: 250,
placeholder:'Presiona ENTER para buscar'},
{view: 'search', id: 'tsearch_client_name',
name: 'tsearch_client_name', label: 'por Nombre o RFC',
labelPosition: 'top', suggest: ticket_suggest_partners,
placeholder: 'Captura al menos tres letras'},
]},
{cols: [
{view: 'label', id: 'lbl_tclient_title', autowidth:true,
name: "lbl_tclient_title", label: 'Seleccionado: ' },
{view: 'label', id: 'lbl_tclient', name: 'lbl_tclient',
label: 'Ninguno'},
]}
]}},
]},]}
var grid_tickets_active_cols = [
{id: 'index', header: '#', adjust: 'data', css: 'right'},
{id: "id", header:"ID", hidden:true},
{id: "serie", header: ["Serie", {content: "selectFilter"}], adjust: "data",
sort: "string", hidden: true},
{id: 'folio', header: ['Folio', {content: 'numberFilter'}], adjust: 'header',
sort: 'int', css: 'right', footer: {content: 'countRows', css: 'right'}},
{id: "fecha", header: ["Fecha y Hora"], adjust: "data", sort: "string",
footer: 'Tickets'},
{id: 'total', header: 'Total', width: 150,sort: 'int', css: 'right',
format: webix.i18n.priceFormat, footer: {content: 'summColumn',
css: 'right'}},
]
var grid_tickets_active = {
view: 'datatable',
id: 'grid_tickets_active',
select: 'row',
adjust: true,
footer: true,
drag: true,
resizeColumn: true,
headermenu: true,
columns: grid_tickets_active_cols,
on:{
'data->onStoreUpdated':function(){
this.data.each(function(obj, i){
obj.index = i + 1
})
}
},
}
var grid_tickets_invoice_cols = [
{id: 'index', header: '#', adjust: 'data', css: 'right'},
{id: "id", header:"ID", hidden:true},
{id: "serie", header: ["Serie", {content: "selectFilter"}], adjust: "data",
sort: "string", hidden: true},
{id: 'folio', header: ['Folio', {content: 'numberFilter'}], adjust: 'header',
sort: 'int', css: 'right', footer: {content: 'countRows', css: 'right'}},
{id: "fecha", header: ["Fecha y Hora"], adjust: "data", sort: "string",
footer: 'Tickets'},
{id: 'total', header: 'Total', width: 150,sort: 'int', css: 'right',
format: webix.i18n.priceFormat, footer: {content: 'summColumn',
css: 'right'}},
]
var grid_tickets_invoice = {
view: 'datatable',
id: 'grid_tickets_invoice',
select: 'row',
adjust: true,
footer: true,
drag: true,
resizeColumn: true,
headermenu: true,
columns: grid_tickets_invoice_cols,
on:{
'data->onStoreUpdated':function(){
this.data.each(function(obj, i){
obj.index = i + 1
})
}
},
}
var controls_ticket_to_invoice = [
{minHeight: 10, maxHeight: 10},
toolbar_ticket_invoice,
{minHeight: 10, maxHeight: 10},
ticket_search_client,
{minHeight: 5, maxHeight: 5},
{cols:[
{rows: [{view: 'label', label: 'Tickets sin facturar', height: 30,
align: 'left'},
grid_tickets_active]},
{rows:[{},
{view: 'button', id: 'cmd_move_tickets_right', label: '->', autowidth: true},
{view: 'button', id: 'cmd_move_tickets_left', label: '<-', autowidth: true},
{}]},
{rows: [{view: 'label', label: 'Tickets a facturar', height: 30, align: 'left'}, grid_tickets_invoice]},
]},
{minHeight: 20, maxHeight: 20},
{margin: 20, cols: [{},
{view: 'button', id: 'cmd_new_invoice_from_ticket', label: 'Facturar',
icon: 'ticket', type: 'iconButton', autowidth: true, align: 'center'},
{}]
},
{rows: [
{template: '', type: 'section'},
{margin: 10, cols: [{},
{view: 'button', id: 'cmd_close_ticket_invoice', label: 'Cerrar',
type: 'danger', autowidth: true, align: 'center'}
]
},
]}
]
var controls_new_ticket = [
{
view: 'tabview',
id: 'tv_new_ticket',
animate: true,
cells: [
{id: 'Generar', rows: controls_generate_ticket},
]
},
]
var controls_ticket_invoice = [
{
view: 'tabview',
id: 'tv_ticket_invoice',
animate: true,
cells: [
{id: 'Facturar Tickets', rows: controls_ticket_to_invoice},
]
},
]
var form_new_ticket = {
type: 'space',
responsive: true,
cols: [{
view: 'form',
id: 'form_new_ticket',
complexData: true,
scroll: true,
elements: controls_new_ticket,
}],
}
var form_ticket_invoice = {
type: 'space',
responsive: true,
cols: [{
view: 'form',
id: 'form_ticket_invoice',
complexData: true,
scroll: true,
elements: controls_ticket_invoice,
}],
}
var multi_tickets = {
id: 'multi_tickets',
view: 'multiview',
animate: true,
cells:[
{id: 'tickets_home', rows: rows_tickets_home},
{id: 'tickets_new', rows:[form_new_ticket]},
{id: 'tickets_invoice', rows:[form_ticket_invoice]}
],
}
var app_tickets = {
id: 'app_tickets',
rows:[
{view: 'template', id: 'th_ticckets', type: 'header',
template: 'Punto de venta - Tickets'},
multi_tickets
],
}

View File

@ -8,12 +8,14 @@
<script src="/static/js/ui/partners.js" type="text/javascript" ></script> <script src="/static/js/ui/partners.js" type="text/javascript" ></script>
<script src="/static/js/ui/products.js" type="text/javascript" ></script> <script src="/static/js/ui/products.js" type="text/javascript" ></script>
<script src="/static/js/ui/bancos.js" type="text/javascript" ></script> <script src="/static/js/ui/bancos.js" type="text/javascript" ></script>
<script src="/static/js/ui/tickets.js" type="text/javascript" ></script>
<script src="/static/js/ui/invoices.js" type="text/javascript" ></script> <script src="/static/js/ui/invoices.js" type="text/javascript" ></script>
<script src="/static/js/ui/main.js" type="text/javascript" ></script> <script src="/static/js/ui/main.js" type="text/javascript" ></script>
<script src="/static/js/controller/partners.js" type="text/javascript" ></script> <script src="/static/js/controller/partners.js" type="text/javascript" ></script>
<script src="/static/js/controller/products.js" type="text/javascript" ></script> <script src="/static/js/controller/products.js" type="text/javascript" ></script>
<script src="/static/js/controller/bancos.js" type="text/javascript" ></script> <script src="/static/js/controller/bancos.js" type="text/javascript" ></script>
<script src="/static/js/controller/tickets.js" type="text/javascript" ></script>
<script src="/static/js/controller/invoices.js" type="text/javascript" ></script> <script src="/static/js/controller/invoices.js" type="text/javascript" ></script>
<script src="/static/js/controller/main.js" type="text/javascript" ></script> <script src="/static/js/controller/main.js" type="text/javascript" ></script>