PDF from Carta Porte v3

This commit is contained in:
El Mau 2024-04-02 00:20:44 -06:00
parent 7f129b84b2
commit 29bfb42abc
5 changed files with 119 additions and 95 deletions

View File

@ -30,6 +30,7 @@ import requests
import sqlite3
import socket
import subprocess
import sys
import tempfile
import textwrap
import threading
@ -44,7 +45,6 @@ from pathlib import Path
from xml.etree import ElementTree as ET
from xml.dom.minidom import parseString
try:
import uno
from com.sun.star.beans import PropertyValue
@ -52,9 +52,20 @@ try:
from com.sun.star.view.PaperFormat import LETTER
APP_LIBO = True
except ImportError:
APP_LIBO = False
path_fun = '/usr/lib/libreoffice/program/fundamentalrc'
path_uno = '/usr/lib/libreoffice/program/'
os.environ['URE_BOOTSTRAP'] = f'vnd.sun.star.pathname:{path_fun}'
os.environ['UNO_PATH'] = path_uno
sys.path.append(path_uno)
try:
import uno
from com.sun.star.beans import PropertyValue
from com.sun.star.awt import Size
from com.sun.star.view.PaperFormat import LETTER
APP_LIBO = True
except ImportError:
APP_LIBO = False
# ~ import pyqrcode
from dateutil import parser
from lxml import etree
@ -69,7 +80,6 @@ from settings import DEBUG, MV, log, template_lookup, COMPANIES, DB_SAT, \
PATH_XMLSEC, TEMPLATE_CANCEL, DEFAULT_SAT_PRODUCTO, DECIMALES, DIR_FACTURAS
from settings import USAR_TOKEN, API, DECIMALES_TAX
# ~ from .configpac import AUTH
from .utils import get_qr
@ -951,6 +961,7 @@ class LIBO(object):
return
# ~ print(data)
qr = data.pop('qr', False)
figuras = data.pop('figuras')
mercancias = data.pop('mercancias')
detalle = mercancias.pop('detalle')
@ -1019,7 +1030,27 @@ class LIBO(object):
cell_3 = self._set_cell(v=unidad, cell=cell_3)
cell_4 = self._set_cell(v=cantidad, cell=cell_4)
cell_5 = self._set_cell(v=peso, cell=cell_5)
if qr:
self._timbre_carta(qr)
return
def _timbre_carta(self, qr):
pd = self._sheet.getDrawPage()
image = self._template.createInstance('com.sun.star.drawing.GraphicObjectShape')
gp = self._create_instance('com.sun.star.graphic.GraphicProvider')
pd.add(image)
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 = 4000
s.Height = 4000
image.setSize(s)
image.Anchor = self._set_cell('{cp.qr}')
return
def _comercio_exterior(self, data):
@ -1690,7 +1721,7 @@ def to_pdf(data, emisor_rfc, ods=False, pdf_from='1'):
version = f'{version}_cn_{version_nomina}'
if 'carta_porte' in data:
default = 'plantilla_factura_ccp.ods'
default = 'plantilla_ccp.ods'
version = '{}_ccp_{}'.format(version, data['carta_porte']['version'])
if data.get('pagos', False):
@ -1844,24 +1875,6 @@ def to_letters(value, currency):
return NumLet(value, currency).letras
# ~ def get_qr(data, p=True):
# ~ qr = pyqrcode.create(data, mode='binary')
# ~ if p:
# ~ path = get_path_temp('.qr')
# ~ qr.png(path, scale=7)
# ~ return path
# ~ buffer = io.BytesIO()
# ~ qr.png(buffer, scale=8)
# ~ 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:
@ -3146,7 +3159,8 @@ def parse_xml2(xml_str):
def get_idccp():
uuid_v4 = uuid.uuid4()
custom_uuid_str = f"CCC{uuid_v4.hex[3:8].upper()}-{uuid_v4.hex[8:12].upper()}-{uuid_v4.hex[12:16].upper()}-{uuid_v4.hex[16:20].upper()}-{uuid_v4.hex[20:32].upper()}"
uuid4 = str(uuid.uuid4()).upper()
custom_uuid_str = f'CCC{uuid4[3:]}'
# ~ custom_uuid_str = f"CCC{uuid_v4.hex[3:8].upper()}-{uuid_v4.hex[8:12].upper()}-{uuid_v4.hex[12:16].upper()}-{uuid_v4.hex[16:20].upper()}-{uuid_v4.hex[20:32].upper()}"
return custom_uuid_str

