Resolver conflictos

This commit is contained in:
El Mau 2021-12-12 22:30:15 -06:00
commit 47ec5ad360
33 changed files with 3798 additions and 373 deletions

4
.gitignore vendored
View File

@ -14,7 +14,7 @@ source/media
# Sphinx documentation
*.xlsx
docs/bk/
bk/
source/docs/
site/
vedev/
@ -23,7 +23,7 @@ vedev/
.env/
virtual/
docs/
docs/build
cache/
credenciales.conf
*.sqlite

View File

@ -1,3 +1,32 @@
=======
v 1.43.0 [12-Dic-2021]
----------------------
- Soporte para entradas de almacen.
- Soporte para multi almacen.
- Soporte para regenerar un ticket.
- Soporte para movimientos entre almacenes
- Consulta en SAT para estatus de nómina
* IMPORTANTE:
Instalar nuevo requerimiento:
```
pip install segno
```
Es necesario hacer una migración:
```
git pull origin master
cd source/app/models
python main.py -bk
python main.py -m -r RFC
```
v 1.42.2 [14-Sep-2021]
----------------------
- Los productos pueden no llevar ningún impuesto.

View File

@ -25,6 +25,12 @@ BTC: `3FhiXcXmAesmQzrNEngjHFnvaJRhU1AGWV`
* openssl
* xmlsec
* Ubuntu 20.04
```
apt install pkg-config libxml2-dev libxmlsec1-dev libxmlsec1-openssl xsltproc
```
Debería de funcionar con cualquier combinación servidor-wsgi que soporte
aplicaciones Python.

View File

@ -1,2 +1,2 @@
1.42.2
1.43.0

20
docs/Makefile Normal file
View File

@ -0,0 +1,20 @@
# Minimal makefile for Sphinx documentation
#
# You can set these variables from the command line, and also
# from the environment for the first two.
SPHINXOPTS ?=
SPHINXBUILD ?= sphinx-build
SOURCEDIR = source
BUILDDIR = build
# Put it first so that "make" without argument is like "make help".
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
.PHONY: help Makefile
# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)

35
docs/make.bat Normal file
View File

@ -0,0 +1,35 @@
@ECHO OFF
pushd %~dp0
REM Command file for Sphinx documentation
if "%SPHINXBUILD%" == "" (
set SPHINXBUILD=sphinx-build
)
set SOURCEDIR=source
set BUILDDIR=build
if "%1" == "" goto help
%SPHINXBUILD% >NUL 2>NUL
if errorlevel 9009 (
echo.
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
echo.installed, then set the SPHINXBUILD environment variable to point
echo.to the full path of the 'sphinx-build' executable. Alternatively you
echo.may add the Sphinx directory to PATH.
echo.
echo.If you don't have Sphinx installed, grab it from
echo.http://sphinx-doc.org/
exit /b 1
)
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
goto end
:help
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
:end
popd

21
docs/source/index.rst Normal file
View File

