diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 4dde17b8..ac8e28c2 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,2 +1,2 @@ [bumpversion] -current_version = 3.0.0 +current_version = 3.6.0 diff --git a/.github/workflows/python-app.yml b/.github/workflows/python-app.yml index 86f95b13..b7a56ac5 100644 --- a/.github/workflows/python-app.yml +++ b/.github/workflows/python-app.yml @@ -14,7 +14,7 @@ on: jobs: build: - runs-on: ubuntu-latest + runs-on: ubuntu-20.04 strategy: fail-fast: false # You can use PyPy versions in python-version. @@ -33,20 +33,19 @@ jobs: if: matrix.python-version == '2.7' run: | sudo apt update - sudo apt install python2 python-pip + sudo apt install python2 python2-dev + curl https://bootstrap.pypa.io/pip/2.7/get-pip.py --output get-pip.py + sudo python2 get-pip.py sudo update-alternatives --install /usr/bin/python python /usr/bin/python2 1 sudo update-alternatives --install /usr/bin/python python /usr/bin/python3 2 printf '1\n' | sudo update-alternatives --config python - cd /usr/bin - sudo ln -s /usr/bin/pip2 ./pip - name: Upgrade pip run: | pip install --upgrade pip setuptools wheel - name: Install dependencies run: | + pip install -r requirements-dev.txt pip install . - # if [[ $TRAVIS_PYTHON_VERSION == 2.6 ]]; then pip install unittest2; fi - pip install coveralls - name: Run test if: matrix.python-version == '2.7' run: | @@ -54,4 +53,5 @@ jobs: - name: Run test if: matrix.python-version == '3.11' run: | - coverage run setup.py test + python -m unittest discover -v + diff --git a/gestionatr/__init__.py b/gestionatr/__init__.py index 691efca8..fbd49fc9 100644 --- a/gestionatr/__init__.py +++ b/gestionatr/__init__.py @@ -1,2 +1,2 @@ # -*- coding: utf-8 -*- -__version__ = '3.1.8' +__version__ = '3.6.0' diff --git a/gestionatr/cli.py b/gestionatr/cli.py index 35abe332..28fee826 100644 --- a/gestionatr/cli.py +++ b/gestionatr/cli.py @@ -3,6 +3,7 @@ import six import sys import click +import re from suds.cache import NoCache from suds.client import Client from suds.transport.https import HttpAuthenticated @@ -15,6 +16,7 @@ from gestionatr.input.messages import message_gas from gestionatr.input.messages.message import except_f1 from gestionatr.output.messages.sw_p0 import ENVELOP_BY_DISTR +from gestionatr.output.messages.sw_a5_29 import ENVELOP_GAS_BY_DISTR from gestionatr.exceptions import * from gestionatr import __version__ @@ -38,6 +40,25 @@ """ +A29_TEMPLATE = """ + + + GML + {emisora} + {destino} + {fecha_solicitud} + {hora_solicitud} + 29 + A5 + + + {fecha_solicitud} + {hora_solicitud} + {cups} + N + + +""" def get_gestionatr_version(ctx, param, value): if not value or ctx.resilient_parsing: @@ -80,6 +101,89 @@ def test(filename, sector): sys.stdout.flush() +def request_atr_29(url, user, password, xml_str=None, params=None): + import random + import re + from datetime import datetime + if xml_str is None and params is None: + raise ValueError("XML or params must be passed to request_A5") + if xml_str is None and params: + codi_receptor = params['destino'] + params['fecha_solicitud'] = datetime.now().strftime('%Y-%m-%d') + params['hora_solicitud'] = datetime.now().strftime('%H:%M:%S') + params['solicitud'] = 10**11 + int((random.random() * 10**11)) + xml_str = re.sub(r'\s+<', '<', A29_TEMPLATE) + xml_str = re.sub(r'\s+$', '', xml_str) + xml_str = re.sub(r'\n', '', xml_str).format(**params) + else: + codi_receptor = xml_str.split("")[1].split("")[0] + + base64string = base64.encodestring(str('%s:%s' % (user, password))).replace('\n', '') + headers = { + "Authorization": "Basic %s" % base64string, + "content-type": 'text/xml; charset=utf-8', + } + + # Clean XML + xml_str = xml_str.strip() + xml_str = xml_str.replace("'utf-8'", "'UTF-8'") + xml_str = xml_str.replace("", "") + xml_str = xml_str.replace("""""", "") + xml_str = xml_str.replace("""""", "") + xml_str = xml_str.replace("<", "", """""") + res = res.replace("", """""", 1) + aux = etree.fromstring(res) + aux_res = find_child(aux, "sctdapplication") + + res = etree.tostring(aux_res) + return res + except A529FaultError as A529efe: + if not error: + error = A529efe + except Exception as e: + if not error: + error = e + + if error: + if isinstance(error, A529FaultError): + raise error + else: + print(error) + + return res + + def request_p0(url, user, password, xml_str=None, params=None): import random import re @@ -187,4 +291,24 @@ def sollicitar_p0(url, user, password, file=None, emisora=None, destino=None, cu res = etree.fromstring(res) print(etree.tostring(res, pretty_print=True)) +@atr.command(name='a529') +@click.option('-u', '--url', default='http://localhost', help=u'URL del webservice', show_default=True) +@click.option('-s', '--user', default='admin', help=u'User del webservice', show_default=True) +@click.option('-p', '--password', default='admin', help=u'Password del webservice', show_default=True) +@click.option('-f', '--file', help=u'Fitxer 29 pas A5 per enviar', show_default=True) +@click.option('--emisora', help='Código REE empresa emisora') +@click.option('--destino', help='Código REE empresa destino') +@click.option('--cups', help='CUPS') +def sollicitar_a529(url, user, password, file=None, emisora=None, destino=None, cups=None): + params = None + from lxml import etree + if emisora and destino and cups: + params = { + 'emisora': emisora, + 'destino': destino, + 'cups': cups + } + res = request_atr_29(url, user, password, file, params) + res = etree.fromstring(res) + print(etree.tostring(res, pretty_print=True)) diff --git a/gestionatr/defs.py b/gestionatr/defs.py index ece29064..2e1f88d2 100644 --- a/gestionatr/defs.py +++ b/gestionatr/defs.py @@ -2249,6 +2249,7 @@ ('11', u'Pendiente envío de fichero de coeficientes de reparto variables para el año en curso'), ('13', u'Información aplicación descuento por retardo en activación autoconsumo imputable al distribuidor'), ('14', u'Información aplicación descuento por retardo en activación autoconsumo NO imputable al distribuidor'), + ('15', u'Alta en autoconsumo colectivo comunicada por un participante'), ] TABLA_110 = [('01', u'Acompaña curva de carga'), diff --git a/gestionatr/exceptions.py b/gestionatr/exceptions.py index 383e8e2d..f9b0dee9 100644 --- a/gestionatr/exceptions.py +++ b/gestionatr/exceptions.py @@ -1,2 +1,6 @@ class P0FaultError(Exception): pass + + +class A529FaultError(Exception): + pass diff --git a/gestionatr/input/messages/A5_29.py b/gestionatr/input/messages/A5_29.py new file mode 100644 index 00000000..d5feb3a8 --- /dev/null +++ b/gestionatr/input/messages/A5_29.py @@ -0,0 +1,834 @@ +# -*- coding: utf-8 -*- +from __future__ import absolute_import, unicode_literals +from .message_gas import MessageGas +from .Deadlines import ProcessDeadline +from gestionatr.utils import get_rec_attr + + +class A5_29(MessageGas, ProcessDeadline): + + # Datos paso a529 + # TODO + + # Datos paso a629 + + @property + def codrpds(self): + tree = '{0}.codrpds'.format(self._header) + data = get_rec_attr(self.obj, tree, False) + if data is not None and data is not False: + return data.text + else: + return False + + @property + def fechacreacion(self): + tree = '{0}.fechacreacion'.format(self._header) + data = get_rec_attr(self.obj, tree, False) + if data is not None and data is not False: + return data.text + else: + return False + + @property + def horacreacion(self): + tree = '{0}.horacreacion'.format(self._header) + data = get_rec_attr(self.obj, tree, False) + if data is not None and data is not False: + return data.text + else: + return False + + @property + def codresultado(self): + tree = '{0}.codresultado'.format(self._header) + data = get_rec_attr(self.obj, tree, False) + if data is not None and data is not False: + return data.text + else: + return False + + @property + def descresultado(self): + tree = '{0}.descresultado'.format(self._header) + data = get_rec_attr(self.obj, tree, False) + if data is not None and data is not False: + return data.text + else: + return False + + @property + def codmotivo(self): + tree = '{0}.codmotivo'.format(self._header) + data = get_rec_attr(self.obj, tree, False) + if data is not None and data is not False: + return data.text + else: + return False + + @property + def descmotresultado(self): + tree = '{0}.descmotresultado'.format(self._header) + data = get_rec_attr(self.obj, tree, False) + if data is not None and data is not False: + return data.text + else: + return False + + @property + def resvalidacioncliente(self): + tree = '{0}.resvalidacioncliente'.format(self._header) + data = get_rec_attr(self.obj, tree, False) + if data is not None and data is not False: + return data.text + else: + return False + + @property + def datosdis(self): + tree = '{0}.datosdis'.format(self._header) + data = get_rec_attr(self.obj, tree, False) + if data not in [None, False]: + return DatosDis(data) + else: + return False + + @property + def datoscom(self): + tree = '{0}.datoscom'.format(self._header) + data = get_rec_attr(self.obj, tree, False) + if data not in [None, False]: + return DatosCom(data) + else: + return False + + +class DatosDis(object): + def __init__(self, data): + self.datosdis = data + + @property + def cups(self): + data = False + try: + data = self.datosdis.cups.text + except AttributeError: + pass + return data + + @property + def distribuidor(self): + data = False + try: + data = self.datosdis.distribuidor.text + except AttributeError: + pass + return data + + @property + def razonsocial(self): + data = False + try: + data = self.datosdis.razonsocial.text + except AttributeError: + pass + return data + + @property + def codprovincia(self): + data = False + try: + data = self.datosdis.codprovincia.text + except AttributeError: + pass + return data + + @property + def descprovincia(self): + data = False + try: + data = self.datosdis.descprovincia.text + except AttributeError: + pass + return data + + @property + def codpostal(self): + data = False + try: + data = self.datosdis.codpostal.text + except AttributeError: + pass + return data + + @property + def codmunicipio(self): + data = False + try: + data = self.datosdis.codmunicipio.text + except AttributeError: + pass + return data + + @property + def descmunicipio(self): + data = False + try: + data = self.datosdis.descmunicipio.text + except AttributeError: + pass + return data + + @property + def tipovia(self): + data = False + try: + data = self.datosdis.tipovia.text + except AttributeError: + pass + return data + + @property + def via(self): + data = False + try: + data = self.datosdis.via.text + except AttributeError: + pass + return data + + @property + def numfinca(self): + data = False + try: + data = self.datosdis.numfinca.text + except AttributeError: + pass + return data + + @property + def restodireccion(self): + data = False + try: + data = self.datosdis.restodireccion.text + except AttributeError: + pass + return data + + @property + def viviendahabitual(self): + data = False + try: + data = self.datosdis.viviendahabitual.text + except AttributeError: + pass + return data + + @property + def fechaalta(self): + data = False + try: + data = self.datosdis.fechaalta.text + except AttributeError: + pass + return data + + @property + def fechaultimalectura(self): + data = False + try: + data = self.datosdis.fechaultimalectura.text + except AttributeError: + pass + return data + + @property + def puntocortado(self): + data = False + try: + data = self.datosdis.puntocortado.text + except AttributeError: + pass + return data + + @property + def rangopresiondiseno(self): + data = False + try: + data = self.datosdis.rangopresiondiseno.text + except AttributeError: + pass + return data + + @property + def qh(self): + data = False + try: + data = self.datosdis.qh.text + except AttributeError: + pass + return data + + @property + def qmax(self): + data = False + try: + data = self.datosdis.qmax.text + except AttributeError: + pass + return data + + @property + def derechotur(self): + data = False + try: + data = self.datosdis.derechotur.text + except AttributeError: + pass + return data + + @property + def tarifapeaje(self): + data = False + try: + data = self.datosdis.tarifapeaje.text + except AttributeError: + pass + return data + + @property + def fechaultimainspeccion(self): + data = False + try: + data = self.datosdis.fechaultimainspeccion.text + except AttributeError: + pass + return data + + @property + def resultimainspeccion(self): + data = False + try: + data = self.datosdis.resultimainspeccion.text + except AttributeError: + pass + return data + + @property + def fechaultimarevision(self): + data = False + try: + data = self.datosdis.fechaultimarevision.text + except AttributeError: + pass + return data + + @property + def resultimarevision(self): + data = False + try: + data = self.datosdis.resultimarevision.text + except AttributeError: + pass + return data + + @property + def historicoConsumo(self): + data = [] + if self.datosdis.historicoConsumo not in [None, False]: + for i in self.datosdis.historicoConsumo: + data.append(HistoricoConsumo(i)) + return data + return data + + @property + def pctjeconsumonoct(self): + data = False + try: + data = self.datosdis.pctjeconsumonoct.text + except AttributeError: + pass + return data + + @property + def codequipo(self): + data = False + try: + data = self.datosdis.codequipo.text + except AttributeError: + pass + return data + + @property + def contelemedida(self): + data = False + try: + data = self.datosdis.contelemedida.text + except AttributeError: + pass + return data + + @property + def marcamodelocont(self): + data = False + try: + data = self.datosdis.marcamodelocont.text + except AttributeError: + pass + return data + + @property + def marcamodelocorr(self): + data = False + try: + data = self.datosdis.marcamodelocorr.text + except AttributeError: + pass + return data + + @property + def propiedad(self): + data = False + try: + data = self.datosdis.propiedad.text + except AttributeError: + pass + return data + + @property + def tipocorrector(self): + data = False + try: + data = self.datosdis.tipocorrector.text + except AttributeError: + pass + return data + + @property + def fecultcambiotarifa(self): + data = False + try: + data = self.datosdis.fecultcambiotarifa.text + except AttributeError: + pass + return data + + @property + def fecultcontrato(self): + data = False + try: + data = self.datosdis.fecultcontrato.text + except AttributeError: + pass + return data + + @property + def fecultcambiocom(self): + data = False + try: + data = self.datosdis.fecultcambiocom.text + except AttributeError: + pass + return data + + @property + def perfil(self): + data = False + try: + data = self.datosdis.perfil.text + except AttributeError: + pass + return data + + @property + def indconectadoplantasatelite(self): + data = False + try: + data = self.datosdis.indconectadoplantasatelite.text + except AttributeError: + pass + return data + + @property + def fecactdist(self): + data = False + try: + data = self.datosdis.fecactdist.text + except AttributeError: + pass + return data + + @property + def listaproductos(self): + data = [] + if self.datosdis.listaproductos not in [None, False]: + for i in self.datosdis.listaproductos: + data.append(Producto(i)) + return data + return data + + +class HistoricoConsumo(object): + def __init__(self, data): + self.historicoConsumo = data + + @property + def fecinicioperiodo(self): + data = False + try: + data = self.historicoConsumo.fecinicioperiodo.text + except AttributeError: + pass + return data + + @property + def fecfinperiodo(self): + data = False + try: + data = self.historicoConsumo.fecfinperiodo.text + except AttributeError: + pass + return data + + @property + def consumoperiodo(self): + data = False + try: + data = self.historicoConsumo.consumoperiodo.text + except AttributeError: + pass + return data + + @property + def caudalminperiodo(self): + data = False + try: + data = self.historicoConsumo.caudalminperiodo.text + except AttributeError: + pass + return data + + @property + def caudalmedperiodo(self): + data = False + try: + data = self.historicoConsumo.caudalmedperiodo.text + except AttributeError: + pass + return data + + @property + def caudalmaxperiodo(self): + data = False + try: + data = self.historicoConsumo.caudalmaxperiodo.text + except AttributeError: + pass + return data + + +class Producto(object): + def __init__(self, data): + self.prod = data + + @property + def codigoproducto(self): + data = False + try: + data = self.prod.codigoproducto.text + except AttributeError: + pass + return data + + @property + def tipoproducto(self): + data = False + try: + data = self.prod.tipoproducto.text + except AttributeError: + pass + return data + + @property + def tarifaproducto(self): + data = False + try: + data = self.prod.tarifaproducto.text + except AttributeError: + pass + return data + + @property + def qdproducto(self): + data = False + try: + data = self.prod.qdproducto.text + except AttributeError: + pass + return data + + @property + def qaproducto(self): + data = False + try: + data = self.prod.qaproducto.text + except AttributeError: + pass + return data + + @property + def fechainicioproducto(self): + data = False + try: + data = self.prod.fechainicioproducto.text + except AttributeError: + pass + return data + + @property + def horainicioproducto(self): + data = False + try: + data = self.prod.horainicioproducto.text + except AttributeError: + pass + return data + + @property + def fechafinproducto(self): + data = False + try: + data = self.prod.fechafinproducto.text + except AttributeError: + pass + return data + + +class DatosCom(object): + def __init__(self, data): + self.datoscom = data + + @property + def nombre(self): + data = False + try: + data = self.datoscom.nombre.text + except AttributeError: + pass + return data + + @property + def apellido1(self): + data = False + try: + data = self.datoscom.apellido1.text + except AttributeError: + pass + return data + + @property + def apellido2(self): + data = False + try: + data = self.datoscom.apellido2.text + except AttributeError: + pass + return data + + @property + def codprovincia(self): + data = False + try: + data = self.datoscom.codprovincia.text + except AttributeError: + pass + return data + + @property + def descprovincia(self): + data = False + try: + data = self.datoscom.descprovincia.text + except AttributeError: + pass + return data + + @property + def codpostal(self): + data = False + try: + data = self.datoscom.codpostal.text + except AttributeError: + pass + return data + + @property + def codmunicipio(self): + data = False + try: + data = self.datoscom.codmunicipio.text + except AttributeError: + pass + return data + + @property + def descmunicipio(self): + data = False + try: + data = self.datoscom.descmunicipio.text + except AttributeError: + pass + return data + + @property + def tipovia(self): + data = False + try: + data = self.datoscom.tipovia.text + except AttributeError: + pass + return data + + @property + def via(self): + data = False + try: + data = self.datoscom.via.text + except AttributeError: + pass + return data + + @property + def numfinca(self): + data = False + try: + data = self.datoscom.numfinca.text + except AttributeError: + pass + return data + + @property + def portal(self): + data = False + try: + data = self.datoscom.portal.text + except AttributeError: + pass + return data + + @property + def escalera(self): + data = False + try: + data = self.datoscom.escalera.text + except AttributeError: + pass + return data + + @property + def piso(self): + data = False + try: + data = self.datoscom.piso.text + except AttributeError: + pass + return data + + @property + def puerta(self): + data = False + try: + data = self.datoscom.puerta.text + except AttributeError: + pass + return data + + @property + def tipopersona(self): + data = False + try: + data = self.datoscom.tipopersona.text + except AttributeError: + pass + return data + + @property + def viviendahabitual(self): + data = False + try: + data = self.datoscom.viviendahabitual.text + except AttributeError: + pass + return data + + @property + def impagos(self): + data = False + try: + data = self.datoscom.impagos.text + except AttributeError: + pass + return data + + @property + def fecimpagos(self): + data = False + try: + data = self.datoscom.fecimpagos.text + except AttributeError: + pass + return data + + @property + def catimpagos(self): + data = False + try: + data = self.datoscom.catimpagos.text + except AttributeError: + pass + return data + + @property + def usuimpagos(self): + data = False + try: + data = self.datoscom.usuimpagos.text + except AttributeError: + pass + return data + + @property + def prohibpublicdatcli(self): + data = False + try: + data = self.datoscom.prohibpublicdatcli.text + except AttributeError: + pass + return data + + @property + def fecactprohibpublic(self): + data = False + try: + data = self.datoscom.fecactprohibpublic.text + except AttributeError: + pass + return data + + @property + def catactprohibpublic(self): + data = False + try: + data = self.datoscom.catactprohibpublic.text + except AttributeError: + pass + return data + + @property + def usuactprohibpublic(self): + data = False + try: + data = self.datoscom.usuactprohibpublic.text + except AttributeError: + pass + return data diff --git a/gestionatr/input/messages/__init__.py b/gestionatr/input/messages/__init__.py index ee5b8448..8d1ebc1a 100644 --- a/gestionatr/input/messages/__init__.py +++ b/gestionatr/input/messages/__init__.py @@ -34,4 +34,5 @@ from .A13_50 import A13_50 from .A20_36 import A20_36 from .A1_42 import A1_42 -from .A1_43 import A1_43 \ No newline at end of file +from .A1_43 import A1_43 +from .A5_29 import A5_29 diff --git a/gestionatr/input/messages/message_gas.py b/gestionatr/input/messages/message_gas.py index 6bb38326..1ee16948 100644 --- a/gestionatr/input/messages/message_gas.py +++ b/gestionatr/input/messages/message_gas.py @@ -114,6 +114,10 @@ 'A25': 'A2543.xsd', 'A26': 'A2643.xsd', }, + '29': { + 'A5': 'A529.xsd', + 'A6': 'A629.xsd', + } }) MAIN_MESSAGE_XSD.update({ @@ -204,6 +208,9 @@ 'A443': 'a443', 'A2543': 'a2543', 'A2643': 'a2643', + # 29 + 'A529': 'a529', + 'A629': 'a629', }) @@ -242,9 +249,16 @@ def set_tipus(self): self.pas = self.head.codproceso.text self.codproceso = self.pas except: - msg = u'No se puede identificar el código de proceso ' \ - u'o código de paso' - raise except_f1('Error', msg) + try: + # If per el "P0" (aka A529) + self.tipus = self.head.codproceso.text + self.processcode = self.tipus + self.pas = self.head.tipomensaje.text + self.messagetype = self.pas + except: + msg = u'No se puede identificar el código de proceso ' \ + u'o código de paso' + raise except_f1('Error', msg) # Funcions relacionades amb la capçalera del XML diff --git a/gestionatr/output/messages/base_gas.py b/gestionatr/output/messages/base_gas.py index 77254acf..5bccd2ea 100644 --- a/gestionatr/output/messages/base_gas.py +++ b/gestionatr/output/messages/base_gas.py @@ -20,3 +20,20 @@ def __init__(self): self.messagetype = XmlField('messagetype') super(Heading, self).__init__('heading', 'heading') + +class Cabecera(XmlModel): + _sort_order = ('cabecera', 'codenvio', + 'empresaemisora', 'empresadestino', + 'fechacomunicacion', 'horacomunicacion', 'codproceso', + 'tipomensaje') + + def __init__(self): + self.cabecera = XmlField('cabecera') + self.codenvio = XmlField('codenvio') + self.empresaemisora = XmlField('empresaemisora') + self.empresadestino = XmlField('empresadestino') + self.fechacomunicacion = XmlField('fechacomunicacion') + self.horacomunicacion = XmlField('horacomunicacion') + self.codproceso = XmlField('codproceso') + self.tipomensaje = XmlField('tipomensaje') + super(Cabecera, self).__init__('cabecera', 'cabecera') diff --git a/gestionatr/output/messages/sw_a5_29.py b/gestionatr/output/messages/sw_a5_29.py new file mode 100644 index 00000000..e67c6b89 --- /dev/null +++ b/gestionatr/output/messages/sw_a5_29.py @@ -0,0 +1,73 @@ +# -*- coding: utf-8 -*- + +from libcomxml.core import XmlModel, XmlField +from gestionatr.output.messages.base_gas import Cabecera + + +class MensajeA529(XmlModel): + _sort_order = ('mensaje', 'cabecera', 'a529') + + def __init__(self): + self.doc_root = None + self.mensaje = XmlField( + 'sctdapplication', attributes={'xmlns': 'http://localhost/sctd/A529'} + ) + self.cabecera = Cabecera() + self.a529 = A529() + super(MensajeA529, self).__init__('sctdapplication', 'mensaje') + + +class A529(XmlModel): + _sort_order = ( + 'a529', 'fechacreacion', 'horacreacion', 'cups', 'historicoconsumo', 'validacioncliente' + ) + + def __init__(self): + self.a529 = XmlField('a529') + self.fechacreacion = XmlField('fechacreacion') + self.horacreacion = XmlField('horacreacion') + self.cups = XmlField('cups') + self.historicoconsumo = XmlField('historicoconsumo') + self.validacioncliente = ValidacionClienteA529() + + super(A529, self).__init__('a529', 'a529') + + +class ValidacionClienteA529(XmlModel): + _sort_order = ( + 'validacioncliente', 'tipodocumento', 'numdocumento' + ) + + def __init__(self): + self.validacioncliente = XmlField('validacioncliente') + self.tipodocumento = XmlField('tipodocumento') + self.numdocumento = XmlField('numdocumento') + + super(ValidacionClienteA529, self).__init__('validacioncliente', 'validacioncliente') + + +GAS_ENVELOPE_TEMPLATE_CHATGPT = """ + + + + + +{xml_str} + + + + +""" + + +ENVELOP_GAS_BY_DISTR = { + 'altres': { + 'template': GAS_ENVELOPE_TEMPLATE_CHATGPT, + 'extra_headers': { + "soapAction": "" + }, + }, +}