View File

@ -261,9 +261,11 @@ class CfdiToDict(object):
'cfdi4.0': 'http://www.sat.gob.mx/cfd/4',
}
NS = {
'tfd': 'http://www.sat.gob.mx/TimbreFiscalDigital',
'divisas': 'http://www.sat.gob.mx/divisas',
'leyendasFisc': 'http://www.sat.gob.mx/leyendasFiscales',
'cartaporte20': 'http://www.sat.gob.mx/CartaPorte20',
'cartaporte30': 'http://www.sat.gob.mx/CartaPorte30',
'nomina12': 'http://www.sat.gob.mx/nomina12',
'cce20': 'http://www.sat.gob.mx/ComercioExterior20',
}
@ -397,11 +399,23 @@ class CfdiToDict(object):
self.version = self._root.attrib['Version']
ns = f'cfdi{self.version}'
self.NS['cfdi'] = self.NS_VERSION[ns]
self._timbre_fiscal()
self._informacion_global()
self._receptor()
self._complementos()
return
def _timbre_fiscal(self):
path = '//tfd:TimbreFiscalDigital'
data = self._root.xpath(path, namespaces=self.NS)
if not data:
return
data = data[0]
attr = CaseInsensitiveDict(data.attrib)
self._fecha_timbrado = attr['FechaTimbrado']
return
def _informacion_global(self):
self._values['informacion_global'] = {}
@ -514,10 +528,75 @@ class CfdiToDict(object):
self._values['carta_porte'] = values
self._complemento_carta_porte(complemento)
self._complemento_comercio_exterior(complemento)
return
def _complemento_carta_porte(self, complemento):
path = '//cartaporte30:CartaPorte'
carta_porte = complemento.xpath(path, namespaces=self.NS)
if carta_porte:
self._get_carta_porte_3(carta_porte)
return
def _get_carta_porte_3(self, carta_porte):
URL = 'https://verificacfdi.facturaelectronica.sat.gob.mx/verificaccp/default.aspx'
PRE = '//cartaporte30'
values = CaseInsensitiveDict(carta_porte[0].attrib)
idccp = values['idccp']
for node in carta_porte[0]:
if 'FiguraTransporte' in node.tag:
figuras = CaseInsensitiveDict(node[0].attrib)
figuras['TipoFigura'] = self.tipo_figura[figuras['TipoFigura']]
values['figuras'] = figuras
elif 'Mercancias' in node.tag:
mercancias = CaseInsensitiveDict(node.attrib)
detalle = [CaseInsensitiveDict(n.attrib)
for n in node if 'Mercancia' in n.tag]
values['mercancias'] = {
'mercancias': mercancias,
'detalle': detalle,
}
path = f'{PRE}:Autotransporte'
node_auto = node.xpath(path, namespaces=self.NS)[0]
values_auto = CaseInsensitiveDict(node_auto.attrib)
values['autotransporte'] = values_auto
path = f'{PRE}:IdentificacionVehicular'
node_tmp = node_auto.xpath(path, namespaces=self.NS)[0]
values_auto = CaseInsensitiveDict(node_tmp.attrib)
values['autotransporte'].update(values_auto)
path = f'{PRE}:Seguros'
node_tmp = node_auto.xpath(path, namespaces=self.NS)[0]
values_auto = CaseInsensitiveDict(node_tmp.attrib)
values['autotransporte'].update(values_auto)
path = f'{PRE}:Remolques'
try:
node_tmp = node_auto.xpath(path, namespaces=self.NS)[0][0]
values_auto = CaseInsensitiveDict(node_tmp.attrib)
values['autotransporte'].update(values_auto)
except IndexError:
pass
elif 'Ubicaciones' in node.tag:
ubicaciones = []
for n in node:
ubicacion = CaseInsensitiveDict(n.attrib)
if ubicacion['TipoUbicacion'] == 'Origen':
fecha_origen = ubicacion['FechaHoraSalidaLlegada']
ubicacion['domicilio'] = self._set_carta_porte_domicilio(
CaseInsensitiveDict(n[0].attrib))
ubicaciones.append(ubicacion)
values['FechaOrigen'] = fecha_origen
values['ubicaciones'] = ubicaciones
qr_data = f'{URL}?IdCCP={idccp}&FechaOrig={fecha_origen}&FechaTimb={self._fecha_timbrado}'
values['qr'] = get_qr(qr_data, 'png')
self._values['carta_porte'] = values
return
def _complemento_comercio_exterior(self, complemento):
path = '//cce20:ComercioExterior'
comercio_exterior = complemento.xpath(path, namespaces=self.NS)