@ -0,0 +1,21 @@
.. Documentación de Empresa Libre documentation master file, created by
sphinx-quickstart on Sat Oct 23 21:49:03 2021.
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
Documentación de Empresa Libre
==============================
.. toctree::
:maxdepth: 2
:caption: Contents:
inicio
Indices and tables
==================
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`

39
docs/source/inicio.rst Normal file
View File

@ -0,0 +1,39 @@
Introducción
============
Acerca de Empresa Libre
-----------------------
**Empresa Libre** es un sistema de facturación electrónica especialmente diseñado
para la legislación mexicana.
**Empresa Libre** es 100% Software Libre, recuerda, **Software Libre, NO gratis.**
Requerimientos
--------------
* Servidor web Nginx
* Servidor de bases de datos PostgreSQL
* uwsgi
* python3.7+
* xsltproc
* openssl
* xmlsec
y los requerimientos del archivo *requirements.txt*
También es necesario un certificado de sellos vigente para facturar.
Repositorio
-----------
En el `repositorio del proyecto`_, puedes usar el sistema de tickets para solicitar soporte gratuito.
Contratar soporte de pago ayuda al desarrollo y mantenimiento de **Empresa Libre**.
.. _repositorio del proyecto: https://git.cuates.net/elmau/empresa-libre

View File

@ -8,15 +8,15 @@ bcrypt
python-dateutil
zeep
chardet
pyqrcode
pypng
reportlab
psycopg2-binary
cryptography
cryptography==3.4.8
xmlsec
segno
# pyqrcode
# pypng
# python-escpos
# pyusb
# pyserial
# qrcode

View File

@ -1,5 +1,6 @@
#!/usr/bin/env python3
import io
import os
import re
import smtplib
@ -597,7 +598,7 @@ class TemplateInvoice(BaseDocTemplate):
p = Paragraph(v, ps)
ls.append(p)
cbb = Image(data['path_cbb'])
cbb = Image(data['cbb'])
cbb.drawHeight = 4 * cm
cbb.drawWidth = 4 * cm

View File

@ -279,7 +279,11 @@ class AppProducts(object):
def on_get(self, req, resp):
values = req.params
req.context['result'] = self._db.get_products(values)
user = req.env['beaker.session']['userobj']
if 'opt' in values:
req.context['result'] = self._db.products_get(values, user)
else:
req.context['result'] = self._db.get_products(values)
resp.status = falcon.HTTP_200
def on_post(self, req, resp):
@ -494,14 +498,18 @@ class AppNomina(object):
def on_get(self, req, resp):
values = req.params
by = values.get('by', '')
req.context['result'] = self._db.get_nomina(values)
if 'download' in by:
name = req.context['result']['name']
req.context['blob'] = req.context['result']['data']
resp.content_type = 'application/octet-stream'
resp.append_header(
'Content-Disposition', f'attachment; filename={name}')
user = req.env['beaker.session']['userobj']
if 'opt' in values:
req.context['result'] = self._db.nomina_get(values, user)
else:
by = values.get('by', '')
req.context['result'] = self._db.get_nomina(values)
if 'download' in by:
name = req.context['result']['name']
req.context['blob'] = req.context['result']['data']
resp.content_type = 'application/octet-stream'
resp.append_header(
'Content-Disposition', f'attachment; filename={name}')
resp.status = falcon.HTTP_200
def on_post(self, req, resp):
@ -664,3 +672,100 @@ class AppSucursales(object):
req.context['result'] = self._db.sucursales_post(values)
resp.status = falcon.HTTP_200
class AppPartnerProducts(object):
def __init__(self, db):
self._db = db
def on_get(self, req, resp):
values = req.params
req.context['result'] = self._db.partner_products_get(values)
resp.status = falcon.HTTP_200
def on_post(self, req, resp):
values = req.params
req.context['result'] = self._db.partner_products_post(values)
resp.status = falcon.HTTP_200
class AppInventoryEntries(object):
def __init__(self, db):
self._db = db
def on_get(self, req, resp):
values = req.params
req.context['result'] = self._db.inventory_entries_get(values)
resp.status = falcon.HTTP_200
def on_post(self, req, resp):
values = req.params
user = req.env['beaker.session']['userobj']
req.context['result'] = self._db.inventory_entries_post(values, user)
resp.status = falcon.HTTP_200
class AppWareHouse(object):
def __init__(self, db):
self._db = db
def on_get(self, req, resp):
values = req.params
req.context['result'] = self._db.warehouse_get(values)
resp.status = falcon.HTTP_200
def on_post(self, req, resp):
values = req.params
user = req.env['beaker.session']['userobj']
req.context['result'] = self._db.warehouse_post(values, user)
resp.status = falcon.HTTP_200
class AppWareHouseProduct(object):
def __init__(self, db):
self._db = db
def on_get(self, req, resp):
values = req.params
user = req.env['beaker.session']['userobj']
req.context['result'] = self._db.warehouseproduct_get(values, user)
resp.status = falcon.HTTP_200
def on_post(self, req, resp):
values = req.params
user = req.env['beaker.session']['userobj']
req.context['result'] = self._db.warehouseproduct_post(values, user)
resp.status = falcon.HTTP_200
class AppTicketsDetails(object):
def __init__(self, db):
self._db = db
def on_get(self, req, resp):
values = req.params
user = req.env['beaker.session']['userobj']
req.context['result'] = self._db.ticketsdetails_get(values, user)
resp.status = falcon.HTTP_200
class AppUsers(object):
def __init__(self, db):
self._db = db
def on_get(self, req, resp):
values = req.params
user = req.env['beaker.session']['userobj']
req.context['result'] = self._db.users_get(values, user)
resp.status = falcon.HTTP_200
def on_post(self, req, resp):
values = req.params
user = req.env['beaker.session']['userobj']
req.context['result'] = self._db.users_post(values, user)
resp.status = falcon.HTTP_200

View File

@ -4,9 +4,11 @@ import argparse
import base64
import datetime
import getpass
import hashlib
import subprocess
from pathlib import Path
import lxml.etree as ET
import xmlsec
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import serialization
@ -30,6 +32,9 @@ class SATCertificate(object):
def _init_values(self):
self._rfc = ''
self._serial_number = ''
self._serial_number2 = ''
self._subject = ''
self._issuer = ''
self._not_before = None
self._not_after = None
self._is_fiel = False
@ -67,9 +72,14 @@ class SATCertificate(object):
def _get_data_cer(self, cer):
self._cer = cer
obj = x509.load_der_x509_certificate(cer, default_backend())
self._issuer = obj.issuer.rfc4514_string()
self._subject = obj.subject.rfc4514_string()
self._rfc = obj.subject.get_attributes_for_oid(
NameOID.X500_UNIQUE_IDENTIFIER)[0].value.split(' ')[0]
self._serial_number = '{0:x}'.format(obj.serial_number)[1::2]
self._serial_number2 = '{0:x}'.format(obj.serial_number)
self._serial_number = self._serial_number2[1::2]
self._not_before = obj.not_valid_before
self._not_after = obj.not_valid_after
now = datetime.datetime.utcnow()
@ -145,12 +155,24 @@ class SATCertificate(object):
def sign_xml(self, tree):
node = xmlsec.tree.find_node(tree, xmlsec.constants.NodeSignature)
ctx = xmlsec.SignatureContext()
key = xmlsec.Key.from_memory(self.key_pem, xmlsec.constants.KeyDataFormatPem)
key = xmlsec.Key.from_memory(
self.key_pem, xmlsec.constants.KeyDataFormatPem)
ctx.key = key
ctx.sign(node)
node = xmlsec.tree.find_node(tree, 'X509Certificate')
node.text = self.cer_txt
return tree
# ~ node = xmlsec.tree.find_node(tree, 'X509IssuerName')
# ~ node.text = self.issuer
node = xmlsec.tree.find_node(tree, 'X509SerialNumber')
node.text = self.serial_number
# ~ node = xmlsec.tree.find_node(tree, 'SignatureValue')
# ~ node.text = node.text.replace('\n', '')
xml_signed = ET.tostring(tree,
xml_declaration=True, encoding='UTF-8').decode()
return xml_signed
@property
def rfc(self):
@ -160,6 +182,18 @@ class SATCertificate(object):
def serial_number(self):
return self._serial_number
@property
def serial_number2(self):
return self._serial_number2
@property
def issuer(self):
return self._issuer
@property
def subject(self):
return self._subject
@property
def not_before(self):
return self._not_before

View File

@ -17,6 +17,7 @@
# ~ along with this program. If not, see <http://www.gnu.org/licenses/>.
import base64
import logging
import lxml.etree as ET
@ -146,6 +147,11 @@ class PACComercioDigital(object):
return data
def _get_data_cancel(self, cfdi, info, auth):
info['pass'] = '12345678a'
info['tipo'] = 'cfdi3.3'
info['key'] = base64.b64encode(info['key']).decode()
info['cer'] = base64.b64encode(info['cer']).decode()
NS_CFDI = {
'cfdi': 'http://www.sat.gob.mx/cfd/3',
'tdf': 'http://www.sat.gob.mx/TimbreFiscalDigital',
@ -200,7 +206,15 @@ class PACComercioDigital(object):
self._error(result.headers['errmsg'])
return ''
return result.text
tree = ET.fromstring(result.text)
date_cancel = tree.xpath('string(//Acuse/@Fecha)')[:19]
data = {
'acuse': result.text,
'date': date_cancel,
}
return data
def _get_headers_cancel_xml(self, cfdi, info, auth):
NS_CFDI = {

View File

@ -16,7 +16,7 @@
# ~ You should have received a copy of the GNU General Public License
# ~ along with this program. If not, see <http://www.gnu.org/licenses/>.
import base64
# ~ import base64
import datetime
import logging
import os
@ -129,7 +129,9 @@ class PACFinkok(object):
return {}
if self.CODE['200'] != ce:
log.error('CodEstatus', type(ce), ce)
self._error = ce
return {}
return result
return result
@ -243,8 +245,6 @@ class PACFinkok(object):
client = Client(self.URL[method],
transport=self._transport, plugins=self._plugins)
client.set_ns_prefix('can', 'http://facturacion.finkok.com/cancel')
# ~ xml = f'<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n{xml}'
# ~ xml = f'<?xml version="1.0" encoding="UTF-8" standalone="true"?>\n{xml}'
args = {
'xml': xml.encode(),
'username': auth['user'],

View File

@ -0,0 +1,87 @@
#!/usr/bin/env python3
from decimal import Decimal, getcontext
# ~ getcontext().prec = 6
import lxml.etree as ET
from requests.structures import CaseInsensitiveDict as CIDict
NS_CFDI = {
'cfdi': 'http://www.sat.gob.mx/cfd/3',
'tfd': 'http://www.sat.gob.mx/TimbreFiscalDigital',
'nomina12': 'http://www.sat.gob.mx/nomina12',
}
PRE = '/cfdi:Comprobante'
class CfdiRead(object):
def __init__(self, source):
self._source = source
self._data = {}
self._error = ''
self._rfc_emisor = ''
self._rfc_receptor = ''
self._parse()
@property
def source(self):
return self._source
@property
def data(self):
return self._data
@property
def rfc_emisor(self):
return self._rfc_emisor
@property
def rfc_receptor(self):
return self._rfc_receptor
@property
def error(self):
return self._error
def _parse(self):
self._tree = ET.fromstring(self.source)
self._data['cfdi'] = dict(self._tree.attrib)
node_name = f'{PRE}/cfdi:Emisor'
self._data['emisor'] = self._get_attr(node_name)
self._rfc_emisor = self._data['emisor']['Rfc']
node_name = f'{PRE}/cfdi:Receptor'
self._data['receptor'] = self._get_attr(node_name)
self._rfc_receptor = self._data['receptor']['Rfc']
node_name = f'{PRE}/cfdi:Complemento/tfd:TimbreFiscalDigital'
self._data['timbre'] = self._get_attr(node_name)
self._parse_details()
return
def _get_attr(self, node_name):
node = self._tree.xpath(node_name, namespaces=NS_CFDI)[0]
attr = dict(node.attrib)
return attr
def _parse_details(self):
node_name = f'{PRE}/cfdi:Conceptos/cfdi:Concepto'
details = self._tree.xpath(node_name, namespaces=NS_CFDI)
rows = []
for detail in details:
row = dict(detail.attrib)
for k, v in row.items():
if k in ('Cantidad', 'ValorUnitario', 'Descuento', 'Importe'):
row[k] = Decimal(v)
# ~ row['taxes'] = self._get_taxes(detail)
rows.append(row)
self._data['conceptos'] = rows
return
class CfdiWrite(object):
pass

View File

@ -54,7 +54,7 @@ try:
except ImportError:
APP_LIBO = False
import pyqrcode
# ~ import pyqrcode
from dateutil import parser
from lxml import etree
@ -73,6 +73,7 @@ from settings import USAR_TOKEN, API, DECIMALES_TAX
# ~ v2
import segno
from .pacs.cfdi_cert import SATCertificate
from settings import (
@ -844,21 +845,28 @@ class LIBO(object):
if self._es_pre or self._is_ticket:
return
qr = data.pop('cbb')
for k, v in data.items():
self._set_cell('{timbre.%s}' % k, v)
pd = self._sheet.getDrawPage()
image = self._template.createInstance('com.sun.star.drawing.GraphicObjectShape')
gp = self._create_instance('com.sun.star.graphic.GraphicProvider')
# ~ image.GraphicURL = data['path_cbb']
pd.add(image)
properties = self._set_properties({'URL': self._path_url(data['path_cbb'])})
# ~ image.GraphicURL = data['path_cbb']
# ~ properties = self._set_properties({'URL': self._path_url(data['path_cbb'])})
instance = 'com.sun.star.io.SequenceInputStream'
stream = self._create_instance(instance)
stream.initialize((uno.ByteSequence(qr.getvalue()),))
properties = self._set_properties({'InputStream': stream})
image.Graphic = gp.queryGraphic(properties)
s = Size()
s.Width = 4150
s.Height = 4500
image.setSize(s)
image.Anchor = self._set_cell('{timbre.cbb}')
_kill(data['path_cbb'])
# ~ _kill(data['path_cbb'])
return
def _donataria(self, data):
@ -1490,19 +1498,13 @@ def to_pdf_from_json(rfc, version, data):
if exists(path_logo):
data['emisor']['logo2'] = path_logo
path_cbb = data['timbre']['path_cbb']
path = get_path_temp()
pdf = TemplateInvoice(path)
buffer = io.BytesIO()
pdf = TemplateInvoice(buffer)
pdf.custom_styles = custom_styles
pdf.data = data
pdf.render()
data = read_file(path)
_kill(path)
_kill(path_cbb)
return data
return buffer.getvalue()
def format_currency(value, currency, digits=2):
@ -1616,6 +1618,12 @@ def get_qr(data, p=True):
return base64.b64encode(buffer.getvalue()).decode()
def get_qr2(data, kind='svg'):
buffer = io.BytesIO()
segno.make(data).save(buffer, kind=kind, scale=8, border=2)
return buffer
def _get_relacionados(doc, version):
node = doc.find('{}CfdiRelacionados'.format(PRE[version]))
if node is None:
@ -1851,7 +1859,7 @@ def _totales(doc, cfdi, version):
return data
def _timbre(doc, version, values):
def _timbre(doc, version, values, pdf_from='1'):
CADENA = '||{version}|{UUID}|{FechaTimbrado}|{selloCFD}|{noCertificadoSAT}||'
if version == '3.3':
CADENA = '||{Version}|{UUID}|{FechaTimbrado}|{SelloCFD}|{NoCertificadoSAT}||'
@ -1869,7 +1877,11 @@ def _timbre(doc, version, values):
}
qr_data = '{url}{uuid}{emisor}{receptor}{total}{sello}'.format(**qr_data)
data['path_cbb'] = get_qr(qr_data)
if pdf_from == '1':
data['cbb'] = get_qr2(qr_data)
else:
data['cbb'] = get_qr2(qr_data, 'png')
data['cadenaoriginal'] = CADENA.format(**data)
return data
@ -1998,7 +2010,7 @@ def _cfdipays(doc, data, version):
return info
def get_data_from_xml(invoice, values):
def get_data_from_xml(invoice, values, pdf_from='1'):
data = {'cancelada': invoice.cancelada, 'donativo': False}
if hasattr(invoice, 'donativo'):
data['donativo'] = invoice.donativo
@ -2017,7 +2029,7 @@ def get_data_from_xml(invoice, values):
'rfc_receptor': data['receptor']['rfc'],
'total': data['comprobante']['total'],
}
data['timbre'] = _timbre(doc, version, options)
data['timbre'] = _timbre(doc, version, options, pdf_from)
del data['timbre']['version']
data['comprobante'].update(data['timbre'])
@ -2026,9 +2038,8 @@ def get_data_from_xml(invoice, values):
if data['pagos']:
data['pays'] = _cfdipays(doc, data, version)
data['pakings'] = values.get('pakings', [])
# ~ data['version'] = values['version']
# ~ data['version'] = version
data['el.version'] = values['el.version']
return data

View File

@ -21,6 +21,7 @@ import collections
import csv
import datetime
import getpass
import io
import json
import logging
import math
@ -59,6 +60,10 @@ from .pacs.cfdi_cert import SATCertificate
from .pacs import PACComercioDigital
from .pacs import PACFinkok
# ~ v2
import segno
from .pycfdi import CfdiRead
LOG_FORMAT = '%(asctime)s - %(levelname)s - %(message)s'
LOG_DATE = '%d/%m/%Y %H:%M:%S'
@ -660,7 +665,7 @@ def get_pac_by_rfc(cfdi):
return RFCS[rfc_pac]
def _cancel_finkok(invoice, auth, certificado):
def _cancel_with_cert(invoice, auth, certificado):
cert = SATCertificate(certificado.cer, certificado.key_enc.encode())
pac = PACS[auth['pac']]()
info = {'cer': cert.cer_pem, 'key': cert.key_pem}
@ -677,8 +682,8 @@ def _cancel_finkok(invoice, auth, certificado):
def cancel_xml_sign(invoice, auth, certificado):
if auth['pac'] == 'finkok':
return _cancel_finkok(invoice, auth, certificado)
if DEBUG:
return _cancel_with_cert(invoice, auth, certificado)
cert = SATCertificate(certificado.cer, certificado.key_enc.encode())
pac = PACS[auth['pac']]()
@ -689,10 +694,10 @@ def cancel_xml_sign(invoice, auth, certificado):
}
template = TEMPLATE_CANCEL.format(**data)
tree = ET.fromstring(template.encode())
tree = cert.sign_xml(tree)
sign_xml = ET.tostring(tree).decode()
sign_xml = cert.sign_xml(tree)
result = pac.cancel_xml(sign_xml, auth, invoice.xml)
if pac.error:
data = {'ok': False, 'msg': pac.error, 'row': {}}
return data
@ -778,3 +783,54 @@ def read_csv(path, args={'delimiter': '|'}):
rows = [r for r in reader]
return rows
def _products_from_xml(rfc, data):
result = {'status': 'server', 'error': ''}
cfdi = CfdiRead(data)
if not DEBUG and rfc != cfdi.rfc_receptor:
msg = f'El receptor no es: {rfc}'
result['error'] = msg
return result
result['data'] = cfdi.data
# ~ result['data']['xml'] = cfdi.source
del result['data']['cfdi']['Certificado']
del result['data']['cfdi']['NoCertificado']
del result['data']['cfdi']['Sello']
del result['data']['timbre']['SelloCFD']
del result['data']['timbre']['SelloSAT']
emisor = result['data']['emisor']
emisor['rfc'] = emisor.pop('Rfc')
emisor['nombre'] = emisor.pop('Nombre')
result['data']['emisor'] = emisor
products = result['data']['conceptos']
rows = []
for p in products:
row = {
'key': p['NoIdentificacion'],
'key_sat': p['ClaveProdServ'],
'description': p['Descripcion'],
'unit': p['Unidad'],
'unit_value': p['ValorUnitario'],
'import': p['Importe'],
'cant': p['Cantidad'],
}
rows.append(row)
result['data']['conceptos'] = rows
return result
def upload_file(rfc, opt, file_obj):
if opt == 'productsadd':
result = _products_from_xml(rfc, file_obj.file.read())
return result
def get_qr(data):
buffer = io.BytesIO()
segno.make(data).save(buffer, kind='svg', scale=8, border=2)
return buffer.getvalue()

View File

@ -17,7 +17,13 @@ from controllers.main import (AppEmpresas,
AppDocumentos, AppFiles, AppPreInvoices, AppCuentasBanco,
AppMovimientosBanco, AppTickets, AppStudents, AppEmployees, AppNomina,
AppInvoicePay, AppCfdiPay, AppSATBancos, AppSociosCuentasBanco,
AppSATFormaPago, AppSATLeyendaFiscales, AppCert, AppSucursales
AppSATFormaPago, AppSATLeyendaFiscales, AppCert, AppSucursales,
AppPartnerProducts,
AppInventoryEntries,
AppTicketsDetails,
AppUsers,
AppWareHouse,
AppWareHouseProduct,
)
@ -64,6 +70,12 @@ api.add_route('/socioscb', AppSociosCuentasBanco(db))
api.add_route('/leyendasfiscales', AppSATLeyendaFiscales(db))
api.add_route('/cert', AppCert(db))
api.add_route('/sucursales', AppSucursales(db))
api.add_route('/partnerproducts', AppPartnerProducts(db))
api.add_route('/inventoryentries', AppInventoryEntries(db))
api.add_route('/warehouse', AppWareHouse(db))
api.add_route('/warehouseproduct', AppWareHouseProduct(db))
api.add_route('/ticketsdetails', AppTicketsDetails(db))
api.add_route('/users', AppUsers(db))
session_options = {

View File

@ -466,17 +466,53 @@ class StorageEngine(object):
return main.SATLeyendasFiscales.remove(values)
# ~ v2
def cert_get(self, values):
return main.Certificado.get_data(values)
def cert_get(self, filters):
return main.Certificado.get_data(filters)
def cert_post(self, values):
return main.Certificado.post(values)
def cert_post(self, filters):
return main.Certificado.post(filters)
def sucursales_get(self, values):
return main.Sucursales.get_data(values)
def sucursales_get(self, filters):
return main.Sucursales.get_data(filters)
def sucursales_post(self, values):
return main.Sucursales.post(values)
def sucursales_post(self, filters):
return main.Sucursales.post(filters)
def partner_products_get(self, filters):
return main.PartnerProducts.get_data(filters)
def inventory_entries_get(self, filters):
return main.InventoryEntries.get_data(filters)
def inventory_entries_post(self, filters, user):
return main.InventoryEntries.post(filters, user)
def warehouse_get(self, filters):
return main.Almacenes.get_data(filters)
def warehouse_post(self, values, user):
return main.Almacenes.post(values, user)
def warehouseproduct_get(self, filters, user):
return main.WareHouseProduct.get_data(filters, user)
def warehouseproduct_post(self, filters, user):
return main.WareHouseProduct.post(filters, user)
def users_get(self, filters, user):
return main.Usuarios.get_data(filters, user)
def users_post(self, args, user):
return main.Usuarios.post(args, user)
def ticketsdetails_get(self, filters, user):
return main.TicketsDetalle.get_data(filters, user)
def products_get(self, filters, user):
return main.Productos.get_data(filters, user)
def nomina_get(self, filters, user):
return main.CfdiNomina.get_data(filters, user)
# Companies only in MV
def _get_empresas(self, values):

File diff suppressed because it is too large Load Diff

View File

@ -42,7 +42,7 @@ except ImportError:
DEBUG = DEBUG
VERSION = '1.42.2'
VERSION = '1.43.0'
EMAIL_SUPPORT = ('soporte@empresalibre.mx',)
TITLE_APP = '{} v{}'.format(TITLE_APP, VERSION)
@ -192,6 +192,8 @@ API = 'https://api.empresalibre.net{}'
CURRENCY_MN = 'MXN'
# ~ v2
CANCEL_VERSION = ('3.3', '4.0')
IS_MV = MV
DB_COMPANIES = os.path.abspath(os.path.join(BASE_DIR, '..', 'db', 'rfc.db'))
path_bk = os.path.join(path_docs, 'tmp')
@ -236,6 +238,7 @@ RFCS = {
'CVD110412TF6': 'finkok',
'SCD110105654': 'comercio',
'AAA010101AAA': 'comercio',
'SPR190613I52': 'comercio',
}
URL = {
@ -249,7 +252,7 @@ DEFAULT_GLOBAL = {
'clave_sat': '01010101',
}
TEMPLATE_CANCEL = """<Cancelacion RfcEmisor="{rfc}" Fecha="{fecha}" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://cancelacfd.sat.gob.mx">
TEMPLATE_CANCEL = """<Cancelacion xmlns="http://cancelacfd.sat.gob.mx" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" RfcEmisor="{rfc}" Fecha="{fecha}">
<Folios>
<UUID>{uuid}</UUID>
</Folios>
@ -262,18 +265,24 @@ TEMPLATE_CANCEL = """<Cancelacion RfcEmisor="{rfc}" Fecha="{fecha}" xmlns:xsi="h
<Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" />
</Transforms>
<DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
<DigestValue />
<DigestValue/>
</Reference>
</SignedInfo>
<SignatureValue />
<SignatureValue/>
<KeyInfo>
<X509Data>
<X509SubjectName />
<X509IssuerSerial />
<X509Certificate />
<X509IssuerSerial>
<X509IssuerName/>
<X509SerialNumber/>
</X509IssuerSerial>
<X509Certificate/>
</X509Data>
<KeyValue />
<KeyValue>
<RSAKeyValue>
<Modulus/>
<Exponent/>
</RSAKeyValue>
</KeyValue>
</KeyInfo>
</Signature>
</Cancelacion>
"""
</Cancelacion>"""

View File

@ -95,7 +95,10 @@ var controllers = {
$$('chk_config_codigo_barras').attachEvent('onItemClick', chk_config_item_click)
$$('chk_config_precio_con_impuestos').attachEvent('onItemClick', chk_config_item_click)
$$('chk_llevar_inventario').attachEvent('onItemClick', chk_config_item_click)
$$('chk_use_packing').attachEvent('onItemClick', chk_config_item_click)
$$('chk_multi_stock').attachEvent('onItemClick', chk_config_item_click)
$$('cmd_add_warehouse').attachEvent('onItemClick', cmd_add_warehouse_click)
$$('grid_warehouse').attachEvent('onItemClick', grid_warehouse_click)
//~ $$('chk_use_packing').attachEvent('onItemClick', chk_config_item_click)
//~ Complements
$$('chk_usar_nomina').attachEvent('onItemClick', chk_config_item_click)
@ -454,6 +457,33 @@ function get_admin_usuarios(){
})
}
})
webix.ajax().get('/users', {'opt': 'is_superadmin'}, {
error: function(text, data, xhr) {
msg = 'Error al consultar'
msg_error(msg)
},
success: function(text, data, xhr) {
var values = data.json()
if(values.is_superadmin){
$$('grid_usuarios').showColumn('es_superusuario')
}
}
})
webix.ajax().get('/sucursales', {'opt': 'there_are_branchs'}, {
error: function(text, data, xhr) {
msg = 'Error al consultar'
msg_error(msg)
},
success: function(text, data, xhr) {
var value = data.json()
if(value){
$$('grid_usuarios').showColumn('in_branch')
}
}
})
}
@ -481,7 +511,12 @@ function set_config_admin_sucursales(){
},
success: function(text, data, xhr) {
var values = data.json()
lst_parse2('lst_sucursal_add_invoice', values)
lst_parse2('lst_sucursal_add_invoice', values.folios)
lst_parse2('lst_sucursal_warehouse', values.warehouse)
if(values.multi_stock){
$$('lst_sucursal_warehouse').show()
$$('grid_sucursales').showColumn('warehouse')
}
}
})
@ -516,7 +551,12 @@ function get_config_values(opt){
$$('lst_pac').setValue(values[key])
}else{
$$(key).setValue(values[key])
if(key=='chk_config_leyendas_fiscales'){
const chk_obj = [
'chk_config_leyendas_fiscales',
'chk_llevar_inventario',
'chk_multi_stock',
]
if(chk_obj.indexOf(key) > 0){
admin_config_other_options(key)
}
}
@ -2017,6 +2057,70 @@ function grid_usuarios_click(id, e, node){
}
function set_user_branch(id, state){
if(state){
win_set_branch_user.init()
$$('txt_user_id').setValue(id)
webix.ajax().get('/sucursales?opt=for_select', function(text, data){
var values = data.json()
$$('lst_branchs').getList().parse(values)
})
$$('win_set_branch_user').show()
}else{
var args = {
opt: 'set_branch_null',
values: {id: id}
}
webix.ajax().post('/users', args, {
error:function(text, data, XmlHttpRequest){
msg = 'Ocurrio un error, consulta a soporte técnico'
msg_error(msg)
},
success:function(text, data, XmlHttpRequest){
var values = data.json()
if(values.ok){
msg_ok('Asignación a Sucursal eliminada correctamente')
}else{
msg_error(values.msg)
}
}
})
}
}
function cmd_set_branch_user_save_click(){
var id_user = $$('txt_user_id').getValue()
var id_branch = $$('lst_branchs').getValue()
$$('win_set_branch_user').hide()
var args = {
opt: 'set_branch',
values: {id_user: id_user, id_branch: id_branch}
}
webix.ajax().post('/users', args, {
error:function(text, data, XmlHttpRequest){
msg = 'Ocurrio un error, consulta a soporte técnico'
msg_error(msg)
},
success:function(text, data, XmlHttpRequest){
var values = data.json()
if(values.ok){
msg_ok('Asignación de Sucursal establecida correctamente')
}else{
msg_error(values.msg)
}
}
})
}
function cmd_set_branch_user_cancel_click(){
$$('win_set_branch_user').hide()
}
function grid_usuarios_on_check(row, column, state){
var values = {
@ -2024,6 +2128,13 @@ function grid_usuarios_on_check(row, column, state){
field: column,
value: state,
}
if(column='in_branch'){
set_user_branch(row, state)
return
}
webix.ajax().get('/values/usuarioupdate', values, {
error: function(text, data, xhr) {
},
@ -2489,18 +2600,42 @@ function opt_make_pdf_from_on_change(new_value, old_value){
}
}
})
}
function admin_config_other_options(id){
var value = Boolean($$(id).getValue())
if(id=='chk_config_leyendas_fiscales'){
var value = Boolean($$(id).getValue())
if(value){
$$('cmd_admin_leyendas_fiscales').enable()
}else{
$$('cmd_admin_leyendas_fiscales').disable()
}
}else if(id=='chk_llevar_inventario'){
if(value){
$$('chk_multi_stock').enable()
}else{
$$('chk_multi_stock').setValue(0)
$$('chk_multi_stock').disable()
set_visible_warehouse(false)
disable_config_option('chk_multi_stock')
}
}else if(id=='chk_multi_stock'){
set_visible_warehouse(value)
}
}
function set_visible_warehouse(value){
if(value){
$$('txt_add_warehouse').show()
$$('cmd_add_warehouse').show()
$$('grid_warehouse').load('/warehouse?opt=all')
$$('grid_warehouse').show()
}else{
$$('txt_add_warehouse').hide()
$$('cmd_add_warehouse').hide()
$$('grid_warehouse').hide()
}
}
@ -2797,6 +2932,7 @@ function cmd_add_sucursal_click(){
sucursal_name = values.sucursal_add_name.trim()
sucursal_invoice = values.sucursal_add_invoice
sucursal_ticket = values.sucursal_add_ticket.trim()
sucursal_warehouse = values.sucursal_warehouse
if(!sucursal_name){
msg = 'El campo Nombre de la sucursal no puede estar vacío'
@ -2829,15 +2965,22 @@ function cmd_add_sucursal_click(){
nombre: sucursal_name,
serie_facturas: sucursal_invoice,
serie_tickets: sucursal_ticket,
//~ warehouse: sucursal_warehouse,
}
if(sucursal_warehouse){
values['warehouse'] = sucursal_warehouse
}
var data = {
opt: 'create',
values: values,
}
var clean = {
txt_sucursal_add_name: '',
lst_sucursal_add_invoice: '',
txt_sucursal_add_ticket: '',
lst_sucursal_warehouse: '',
}
msg = 'Datos correctos.<BR><BR>¿Estás seguro de agregar la nueva sucursal?'
@ -2910,3 +3053,115 @@ function grid_sucursales_click(id, e, node){
})
}
function disable_config_option(id){
var values = {}
values[id] = 0
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_error(values.msg)
}
}
})
}
function cmd_add_warehouse_click(){
var value = $$('txt_add_warehouse').getValue().trim()
if(!value){
msg = 'El nombre del almacen es requerido'
msg_error(msg)
return
}
msg = '¿Deseas agregar este Almacen?'
webix.confirm({
title: 'Agregar Almacen',
ok: 'Si',
cancel: 'No',
type: 'confirm-error',
text: msg,
callback:function(result){
if(result){
add_warehouse(value)
}
}
})
}
function add_warehouse(name){
var grid = $$('grid_warehouse')
var data = {
opt: 'create',
values: {name: name},
}
webix.ajax().post('/warehouse', data, {
error:function(text, data, XmlHttpRequest){
msg = 'Ocurrio un error, consulta a soporte técnico'
msg_error(msg)
},
success:function(text, data, XmlHttpRequest){
var values = data.json()
if(values.ok){
$$('txt_add_warehouse').setValue('')
if(values.msg){
msg_ok(values.msg)
}else{
grid.parse(values.rows)
msg_ok('Almacen agregado correctamente')
}
}else{
msg_error(values.msg)
}
}
})
}
function grid_warehouse_click(id, e, node){
if(id.column != 'delete'){
return
}
var data = {
opt: 'delete',
values: {id: id.row},
}
msg = '¿Estás seguro de borrar el almacen seleccionado?<BR><BR>ESTA ACCIÓN NO SE PUEDE DESHACER'
webix.confirm({
title: 'Borrar Almacen',
ok: 'Si',
cancel: 'No',
type: 'confirm-error',
text: msg,
callback:function(result){
if(result){
webix.ajax().post('/warehouse', data, {
error:function(text, data, XmlHttpRequest){
msg = 'Ocurrio un error, consulta a soporte técnico'
msg_error(msg)
},
success:function(text, data, XmlHttpRequest){
var values = data.json()
if(values.ok){
$$('grid_warehouse').remove(id.row)
msg_ok('Almacen eliminado correctamente')
}else{
msg_error(values.msg)
}
}
})
}
}
})
}

View File

@ -581,7 +581,6 @@ function save_invoice(data){
table_totals.clear()
grid.clearAll()
$$('grid_totals').clearAll()
}
return result
@ -909,9 +908,9 @@ function calcular_impuestos(){
}
function set_product(values){
var taxes = values.taxes
var values = values.row
function set_product(data){
var taxes = data.taxes
var values = data.row
var form = $$('form_invoice')
var row = undefined
@ -928,17 +927,31 @@ function set_product(values){
if (row == undefined){
values['cantidad'] = 1
values['importe'] = values['valor_unitario']
grid.add(values)
//~ grid.add(values)
} else {
values['cantidad'] = parseFloat(row.cantidad) + 1
values['descuento'] = parseFloat(row.descuento)
values['valor_unitario'] = parseFloat(row.valor_unitario)
var precio_final = values['valor_unitario'] - values['descuento']
values['importe'] = (precio_final * values['cantidad']).round(DECIMALES)
grid.updateItem(row.id, values)
//~ grid.updateItem(row.id, values)
}
form.setValues({search_product_id: '', search_product_name: ''}, true)
//~ Validate stock
if(values.inventario){
if(values.cantidad > values.existencia){
msg_error('No hay suficiente existencia de este producto')
return
}
}
if(row == undefined){
grid.add(values)
}else{
grid.updateItem(row.id, values)
}
for(var v of taxes){
var pt = table_pt.findOne(v)
if(pt === null){
@ -974,7 +987,13 @@ function grid_students_found_click(obj){
function search_product_by_key(key){
webix.ajax().get('/values/productokey', {'key': key}, {
var filters = {
opt: 'by_key',
key: key,
}
webix.ajax().get('/products', filters, {
error: function(text, data, xhr) {
msg_error('Error al consultar')
},
@ -983,12 +1002,27 @@ function search_product_by_key(key){
if (values.ok){
set_product(values)
} else {
msg = 'No se encontró un producto con la clave: ' + key
msg = 'No se encontró un producto con la clave<BR><BR>' + key
msg_error(msg)
}
}
})
//~ 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){
//~ set_product(values)
//~ } else {
//~ msg = 'No se encontró un producto con la clave: ' + key
//~ msg_error(msg)
//~ }
//~ }
//~ })
}
@ -1071,6 +1105,20 @@ function grid_details_before_edit_stop(state, editor){
}
cantidad = cantidad.round(DECIMALES)
//~ Validate stock
if(row['inventario']){
if(cantidad > row['existencia']){
msg = 'No hay suficiente existencia de este producto'
msg_error(msg)
grid.blockEvent()
state.value = state.old
grid.editCancel()
grid.unblockEvent()
return true
}
}
grid.blockEvent()
state.value = cantidad
grid.unblockEvent()

View File

@ -12,6 +12,7 @@ var nomina_controllers = {
$$('cmd_nomina_without_stamp').attachEvent('onItemClick', cmd_nomina_without_stamp_click)
$$('cmd_nomina_delete').attachEvent('onItemClick', cmd_nomina_delete_click)
$$('cmd_nomina_timbrar').attachEvent('onItemClick', cmd_nomina_timbrar_click)
$$('cmd_nomina_sat').attachEvent('onItemClick', cmd_nomina_sat_click)
$$('cmd_nomina_log').attachEvent('onItemClick', cmd_nomina_log_click)
$$('cmd_nomina_download').attachEvent('onItemClick', cmd_nomina_download_click)
$$('cmd_nomina_cancel').attachEvent('onItemClick', cmd_nomina_cancel_click)
@ -570,3 +571,41 @@ function grid_nomina_on_select_change(){
g.getColumnConfig('empleado').footer[0].text = webix.i18n.priceFormat(total)
g.refreshColumns()
}
function cmd_nomina_sat_click(){
var g = $$('grid_nomina')
if(g.count() == 0){
return
}
var row = g.getSelectedItem()
if (row == undefined){
msg_error('Selecciona un recibo de nómina')
return
}
if (row instanceof Array){
msg_error('Selecciona solo un recibo de nómina')
return
}
if(!row.uuid){
msg_error('La factura no esta timbrada, solo es posible consultar \
el estatus en el SAT de facturas timbradas')
return
}
var options = {opt: 'status_sat', id: row.id}
webix.ajax().get('/nomina', options, function(text, data){
var value = data.json()
if(value == 'Vigente'){
msg_ok(value)
}else if(value == 'uncancel'){
ask_invoice_uncancel(row.id)
}else{
msg_error(value)
}
})
}

View File

@ -1,4 +1,5 @@
var cfg_products = new Object()
var gis_admin = false
function products_default_config(){
@ -19,8 +20,25 @@ function products_default_config(){
$$('grid_products').showColumn('existencia')
}
show('cant_by_packing', values.chk_use_packing)
show('cmd_show_exists', values.chk_multi_stock)
}
})
webix.ajax().get('/users', {'opt': 'is_admin'}, {
error: function(text, data, xhr) {
msg = 'Error al consultar'
msg_error(msg)
},
success: function(text, data, xhr) {
var values = data.json()
gis_admin = values.is_admin
if(values.is_admin){
$$('cmd_add_inventory').show()
//~ $$('cmd_products_add').show()
}
}
})
}
@ -32,6 +50,12 @@ var products_controllers = {
$$("cmd_save_product").attachEvent("onItemClick", cmd_save_product_click)
$$("cmd_cancel_product").attachEvent("onItemClick", cmd_cancel_product_click)
$$("cmd_import_products").attachEvent("onItemClick", cmd_import_products_click)
$$("cmd_add_inventory").attachEvent("onItemClick", cmd_add_inventory_click)
$$("cmd_products_add").attachEvent("onItemClick", cmd_products_add_click)
$$('cmd_add_products_from_xml').attachEvent('onItemClick', cmd_add_products_from_xml_click)
$$('cmd_show_exists').attachEvent('onItemClick', cmd_show_exists_click)
$$('cmd_save_products_add').attachEvent('onItemClick', cmd_save_products_add_click)
$$('cmd_close_products_add').attachEvent('onItemClick', cmd_close_products_add_click)
$$("chk_automatica").attachEvent("onChange", chk_automatica_change)
$$("valor_unitario").attachEvent("onChange", valor_unitario_change)
$$('precio_con_impuestos').attachEvent('onChange', precio_con_impuestos_change)
@ -427,3 +451,536 @@ function up_products_upload_complete(response){
}
})
}
//~ Add inventory
function cmd_add_inventory_click(id, e, node){
var row = $$('grid_products').getSelectedItem()
if (row == undefined){
msg_error('Selecciona un Producto')
return
}
win_add_inventory.init()
$$('txt_add_id').setValue(row.id)
$$('txt_add_key').setValue(row.clave)
$$('txt_add_unit').setValue(row.unidad)
$$('txt_add_description').setValue(row.descripcion)
$$('lst_warehouses').getList().load('/warehouse?opt=for_select')
$$('win_add_inventory').show()
}
//~ Show details inventory
function cmd_show_exists_click(id, e, node){
var row = $$('grid_products').getSelectedItem()
if (row == undefined){
msg_error('Selecciona un Producto')
return
}
win_show_exists.init()
$$('txt_id_product').setValue(row.id)
$$('grid_warehouse_exists').load('warehouseproduct?opt=by_product&id=' + row.id)
if(gis_admin){
$$('lst_warehouse_target').getList().load('/warehouse?opt=for_select')
}else{
$$('lbl_title_move').hide()
$$('txt_cant_to_move').hide()
$$('lst_warehouse_target').hide()
$$('cmd_warehouse_move').hide()
}
$$('win_show_exists').show()
}
function cmd_warehouse_move_click(id, e, node){
var id_product = $$('txt_id_product').getValue()
var row = $$('grid_warehouse_exists').getSelectedItem()
if (row == undefined){
msg_error('Selecciona un Almacen origen')
return
}
var warehouse_source = row.id
var cant_to_move = $$('txt_cant_to_move').getValue()
var warehouse_target = $$('lst_warehouse_target').getValue()
if(!cant_to_move){
msg_error('La cantidad no puede ser cero')
return
}
if(cant_to_move > row.exists){
msg_error('La cantidad a mover no puede ser mayor a la existencia')
return
}
if (warehouse_target == ''){
msg_error('Selecciona un Almacen destino')
return
}
if (warehouse_source == warehouse_target){
msg_error('Los almacenes origen y destino deben ser diferentes')
return
}
var values = {
id_product: id_product,
cant: cant_to_move,
source: warehouse_source,
target: warehouse_target
}
msg = '¿Estás seguro de hacer este movimiento?'
webix.confirm({
title: 'Movimiento de Almacen',
ok: 'Si',
cancel: 'No',
type: 'confirm-error',
text: msg,
callback:function(result){
if(result){
_warehouse_movement(values)
}
}
})
}
function _warehouse_movement(args){
var values = {
opt: 'warehouse_movement',
values: args,
}
webix.ajax().sync().post('inventoryentries', values, {
error:function(text, data, XmlHttpRequest){
msg = 'Ocurrio un error, consulta a soporte técnico'
msg_error(msg)
},
success:function(text, data, XmlHttpRequest){
var values = data.json();
if (values.ok) {
$$('txt_cant_to_move').setValue(0)
$$('lst_warehouse_target').setValue('')
$$('grid_warehouse_exists').load('warehouseproduct?opt=by_product&id=' + args.id_product)
$$('grid_warehouse_exists').clearSelection()
msg_ok('Movimiento realizado correctamente')
}else{
msg_error(values.msg)
}
}
})
}
function cmd_win_show_exists_close_click(id, e, node){
$$('win_show_exists').close()
}
//~ Add products
function cmd_products_add_click(id, e, node){
$$("multi_products").setValue("product_add")
cfg_products['partner_id'] = 0
cfg_products['partner_rfc'] = ''
}
//~ Close add products
function cmd_close_products_add_click(id, e, node){
var grid = $$('grid_partner_products')
$$('multi_invoices').setValue('products_home')
$$('lbl_partner').setValue('')
grid.clearAll()
}
function _add_entries_inventory(data){
var grid = $$('grid_partner_products')
var values = {
opt: 'create',
values: data,
}
webix.ajax().sync().post('inventoryentries', values, {
error:function(text, data, XmlHttpRequest){
msg = 'Ocurrio un error, consulta a soporte técnico'
msg_error(msg)
},
success:function(text, data, XmlHttpRequest){
var values = data.json();
if (values.ok) {
get_products()
$$('multi_invoices').setValue('products_home')
$$('lbl_partner').setValue('')
grid.clearAll()
}else{
msg_error(values.msg)
}
}
})
}
//~ Save add products
function cmd_save_products_add_click(id, e, node){
var grid = $$('grid_partner_products')
var count = 0
var products = []
var validate_import = false
var validate_cant = false
grid.eachRow(function(row){
var r = grid.getItem(row)
if(r.select){
var p = {}
count += 1
p.id_product = r.id_product
p.key = r.key
p.key_sat = r.key_sat1
p.description = r.description1
p.unit = r.unit
p.unit_value = r.unit_value1
p.cant = r.cant1
products.push(p)
if(p.unit_value < parseFloat(r.unit_value)){
validate_import = true
}
if(p.cant > r.cant){
validate_cant = true
}
}
})
if(!count){
msg = 'Selecciona al menos un registro'
msg_error(msg)
return
}
if(validate_import){
msg = 'El Valor Unitario no puede ser menor al Valor Unitario del proveedor'
msg_error(msg)
return
}
if(validate_cant){
msg = 'La Cantidad no puede ser mayor a la Cantidad del proveedor'
msg_error(msg)
return
}
var data = {
partner: cfg_products['partner'],
products: products,
}
msg = '¿Estás seguro de ingresar estos productos? <br/><br/>\
Esta acción no se puede deshacer.'
webix.confirm({
title: 'Agregar entrada',
ok: 'Si',
cancel: 'No',
type: 'confirm-error',
text: msg,
callback:function(result){
if(result){
_add_entries_inventory(data)
}
}
})
}
//~ Import from xml
function cmd_add_products_from_xml_click(){
win_add_products_from_xml.init()
$$('win_add_products_from_xml').show()
}
//~ Upload XML
function cmd_upload_products_from_xml_click(){
var form = $$('form_upload_products_from_xml')
var values = form.getValues()
var list = $$('lst_up_products_from_xml')
var upload = $$('up_products_from_xml')
if(!list.count()){
$$('win_add_products_from_xml').close()
return
}
if(list.count() > 1){
msg = 'Selecciona solo un archivo'
msg_error(msg)
return
}
var template = upload.files.getItem(upload.files.getFirstId())
if(template.type.toLowerCase() != 'xml'){
msg = 'Archivo inválido.\n\nSe requiere un archivo XML'
msg_error(msg)
return
}
msg = '¿Estás seguro de importar este archivo? <br/><br/>\
Si hay datos previos seran reemplazados.'
webix.confirm({
title: 'Importar Productos',
ok: 'Si',
cancel: 'No',
type: 'confirm-error',
text: msg,
callback:function(result){
if(result){
upload.send()
}
}
})
}
function up_products_from_xml_upload_complete(response){
if(response.status != 'server'){
msg = 'Ocurrio un error al subir el archivo'
msg_error(msg)
return
}
$$('win_add_products_from_xml').close()
if(response.error){
msg_error(response.error)
return
}
var grid = $$('grid_partner_products')
var data = response.data
cfg_products['partner'] = data.emisor
//~ cfg_products['xml'] = data.xml
var html = '<span class="webix_icon fa-user"></span><span class="lbl_partner">'
html += data.emisor.nombre + ' (' + data.emisor.rfc + ')</span>'
$$('lbl_partner').setValue(html)
grid.clearAll()
grid.parse(data.conceptos, 'json')
grid.refresh()
}
function get_partner_product(grid, row){
grid.refresh(row.id)
var partner_id = cfg_products['partner'].id
var filters = {
opt: 'product',
partner: cfg_products['partner'],
product_key: row.key,
}
if(!partner_id){
msg = 'El Proveedor no esta dado de alta'
msg_ok(msg)
return
}
webix.ajax().get('/partnerproducts', filters, {
error: function(text, data, xhr) {
msg_error('Ocurrio un error, consulta a soporte técnico.')
},
success: function(text, data, xhr){
var values = data.json()
if(values.ok){
row['id_product'] = values.row.id_product
row['key_sat1'] = values.row.key_sat1
row['description1'] = values.row.description1
row['unit_value1'] = values.row.unit_value1
grid.refresh(row.id)
}else{
msg_error(values.msg)
}
}
})
}
function grid_partner_products_select(row_id, state){
var grid = $$('grid_partner_products')
var row = grid.getItem(row_id)
if(state){
row['key_sat1'] = row.key_sat
row['description1'] = row.description
row['cant1'] = row.cant
row['unit_value1'] = 0.0
get_partner_product(grid, row)
}else{
row['key_sat1'] = ''
row['description1'] = ''
row['cant1'] = ''
grid.refresh(row_id)
}
}
function cmd_add_inventory_cancel_click(id, e, node){
$$('win_add_inventory').close()
}
function cmd_add_inventory_save_click(id, e, node){
var product_id = $$('txt_add_id').getValue()
//~ var product_key = $$('txt_add_key').getValue()
var new_cant = $$('txt_new_cant').getValue()
var warehouse = $$('lst_warehouses').getValue()
if(new_cant<=0) {
msg = 'La cantidad no puede ser cero'
msg_error(msg)
return
}
if($$('lst_warehouses').isVisible()){
if(!warehouse){
msg = 'Selecciona un almacen'
msg_error(msg)
return
}
}
msg = '¿Estas seguro de guardar esta entrada? <br/><br/>\
Esta acción no se puede deshacer'
webix.confirm({
title: 'Agregar entrada',
ok: 'Si',
cancel: 'No',
type: 'confirm-error',
text: msg,
callback:function(result){
if(result){
add_product_stock(product_id, new_cant, warehouse)
}
}
})
}
function add_product_stock(product_id, new_cant, warehouse){
var data = {
product_id: product_id,
cant: new_cant,
warehouse: warehouse,
}
_add_entries_inventory_manual(product_id, data)
$$('win_add_inventory').close()
}
function _add_entries_inventory_manual(row_id, data){
var grid = $$('grid_products')
var values = {
opt: 'create_manual',
values: data,
}
webix.ajax().sync().post('inventoryentries', values, {
error:function(text, data, XmlHttpRequest){
msg = 'Ocurrio un error, consulta a soporte técnico'
msg_error(msg)
},
success:function(text, data, XmlHttpRequest){
var values = data.json();
if (values.ok) {
grid.updateItem(row_id, values.row)
grid.refresh()
}else{
msg_error(values.msg)
}
}
})
}
function cmd_adjust_stock_click(id, e, node){
var id_product = $$('txt_id_product').getValue()
var row = $$('grid_warehouse_exists').getSelectedItem()
if (row == undefined){
msg_error('Selecciona un Almacen origen')
return
}
var warehouse_source = row.id
var cant_to_adjust = $$('txt_cant_to_adjust').getValue()
if(cant_to_adjust == 0){
msg_error('La cantidad a ajustar no puede ser cero')
return
}
if(cant_to_adjust > row.exists){
msg_error('La cantidad a ajustar no puede ser mayor a la existencia')
return
}
var values = {
id_product: id_product,
cant: cant_to_adjust,
storage: warehouse_source,
}
msg = '¿Estás seguro de hacer este ajuste?'
webix.confirm({
title: 'Ajuste de Almacen',
ok: 'Si',
cancel: 'No',
type: 'confirm-error',
text: msg,
callback:function(result){
if(result){
_adjust_stock(values)
}
}
})
}
function _adjust_stock(args){
var grid = $$('grid_products')
var row = grid.getSelectedItem()
var values = {
opt: 'adjust_stock',
values: args,
}
webix.ajax().sync().post('warehouseproduct', values, {
error:function(text, data, XmlHttpRequest){
msg = 'Ocurrio un error, consulta a soporte técnico'
msg_error(msg)
},
success:function(text, data, XmlHttpRequest){
var values = data.json();
if (values.ok) {
$$('txt_cant_to_adjust').setValue(0)
$$('grid_warehouse_exists').load('warehouseproduct?opt=by_product&id=' + args.id_product)
$$('grid_warehouse_exists').clearSelection()
grid.updateItem(row['id'], values.row)
grid.refresh()
msg_ok('Ajuste realizado correctamente')
}else{
msg_error(values.msg)
}
}
})
}

View File

@ -7,6 +7,7 @@ var last_forma_pago = ''
var tickets_controllers = {
init: function(){
$$('cmd_nuevo_ticket').attachEvent('onItemClick', cmd_nuevo_ticket_click)
$$('cmd_ticket_from_ticket').attachEvent('onItemClick', cmd_ticket_from_ticket_click)
$$('cmd_ticket_to_invoice').attachEvent('onItemClick', cmd_ticket_to_invoice_click)
$$('cmd_ticket_report_pdf').attachEvent('onItemClick', cmd_ticket_report_pdf_click)
$$('cmd_ticket_report_xls').attachEvent('onItemClick', cmd_ticket_report_xls_click)
@ -183,6 +184,25 @@ function cmd_nuevo_ticket_click(){
}
function cmd_ticket_from_ticket_click(){
var grid = $$('grid_tickets')
if(grid.count() == 0){
return
}
var row = grid.getSelectedItem()
if (row == undefined){
msg_error('Selecciona un ticket')
return
}
configuracion_inicial_nuevo_ticket()
$$('multi_tickets').setValue('tickets_new')
$$('grid_tdetails').load('ticketsdetails?opt=by_ticket_id&id=' + row.id)
}
function cmd_ticket_to_invoice_click(){
configuracion_inicial_ticket_to_invoice()
$$('multi_tickets').setValue('tickets_invoice')
@ -342,22 +362,43 @@ function agregar_producto(values){
producto['valor_unitario'] = calcular_precio_con_impuestos(
parseFloat(producto['valor_unitario']), taxes)
producto['importe'] = producto['valor_unitario']
var id = grid.add(producto, 0)
edit_cant(id)
//~ var id = grid.add(producto, 0)
//~ edit_cant(id)
}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)
//~ grid.updateItem(row.id, producto)
}
form.setValues({tsearch_product_key: '', tsearch_product_name: ''}, true)
//~ Validate stock
if(producto.inventario){
if(producto.cantidad > producto.existencia){
msg_error('No hay suficiente existencia de este producto')
return
}
}
if(row == undefined){
var id = grid.add(producto, 0)
edit_cant(id)
}else{
grid.updateItem(row.id, producto)
}
}
function buscar_producto_key(key){
webix.ajax().get('/values/productokey', {'key': key}, {
var filters = {
opt: 'by_key',
key: key,
}
webix.ajax().get('/products', filters, {
error: function(text, data, xhr) {
msg_error('Error al consultar')
},
@ -371,6 +412,20 @@ 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)
//~ }
//~ }
//~ })
}
@ -417,6 +472,20 @@ function grid_tickets_details_before_edit_stop(state, editor){
grid.unblockEvent()
return true
}
//~ Validate stock
if(row['inventario']){
if(cantidad > row['existencia']){
msg = 'No hay suficiente existencia de este producto'
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'])
}
@ -643,22 +712,30 @@ function grid_tickets_invoice_double_click(id, e, node){
function cmd_move_tickets_right_click(){
$$('grid_tickets_active').eachRow(
function(row){
this.copy(row, -1, $$('grid_tickets_invoice'))
}
)
$$('grid_tickets_active').clearAll()
var source = $$('grid_tickets_active')
var target = $$('grid_tickets_invoice')
_move_tickets(source, target)
}
function cmd_move_tickets_left_click(){
$$('grid_tickets_invoice').eachRow(
function(row){
this.copy(row, -1, $$('grid_tickets_active'))
}
)
$$('grid_tickets_invoice').clearAll()
var target = $$('grid_tickets_active')
var source = $$('grid_tickets_invoice')
_move_tickets(source, target)
}
function _move_tickets(source, target){
var rows = source.getSelectedItem()
if(rows == undefined){
source.eachRow(
function(row){
this.copy(row, -1, target)
}
)
source.clearAll()
}
}

View File

@ -1,5 +1,5 @@
//~ Empresa Libre
//~ Copyright (C) 2016-2018 Mauricio Baeza Servin (web@correolibre.net)
//~ Copyright (C) 2016-2021 Mauricio Baeza Servin (public@correolibre.net)
//~
//~ This program is free software: you can redistribute it and/or modify
//~ it under the terms of the GNU General Public License as published by
@ -287,7 +287,7 @@ var emisor_certificado = [
{view: 'form', id: 'form_upload', rows: [
{cols: [{},
{view: 'uploader', id: 'up_cert', autosend: false, link: 'lst_cert',
value: 'Seleccionar certificado'}, {}]},
value: 'Seleccionar certificado (CER y KEY)'}, {}]},
{cols: [{},
{view: 'list', id: 'lst_cert', name: 'certificado',
type: 'uploader', autoheight:true, borderless: true}, {}]},
@ -762,6 +762,31 @@ var options_admin_partners = [
]
var grid_warehouse_cols = [
{id: 'id', header: 'ID', hidden: true},
{id: 'delete', header: '', width: 30, css: 'delete'},
{id: 'name', header: 'Nombre', fillspace: 1},
]
var grid_warehouse = {
view: 'datatable',
id: 'grid_warehouse',
select: 'row',
adjust: true,
headermenu: true,
footer: true,
columns: grid_warehouse_cols,
on:{
'data->onStoreUpdated':function(){
this.data.each(function(obj, i){
obj.delete = '-'
})
}
},
}
var options_admin_products = [
{maxHeight: 20},
{cols: [{view: 'checkbox', id: 'chk_config_cuenta_predial', labelWidth: 15,
@ -772,8 +797,23 @@ var options_admin_products = [
labelRight: 'Mostrar precio con impuestos'}]},
{cols: [{view: 'checkbox', id: 'chk_llevar_inventario', labelWidth: 15,
labelRight: 'Mostrar inventario'}]},
{cols: [{view: 'checkbox', id: 'chk_use_packing', labelWidth: 15,
labelRight: 'Usar empaques'}]},
{cols: [{maxWidth: 30}, {view: 'checkbox', id: 'chk_multi_stock', labelWidth: 15,
labelRight: 'Multialmacen', disabled: true}]},
{cols: [{maxWidth: 30}, {rows: [
{template: 'Agregar Almacen', type: 'section', id: 'template_add_warehouse'},
{cols: [
{view: 'text', id: 'txt_add_warehouse', label: 'Nombre: ',
labelPosition: 'left', required: true},
{view: 'button', id: 'cmd_add_warehouse', label: 'Agregar',
autowidth: true, type: 'iconButton', icon: 'plus'},
]},
{template: 'Almacenes', type: 'section'},
grid_warehouse,
]}, {},
]}
//~ {cols: [{view: 'checkbox', id: 'chk_use_packing', labelWidth: 15,
//~ labelRight: 'Usar empaques', hidden: true}]},
]
@ -838,6 +878,8 @@ var controls_add_sucursal = [{cols: [
label: 'Serie Facturas: ', labelPosition: 'top', required: true, options: []},
{view: 'text', id: 'txt_sucursal_add_ticket', name: 'sucursal_add_ticket',
required: true, label: 'Serie Tickets: ', labelPosition: 'top'},
{view: 'richselect', id: 'lst_sucursal_warehouse', name: 'sucursal_warehouse',
label: 'Almacen: ', labelPosition: 'top', required: false, options: [], hidden: true},
{view: 'button', id: 'cmd_add_sucursal', label: 'Agregar',
autowidth: true, type: 'iconButton', icon: 'plus'},
{maxWidth: 20},
@ -849,7 +891,9 @@ var grid_sucursales_cols = [
{id: 'delete', header: '', width: 30, css: 'delete'},
{id: 'name', header: 'Nombre', fillspace: 1},
{id: 'serie_invoice', header: 'Serie Facturas', fillspace: 1},
{id: 'serie_tickets', header: 'Serie Tickets', fillspace: 1},]
{id: 'serie_tickets', header: 'Serie Tickets', fillspace: 1},
{id: 'warehouse', header: 'Almacen', fillspace: 1, hidden: true},
]
var grid_sucursales = {
@ -857,7 +901,7 @@ var grid_sucursales = {
id: 'grid_sucursales',
select: 'row',
adjust: true,
headermenu: true,
headermenu: false,
footer: true,
columns: grid_sucursales_cols,
on:{
@ -1296,12 +1340,14 @@ var grid_usuarios_cols = [
hidden: true},
{id: 'ultimo_ingreso', header: 'Ultimo Ingreso', fillspace: 1,
hidden: true},
{id: 'in_branch', header: 'En Sucursal', template: '{common.checkbox()}',
editor: 'checkbox', adjust: 'header', hidden: true},
{id: 'es_activo', header: 'Activo', template: '{common.checkbox()}',
editor: 'checkbox', adjust: 'header'},
{id: 'es_admin', header: 'Es Admin', template: '{common.checkbox()}',
editor: 'checkbox', adjust: 'header'},
{id: 'es_superusuario', header: 'Es SU', template: '{common.checkbox()}',
editor: 'checkbox', adjust: 'header'},
editor: 'checkbox', adjust: 'header', hidden: true},
]
@ -1310,7 +1356,7 @@ var grid_usuarios = {
id: 'grid_usuarios',
select: 'row',
adjust: true,
headermenu: true,
headermenu: false,
footer: true,
columns: grid_usuarios_cols,
on:{
@ -1568,3 +1614,35 @@ var admin_ui_leyendas_fiscales = {
},
}
var body_set_branch_user = {rows: [{minHeight: 10},
{view: 'text', id: 'txt_user_id', readonly: true, hidden: true},
//~ {view: 'text', id: 'txt_user_name', label: 'Usuario', readonly: true},
{view: 'richselect', id: 'lst_branchs', name: 'lst_branchs',
label: 'Sucursales: ', required: true, options: []},
{minHeight: 10},
{cols: [{},
{view: 'button', id: 'cmd_set_branch_user_save', label: 'Guardar'}, {},
{view: 'button', id: 'cmd_set_branch_user_cancel', label: 'Cancelar', type: 'danger'}, {}
]},
{minHeight: 20},
]}
var win_set_branch_user = {
init: function(){
webix.ui({
view: 'window',
id: 'win_set_branch_user',
width: 400,
modal: true,
position: 'center',
head: 'Asignar Sucursal',
body: body_set_branch_user,
})
$$('cmd_set_branch_user_save').attachEvent('onItemClick', cmd_set_branch_user_save_click)
$$('cmd_set_branch_user_cancel').attachEvent('onItemClick', cmd_set_branch_user_cancel_click)
}
}

View File

@ -359,6 +359,8 @@ var grid_details_cols = [
format: webix.i18n.priceFormat, css: 'right'},
{id: "importe", header:{text: 'Importe', css: 'center'}, width: 150,
format: webix.i18n.priceFormat, css: 'right'},
{id: "inventario", hidden: true},
{id: "existencia", hidden: true},
]

View File

@ -59,9 +59,9 @@ var menu_user = {
}
var link_blog = "<a class='link_default' target='_blank' href='https://blog.empresalibre.mx'>Blog</a>";
var link_forum = "<a class='link_default' target='_blank' href='https://gitlab.com/mauriciobaeza/empresa-libre/issues'>Foro</a>";
var link_doc = "<a class='link_default' target='_blank' href='https://doc.empresalibre.mx'>Doc</a>";
var link_blog = "<a class='link_default' target='_blank' href='https://empresalibre.net/blog'>Blog</a>";
var link_forum = "<a class='link_default' target='_blank' href='https://git.cuates.net/elmau/empresa-libre/issues'>Foro</a>";
var link_doc = "<a class='link_default' target='_blank' href='https://empresalibre.net/docs'>Doc</a>";
var ui_main = {

View File

@ -10,6 +10,12 @@ var toolbar_products = [
{},
{view: 'button', id: 'cmd_import_products', label: 'Importar',
type: 'iconButton', autowidth: true, icon: 'upload'},
{view: "button", id: "cmd_add_inventory", label: "Altas", hidden: true,
type: "iconButton", autowidth: true, icon: "plus"},
{view: "button", id: "cmd_products_add", label: "Altas CFDI", hidden: true,
type: "iconButton", autowidth: true, icon: "plus"},
{view: 'button', id: 'cmd_show_exists', label: 'Existencias', hidden: true,
type: 'iconButton', autowidth: true, icon: 'table'},
]
@ -158,7 +164,7 @@ var controls_generals = [
label: 'Inventario', labelAlign: 'right', labelWidth: 130},
{view: 'counter', id: 'txt_existencia', name: 'existencia',
hidden: true, label: 'Existencia', step: 5, value: 0, min: 0,
disabled: true},
disabled: true, readonly: true},
{view: 'counter', id: 'txt_minimo', name: 'minimo', hidden: true,
label: 'Mínimo', step: 5, value: 0, min: 0, disabled: true},
{},]},
@ -176,8 +182,8 @@ var controls_products = [
],
},
{rows: [
{ template:"", type: "section" },
{ margin: 10, cols: [{},
{template: "", type: "section"},
{margin: 10, cols: [{},
{view: "button", id: "cmd_save_product", label: "Guardar" , type: "form", autowidth: true, align:"center"},
{view: "button", id: "cmd_cancel_product", label: "Cancelar" , type: "danger", autowidth: true, align:"center"},
{}]
@ -202,6 +208,129 @@ var form_product = {
}
var toolbar_products_add = {view: 'toolbar', elements: [{},
{view: 'button', id: 'cmd_add_products_from_xml', label: 'Desde XML',
type: 'iconButton', autowidth: true, icon: 'upload'},
{}]}
var rows_pro_add_partner = [
{view: 'fieldset', label: 'Buscar Proveedor', body: {rows: [
{cols: [
{view: 'search', id: 'search_partner_id', label: 'por Clave',
labelPosition: 'top', maxWidth: 200, placeholder: 'Captura la clave'},
{view: 'search', id: 'search_partner_name', label: 'por Nombre o RFC',
labelPosition: 'top', placeholder: 'Captura al menos tres letras'},
]},
{cols: [
{view: 'label', id: 'lbl_partner_title', label: 'Seleccionado: ', autowidth: true},
{view: 'label', id: 'lbl_partner', label: 'Ninguno'},
]}
]}},
]
var grid_partner_products_cols = [
{id: 'select', header: '', template:'{common.checkbox()}', width:35},
{id: 'key', header: {text: 'Clave', css: 'center'}, width: 100,
adjust: 'data'},
{id: 'key_sat', header:{text: 'Clave SAT', css: 'center'}, width: 100,
adjust: 'data'},
{id: 'description', header:{text: 'Descripción', css: 'center'},
fillspace: true},
{id: "pedimento", header: 'Pedimento', editor: 'text', hidden: true},
{id: 'unit', header:{text: 'Unidad', css: 'center'}, width: 100,
adjust: 'data'},
{id: 'unit_value', header:{text: 'Valor Unitario', css: 'center'},
width: 100, format: format_currency, css: 'right'},
{id: 'cant', header: {text: 'Cantidad', css: 'center'}, width: 50,
format: webix.i18n.numberFormat, css: 'right'},
{id: 'separate', header: '', width: 25},
{id: 'id_product', header: '', hidden: true},
{id: 'key1', header:{text: 'Clave', css: 'center'}, width: 100,
adjust: true, editor: 'text', hidden: true},
{id: 'key_sat1', header:{text: 'Clave SAT', css: 'center'}, width: 100,
adjust: true, editor: 'text'},
{id: 'description1', header:{text: 'Descripción', css: 'center'},
fillspace: true, editor: 'popup'},
{id: 'unit_value1', header:{text: 'Valor Unitario', css: 'center'},
width: 100, format: format_currency, css: 'right', editor: 'text'},
{id: 'cant1', header: {text: 'Cantidad', css: 'center'}, width: 50,
format: webix.i18n.numberFormat, css: 'right', editor: 'text'},
]
var grid_partner_products = {
view: 'datatable',
id: 'grid_partner_products',
select: 'row',
adjust: true,
autoheight: true,
editable: true,
columns: grid_partner_products_cols,
data: [],
fixedRowHeight: false,
on:{
onCheck:function(rowId, colId, state){
grid_partner_products_select(rowId, state);
}
}
}
var controls_products_add = [
{minHeight: 10, maxHeight: 10},
toolbar_products_add,
{minHeight: 10, maxHeight: 10},
{cols: [
{rows: rows_pro_add_partner},
{maxWidth: 10},
{},
]},
{view: 'label', label: 'Detalle', height: 30, align: 'left'},
{cols: [
grid_partner_products,
//~ grid_products_add,
]},
{rows: [
{template:"", type: "section" },
{margin: 10, cols: [{},
{view: 'button', id: 'cmd_save_products_add', label: 'Guardar',
autowidth: true, align: 'center'},
{},
{view: 'button', id: 'cmd_close_products_add', label: 'Cancelar',
type: 'danger', autowidth: true, align: 'center'}
]
},
]}
]
var controls_form_products_add = [
{
view: 'tabview',
id: 'tv_invoice',
animate: true,
cells: [
{id: 'Altas a inventario', rows: controls_products_add},
]
},
]
var form_products_add = {
type: 'space',
responsive: true,
cols: [{
view: 'form',
id: 'form_products_add',
complexData: true,
scroll: true,
elements: controls_form_products_add,
}]
}
var multi_products = {
id: "multi_products",
animate: true,
@ -210,7 +339,8 @@ var multi_products = {
{view:"toolbar", elements: toolbar_products},
grid_products,
]},
{id: "product_new", rows:[form_product]}
{id: "product_new", rows:[form_product]},
{id: "product_add", rows:[form_products_add]}
],
}
@ -255,3 +385,151 @@ var win_import_products = {
$$('up_products').attachEvent('onUploadComplete', up_products_upload_complete)
}
}
var body_add_products_from_xml = {rows: [
{view: 'form', id: 'form_upload_products_from_xml', rows: [
{cols: [{},
{view: 'uploader', id: 'up_products_from_xml', autosend: false,
link: 'lst_up_products_from_xml', value: 'Seleccionar Archivo',
upload: '/files/productsadd'}, {}]},
{cols: [
{view: 'list', id: 'lst_up_products_from_xml', type: 'uploader',
autoheight: true, borderless: true}]},
{cols: [{}, {view: 'button', id: 'cmd_upload_products_from_xml',
label: 'Cargar Productos'}, {}]},
]},
]}
var win_add_products_from_xml = {
init: function(){
webix.ui({
view: 'window',
id: 'win_add_products_from_xml',
width: 400,
modal: true,
position: 'center',
head: 'Agregar Productos desde XML',
body: body_add_products_from_xml,
})
$$('cmd_upload_products_from_xml').attachEvent('onItemClick', cmd_upload_products_from_xml_click)
$$('up_products_from_xml').attachEvent('onUploadComplete', up_products_from_xml_upload_complete)
}
}
var body_add_inventory = {rows: [{minHeight: 10},
{view: 'text', id: 'txt_add_id', readonly: true, hidden: true},
{cols: [
{view: 'text', id: 'txt_add_key', label: 'Clave',
labelPosition: 'top', readonly: true},
{view: 'text', id: 'txt_add_unit', label: 'Unidad',
labelPosition: 'top', readonly: true},
]},
{view: 'textarea', id: 'txt_add_description', height: 100,
label: 'Descripción', readonly: true, labelPosition: 'top'},
{minHeight: 10},
{cols: [
{view: 'counter', id: 'txt_new_cant', label: 'Cantidad a agregar',
labelWidth: 'auto', step: 1, value: 0, min: 0.01},
{view: 'richselect', id: 'lst_warehouses', label: 'Almacen: ',
labelPosition: 'left', required: false, options: [], hidden: false},
]},
{minHeight: 20},
{cols: [{},
{view: 'button', id: 'cmd_add_inventory_save', label: 'Guardar'}, {},
{view: 'button', id: 'cmd_add_inventory_cancel', label: 'Cancelar', type: 'danger'}, {}
]},
{minHeight: 20},
]}
var win_add_inventory = {
init: function(){
webix.ui({
view: 'window',
id: 'win_add_inventory',
width: 600,
modal: true,
position: 'center',
head: 'Agregar entrada manualmente',
body: body_add_inventory,
})
$$('cmd_add_inventory_save').attachEvent('onItemClick', cmd_add_inventory_save_click)
$$('cmd_add_inventory_cancel').attachEvent('onItemClick', cmd_add_inventory_cancel_click)
}
}
var grid_warehouse_exists_cols = [
{ id: 'id', header: 'ID', width: 75, hidden: true},
{ id: 'warehouse', header: ['Almacen'], fillspace:true,
footer: {text: 'Total', css: 'right'}},
{ id: 'exists', header: ['Existencia'], width: 100, sort: 'int',
format: webix.i18n.numberFormat, css: 'right',
footer: {content: 'summColumn', css: 'right'} },
]
var grid_warehouse_exists = {
view: 'datatable',
id: 'grid_warehouse_exists',
adjust: true,
autoheight: true,
select: 'row',
footer: true,
columns: grid_warehouse_exists_cols,
}
var body_win_show_exists = {rows: [{maxHeight: 10, minHeight: 10},
{minWidth: 500},
{cols: [
{maxWidth: 10},
{view: 'text', id: 'txt_id_product', readonly: true, hidden: true},
grid_warehouse_exists,
{maxWidth: 10}
]},
{maxHeight: 10},
{cols: [{maxWidth: 10},
{view: 'label', id: 'lbl_title_move', label: 'Primero, selecciona el almacen origen en la tabla superior.'},
]},
{cols: [{maxWidth: 10},
{view: 'counter', id: 'txt_cant_to_move', label: 'Cantidad a mover:',
labelPosition: 'top', step: 1, value: 0, min: 0.01},
{view: 'richselect', id: 'lst_warehouse_target', label: 'Almacen destino: ',
labelPosition: 'top', required: false, options: []}, {maxWidth: 10},
{view: 'button', id: 'cmd_warehouse_move', label: 'Mover', maxWidth: 100},
{maxWidth: 10}]},
{minHeight: 25, maxHeight: 25},
{template: 'Ajuste de almacen', type: 'section'},
{cols: [{maxWidth: 10},
{view: 'counter', id: 'txt_cant_to_adjust', label: 'Cantidad a ajustar:',
labelPosition: 'top', step: 1, value: 0, min: -1000000},
{view: 'button', id: 'cmd_adjust_stock', label: 'Ajustar', maxWidth: 100},
{maxWidth: 10}]},
{maxHeight: 20, minHeight: 20},
{cols: [{},
{view: 'button', id: 'cmd_win_show_exists_close', label: 'Cerrar', type: 'danger'},
{}]},
{maxHeight: 20, minHeight: 20},
]}
var win_show_exists = {
init: function(){
webix.ui({
view: 'window',
id: 'win_show_exists',
width: 500,
modal: true,
position: 'center',
head: 'Existencia por Almacen',
body: body_win_show_exists,
})
$$('cmd_win_show_exists_close').attachEvent('onItemClick', cmd_win_show_exists_close_click)
$$('cmd_warehouse_move').attachEvent('onItemClick', cmd_warehouse_move_click)
$$('cmd_adjust_stock').attachEvent('onItemClick', cmd_adjust_stock_click)
}
}

View File

@ -3,6 +3,8 @@
var toolbar_tickets = [
{view: 'button', id: 'cmd_nuevo_ticket', label: 'Nuevo', type: 'iconButton',
autowidth: true, icon: 'plus'},
{view: 'button', id: 'cmd_ticket_from_ticket', label: 'Regenerar',
type: 'iconButton', autowidth: true, icon: 'pencil'},
{view: 'button', id: 'cmd_ticket_to_invoice', label: 'Facturar',
type: 'iconButton', autowidth: true, icon: 'file-code-o'},
{},
@ -145,6 +147,8 @@ var grid_tdetails_cols = [
{id: "importe", header:{text: 'Importe', css: 'center'}, width: 150,
format: webix.i18n.priceFormat, css: 'right',
footer: {content: 'summColumn', css: 'right_footer2'}},
{id: "inventario", hidden: true},
{id: "existencia", hidden: true},
]
@ -302,6 +306,7 @@ var grid_tickets_active = {
view: 'datatable',
id: 'grid_tickets_active',
select: 'row',
multiselect: true,
adjust: true,
footer: true,
drag: true,
@ -337,6 +342,7 @@ var grid_tickets_invoice = {
view: 'datatable',
id: 'grid_tickets_invoice',
select: 'row',
multiselect: true,
adjust: true,
footer: true,
drag: true,
@ -478,4 +484,4 @@ var win_ticket_notes = {
})
$$('cmd_ticket_save_note').attachEvent('onItemClick', cmd_ticket_save_note_click)
}
}
}

View File

@ -15,6 +15,7 @@
<xsl:include href="pagos10.xslt"/>
<xsl:include href="divisas.xslt"/>
<xsl:include href="servicioconstruccion.xslt"/>
<xsl:include href="cartaporte20.xslt"/>
<!--
<xsl:include href="ecc11.xslt"/>

View File

@ -0,0 +1,615 @@
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:fn="http://www.w3.org/2005/xpath-functions" xmlns:cartaporte20="http://www.sat.gob.mx/CartaPorte20">
<xsl:template match="cartaporte20:CartaPorte">
<!--Manejador de nodos tipo CartaPorte-->
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@Version"/>
</xsl:call-template>
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@TranspInternac"/>
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@EntradaSalidaMerc"/>
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@PaisOrigenDestino"/>
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@ViaEntradaSalida"/>
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@TotalDistRec"/>
</xsl:call-template>
<!-- Iniciamos el manejo de los elementos hijo en la secuencia -->
<xsl:for-each select="./cartaporte20:Ubicaciones">
<xsl:apply-templates select="."/>
</xsl:for-each>
<xsl:for-each select="./cartaporte20:Mercancias">
<xsl:apply-templates select="."/>
</xsl:for-each>
<xsl:for-each select="./cartaporte20:FiguraTransporte">
<xsl:apply-templates select="."/>
</xsl:for-each>
</xsl:template>
<!-- Iniciamos el manejo de los elementos hijo en la secuencia Ubicaciones-->
<xsl:template match="cartaporte20:Ubicaciones">
<!-- Iniciamos el tratamiento de los atributos de Ubicacion-->
<xsl:for-each select="./cartaporte20:Ubicacion">
<xsl:apply-templates select="."/>
</xsl:for-each>
</xsl:template>
<!-- Iniciamos el manejo de los elementos hijo en la secuencia Ubicacion-->
<xsl:template match="cartaporte20:Ubicacion">
<!--Manejador de nodos tipo Ubicacion-->
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@TipoUbicacion"/>
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@IDUbicacion"/>
</xsl:call-template>
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@RFCRemitenteDestinatario"/>
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@NombreRemitenteDestinatario"/>
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@NumRegIdTrib"/>
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@ResidenciaFiscal"/>
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@NumEstacion"/>
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@NombreEstacion"/>
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@NavegacionTrafico"/>
</xsl:call-template>
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@FechaHoraSalidaLlegada"/>
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@TipoEstacion"/>
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@DistanciaRecorrida"/>
</xsl:call-template>
<!-- Iniciamos el tratamiento de los atributos de cartaporte20:Domicilio-->
<xsl:for-each select="./cartaporte20:Domicilio">
<!-- Iniciamos el manejo de los elementos hijo en la secuencia Domicilio-->
<!-- Iniciamos el manejo de los nodos dependientes -->
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@Calle"/>
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@NumeroExterior"/>
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@NumeroInterior"/>
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@Colonia"/>
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@Localidad"/>
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@Referencia"/>
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@Municipio"/>
</xsl:call-template>
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@Estado"/>
</xsl:call-template>
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@Pais"/>
</xsl:call-template>
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@CodigoPostal"/>
</xsl:call-template>
</xsl:for-each>
</xsl:template>
<!-- Iniciamos el manejo de los elementos hijo en la secuencia Origen-->
<xsl:template match="cartaporte20:Mercancias">
<!--Manejador de nodos tipo cartaporte20:Mercancias-->
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@PesoBrutoTotal"/>
</xsl:call-template>
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@UnidadPeso"/>
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@PesoNetoTotal"/>
</xsl:call-template>
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@NumTotalMercancias"/>
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@CargoPorTasacion"/>
</xsl:call-template>
<!-- Iniciamos el tratamiento de los atributos de cartaporte20:Mercancia-->
<xsl:for-each select="./cartaporte20:Mercancia">
<xsl:apply-templates select="."/>
</xsl:for-each>
<!-- Iniciamos el tratamiento de los atributos de cartaporte20:Autotransporte-->
<xsl:for-each select="./cartaporte20:Autotransporte">
<xsl:apply-templates select="."/>
</xsl:for-each>
<!-- Iniciamos el tratamiento de los atributos de cartaporte20:TransporteMaritimo-->
<xsl:for-each select="./cartaporte20:TransporteMaritimo">
<xsl:apply-templates select="."/>
</xsl:for-each>
<!-- Iniciamos el tratamiento de los atributos de cartaporte20:TransporteAereo-->
<xsl:for-each select="./cartaporte20:TransporteAereo">
<xsl:apply-templates select="."/>
</xsl:for-each>
<!-- Iniciamos el tratamiento de los atributos de cartaporte20:TransporteFerroviario-->
<xsl:for-each select="./cartaporte20:TransporteFerroviario">
<xsl:apply-templates select="."/>
</xsl:for-each>
</xsl:template>
<!-- Iniciamos el manejo de los elementos hijo en la secuencia Mercancia-->
<xsl:template match="cartaporte20:Mercancia">
<!--Manejador de nodos tipo cartaporte20:Mercancia-->
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@BienesTransp"/>
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@ClaveSTCC"/>
</xsl:call-template>
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@Descripcion"/>
</xsl:call-template>
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@Cantidad"/>
</xsl:call-template>
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@ClaveUnidad"/>
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@Unidad"/>
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@Dimensiones"/>
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@MaterialPeligroso"/>
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@CveMaterialPeligroso"/>
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@Embalaje"/>
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@DescripEmbalaje"/>
</xsl:call-template>
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@PesoEnKg"/>
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@ValorMercancia"/>
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@Moneda"/>
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@FraccionArancelaria"/>
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@UUIDComercioExt"/>
</xsl:call-template>
<!-- Iniciamos el tratamiento de los atributos de cartaporte20:Pedimentos-->
<xsl:for-each select="./cartaporte20:Pedimentos">
<xsl:apply-templates select="."/>
</xsl:for-each>
<!-- Iniciamos el tratamiento de los atributos de cartaporte20:GuiasIdentificacion-->
<xsl:for-each select="./cartaporte20:GuiasIdentificacion">
<xsl:apply-templates select="."/>
</xsl:for-each>
<!-- Iniciamos el tratamiento de los atributos de cartaporte20:CantidadTransporta-->
<xsl:for-each select="./cartaporte20:CantidadTransporta">
<xsl:apply-templates select="."/>
</xsl:for-each>
<!-- Iniciamos el tratamiento de los atributos de cartaporte20:DetalleMercancia-->
<xsl:for-each select="./cartaporte20:DetalleMercancia">
<xsl:apply-templates select="."/>
</xsl:for-each>
</xsl:template>
<!-- Iniciamos el manejo de los elementos hijo en la secuencia Pedimentos-->
<xsl:template match="cartaporte20:Pedimentos">
<!-- Iniciamos el manejo de los nodos dependientes -->
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@Pedimento"/>
</xsl:call-template>
</xsl:template>
<!-- Iniciamos el manejo de los elementos hijo en la secuencia GuiasIdentificacion-->
<xsl:template match="cartaporte20:GuiasIdentificacion">
<!-- Iniciamos el manejo de los nodos dependientes -->
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@NumeroGuiaIdentificacion"/>
</xsl:call-template>
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@DescripGuiaIdentificacion"/>
</xsl:call-template>
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@PesoGuiaIdentificacion"/>
</xsl:call-template>
</xsl:template>
<!-- Iniciamos el manejo de los elementos hijo en la secuencia CantidadTransporta-->
<xsl:template match="cartaporte20:CantidadTransporta">
<!-- Iniciamos el manejo de los nodos dependientes -->
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@Cantidad"/>
</xsl:call-template>
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@IDOrigen"/>
</xsl:call-template>
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@IDDestino"/>
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@CvesTransporte"/>
</xsl:call-template>
</xsl:template>
<!-- Iniciamos el manejo de los elementos hijo en la secuencia DetalleMercancia-->
<xsl:template match="cartaporte20:DetalleMercancia">
<!-- Iniciamos el manejo de los nodos dependientes -->
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@UnidadPesoMerc"/>
</xsl:call-template>
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@PesoBruto"/>
</xsl:call-template>
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@PesoNeto"/>
</xsl:call-template>
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@PesoTara"/>
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@NumPiezas"/>
</xsl:call-template>
</xsl:template>
<!-- Iniciamos el manejo de los elementos hijo en la secuencia Autotransporte-->
<xsl:template match="cartaporte20:Autotransporte">
<!--Manejador de nodos tipo cartaporte20:Autotransporte-->
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@PermSCT"/>
</xsl:call-template>
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@NumPermisoSCT"/>
</xsl:call-template>
<!-- Iniciamos el tratamiento de los atributos de cartaporte20:IdentificacionVehicular-->
<xsl:for-each select="./cartaporte20:IdentificacionVehicular">
<xsl:apply-templates select="."/>
</xsl:for-each>
<!-- Iniciamos el tratamiento de los atributos de cartaporte20:Seguros-->
<xsl:for-each select="./cartaporte20:Seguros">
<xsl:apply-templates select="."/>
</xsl:for-each>
<!-- Iniciamos el tratamiento de los atributos de cartaporte20:Remolques-->
<xsl:for-each select="./cartaporte20:Remolques">
<xsl:apply-templates select="."/>
</xsl:for-each>
</xsl:template>
<!-- Iniciamos el manejo de los elementos hijo en la secuencia IdentificacionVehicular-->
<xsl:template match="cartaporte20:IdentificacionVehicular">
<!-- Iniciamos el manejo de los nodos dependientes -->
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@ConfigVehicular"/>
</xsl:call-template>
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@PlacaVM"/>
</xsl:call-template>
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@AnioModeloVM"/>
</xsl:call-template>
</xsl:template>
<!-- Iniciamos el manejo de los elementos hijo en la secuencia Seguros-->
<xsl:template match="cartaporte20:Seguros">
<!-- Iniciamos el manejo de los nodos dependientes -->
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@AseguraRespCivil"/>
</xsl:call-template>
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@PolizaRespCivil"/>
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@AseguraMedAmbiente"/>
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@PolizaMedAmbiente"/>
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@AseguraCarga"/>
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@PolizaCarga"/>
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@PrimaSeguro"/>
</xsl:call-template>
</xsl:template>
<!-- Iniciamos el manejo de los elementos hijo en la secuencia Remolques-->
<xsl:template match="cartaporte20:Remolques">
<!-- Iniciamos el tratamiento de los atributos de cartaporte20:Remolque-->
<xsl:for-each select="./cartaporte20:Remolque">
<xsl:apply-templates select="."/>
</xsl:for-each>
</xsl:template>
<!-- Iniciamos el manejo de los elementos hijo en la secuencia Remolque-->
<xsl:template match="cartaporte20:Remolque">
<!-- Iniciamos el manejo de los nodos dependientes -->
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@SubTipoRem"/>
</xsl:call-template>
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@Placa"/>
</xsl:call-template>
</xsl:template>
<!-- Iniciamos el manejo de los elementos hijo en la secuencia TransporteMaritimo-->
<xsl:template match="cartaporte20:TransporteMaritimo">
<!--Manejador de nodos tipo cartaporte20:TransporteMaritimo-->
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@PermSCT"/>
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@NumPermisoSCT"/>
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@NombreAseg"/>
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@NumPolizaSeguro"/>
</xsl:call-template>
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@TipoEmbarcacion"/>
</xsl:call-template>
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@Matricula"/>
</xsl:call-template>
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@NumeroOMI"/>
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@AnioEmbarcacion"/>
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@NombreEmbarc"/>
</xsl:call-template>
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@NacionalidadEmbarc"/>
</xsl:call-template>
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@UnidadesDeArqBruto"/>
</xsl:call-template>
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@TipoCarga"/>
</xsl:call-template>
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@NumCertITC"/>
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@Eslora"/>
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@Manga"/>
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@Calado"/>
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@LineaNaviera"/>
</xsl:call-template>
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@NombreAgenteNaviero"/>
</xsl:call-template>
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@NumAutorizacionNaviero"/>
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@NumViaje"/>
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@NumConocEmbarc"/>
</xsl:call-template>
<!-- Iniciamos el tratamiento de los atributos de cartaporte20:Contenedor-->
<xsl:for-each select="./cartaporte20:Contenedor">
<!-- Iniciamos el manejo de los elementos hijo en la secuencia Contenedor-->
<!-- Iniciamos el manejo de los nodos dependientes -->
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@MatriculaContenedor"/>
</xsl:call-template>
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@TipoContenedor"/>
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@NumPrecinto"/>
</xsl:call-template>
</xsl:for-each>
</xsl:template>
<!-- Iniciamos el manejo de los elementos hijo en la secuencia TransporteAereo-->
<xsl:template match="cartaporte20:TransporteAereo">
<!--Manejador de nodos tipo cartaporte20:TransporteAereo-->
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@PermSCT"/>
</xsl:call-template>
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@NumPermisoSCT"/>
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@MatriculaAeronave"/>
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@NombreAseg"/>
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@NumPolizaSeguro"/>
</xsl:call-template>
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@NumeroGuia"/>
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@LugarContrato"/>
</xsl:call-template>
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@CodigoTransportista"/>
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@RFCEmbarcador"/>
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@NumRegIdTribEmbarc"/>
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@ResidenciaFiscalEmbarc"/>
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@NombreEmbarcador"/>
</xsl:call-template>
</xsl:template>
<!-- Iniciamos el manejo de los elementos hijo en la secuencia TransporteFerroviario-->
<xsl:template match="cartaporte20:TransporteFerroviario">
<!--Manejador de nodos tipo cartaporte20:TransporteFerroviario-->
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@TipoDeServicio"/>
</xsl:call-template>
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@TipoDeTrafico"/>
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@NombreAseg"/>
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@NumPolizaSeguro"/>
</xsl:call-template>
<!-- Iniciamos el tratamiento de los atributos de cartaporte20:DerechosDePaso-->
<xsl:for-each select="./cartaporte20:DerechosDePaso">
<xsl:apply-templates select="."/>
</xsl:for-each>
<!-- Iniciamos el tratamiento de los atributos de cartaporte20:Carro-->
<xsl:for-each select="./cartaporte20:Carro">
<xsl:apply-templates select="."/>
</xsl:for-each>
</xsl:template>
<!-- Iniciamos el manejo de los elementos hijo en la secuencia DerechosDePaso-->
<xsl:template match="cartaporte20:DerechosDePaso">
<!-- Iniciamos el manejo de los nodos dependientes -->
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@TipoDerechoDePaso"/>
</xsl:call-template>
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@KilometrajePagado"/>
</xsl:call-template>
</xsl:template>
<!-- Iniciamos el manejo de los elementos hijo en la secuencia Carro-->
<xsl:template match="cartaporte20:Carro">
<!--Manejador de nodos tipo cartaporte20:Carro-->
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@TipoCarro"/>
</xsl:call-template>
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@MatriculaCarro"/>
</xsl:call-template>
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@GuiaCarro"/>
</xsl:call-template>
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@ToneladasNetasCarro"/>
</xsl:call-template>
<!-- Iniciamos el tratamiento de los atributos de cartaporte20:Contenedor -->
<xsl:for-each select="./cartaporte20:Contenedor ">
<!-- Iniciamos el manejo de los elementos hijo en la secuencia Contenedor-->
<!-- Iniciamos el manejo de los nodos dependientes -->
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@TipoContenedor"/>
</xsl:call-template>
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@PesoContenedorVacio"/>
</xsl:call-template>
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@PesoNetoMercancia"/>
</xsl:call-template>
</xsl:for-each>
</xsl:template>
<!-- Iniciamos el manejo de los elementos hijo en la secuencia FiguraTransporte-->
<xsl:template match="cartaporte20:FiguraTransporte">
<!--Manejador de nodos tipo cartaporte20:FiguraTransporte-->
<!-- Iniciamos el tratamiento de los atributos de cartaporte20:TiposFigura-->
<xsl:for-each select="./cartaporte20:TiposFigura ">
<xsl:apply-templates select="."/>
</xsl:for-each>
</xsl:template>
<!-- Iniciamos el manejo de los elementos hijo en la secuencia TiposFigura-->
<xsl:template match="cartaporte20:TiposFigura">
<!-- Iniciamos el tratamiento de los atributos de cartaporte20:TiposFigura-->
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@TipoFigura"/>
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@RFCFigura"/>
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@NumLicencia"/>
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@NombreFigura"/>
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@NumRegIdTribFigura"/>
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@ResidenciaFiscalFigura"/>
</xsl:call-template>
<xsl:for-each select="./cartaporte20:PartesTransporte">
<xsl:apply-templates select="."/>
</xsl:for-each>
<!-- Iniciamos el tratamiento de los atributos de cartaporte20:Domicilio -->
<xsl:for-each select="./cartaporte20:Domicilio ">
<!-- Iniciamos el manejo de los elementos hijo en la secuencia Domicilio-->
<!-- Iniciamos el manejo de los nodos dependientes -->
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@Calle"/>
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@NumeroExterior"/>
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@NumeroInterior"/>
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@Colonia"/>
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@Localidad"/>
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@Referencia"/>
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@Municipio"/>
</xsl:call-template>
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@Estado"/>
</xsl:call-template>
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@Pais"/>
</xsl:call-template>
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@CodigoPostal"/>
</xsl:call-template>
</xsl:for-each>
</xsl:template>
<!-- Iniciamos el manejo de los elementos hijo en la secuencia PartesTransporte-->
<xsl:template match="cartaporte20:PartesTransporte">
<!--Manejador de nodos tipo cartaporte20:PartesTransporte-->
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@ParteTransporte"/>
</xsl:call-template>
</xsl:template>
</xsl:stylesheet>