View File

@ -4823,16 +4823,6 @@ class Facturas(BaseModel):
obj = SATTipoRelacion.get(SATTipoRelacion.key==invoice.tipo_relacion)
values['tiporelacion'] = str(obj)
# ~ use_packing = Configuracion.get_bool('chk_use_packing')
# ~ if use_packing:
# ~ w = FacturasDetalle.factura == invoice
# ~ q = (FacturasDetalle
# ~ .select(FacturasDetalle.empaques)
# ~ .where(w)
# ~ .order_by(FacturasDetalle.id.asc())
# ~ .tuples())
# ~ values['pakings'] = [str(int(r[0])) for r in q]
return values
def _get_not_in_xml2(self, invoice, data):

View File

@ -882,68 +882,9 @@ function guardar_y_timbrar(values){
var usar_cartaporte = $$('chk_cfdi_usar_cartaporte').getValue()
if(usar_cartaporte){
//~ var total_distance = 0.00
//~ var total_weight = 0.00
//~ var cartaporte = {
//~ TranspInternac: $$('lst_carta_TranspInternac').getValue(),
//~ TotalDistRec: total_distance,
//~ }
//~ var ubicaciones = $$('grid_carta_ubicaciones').data.getRange()
//~ ubicaciones.forEach(function(row, index){
//~ delete row['id']
//~ delete row['delete']
//~ if(row['DistanciaRecorrida']){
//~ total_distance += parseFloat(row['DistanciaRecorrida'])
//~ }
//~ })
//~ cartaporte['TotalDistRec'] = total_distance
//~ cartaporte['ubicaciones'] = ubicaciones
//~ var row = $$('grid_carta_autotransporte').data.getRange()[0]
//~ var autotransporte = {
//~ PermSCT: row['PermSCT'],
//~ NumPermisoSCT: row['NumPermisoSCT'],
//~ identificacion: {
//~ ConfigVehicular: row['ConfigVehicular'],
//~ PlacaVM: row['PlacaVM'],
//~ AnioModeloVM: row['AnioModeloVM'],
//~ },
//~ seguros: {
//~ AseguraRespCivil: row['AseguraRespCivil'],
//~ PolizaRespCivil: row['PolizaRespCivil'],
//~ },
//~ remolque: {
//~ SubTipoRem: row['SubTipoRem'],
//~ Placa: row['Placa'],
//~ }
//~ }
//~ var mercancias = $$('grid_carta_mercancias').data.getRange()
//~ mercancias.forEach(function(row, index){
//~ delete row['id']
//~ delete row['delete']
//~ row['Cantidad'] = String(row['Cantidad'])
//~ row['ValorMercancia'] = String(row['ValorMercancia'])
//~ if(row['PesoEnKg']){
//~ total_weight += parseFloat(row['PesoEnKg'])
//~ }
//~ })
//~ var mercancias = {
//~ 'PesoBrutoTotal': 0.00,
//~ 'UnidadPeso': $$('lst_carta_UnidadPeso').getValue(),
//~ 'NumTotalMercancias': String(mercancias.length),
//~ mercancias: mercancias,
//~ autotransporte: autotransporte,
//~ }
//~ cartaporte['mercancias'] = mercancias
//~ var tiposfigura = $$('grid_carta_tipos_figuras').data.getRange()
//~ tiposfigura.forEach(function(row, index){
//~ delete row['id']
//~ })
//~ cartaporte['tiposfigura'] = tiposfigura
var result = _get_values_carta_porte()
data['cartaporte'] = result.values
$$('chk_cfdi_usar_cartaporte').setValue(false)
}
var usar_comercioe = $$('chk_cfdi_usar_comercioe').getValue()

Binary file not shown.