|
| 1 | +#!/usr/bin/python |
| 2 | +# -*- coding: utf-8 -*- |
| 3 | + |
| 4 | +"Aplicativo Visual (Front-end) Remito Electrónico (COT) ARBA" |
| 5 | + |
| 6 | +from __future__ import with_statement |
| 7 | + |
| 8 | +__author__ = "Mariano Reingart ([email protected])" |
| 9 | +__copyright__ = "Copyright (C) 2013- Mariano Reingart" |
| 10 | +__license__ = "LGPL 3.0" |
| 11 | + |
| 12 | +import datetime |
| 13 | +import decimal |
| 14 | +import time |
| 15 | +import os |
| 16 | +import fnmatch |
| 17 | +import shelve |
| 18 | +import sys |
| 19 | + |
| 20 | +# importar gui2py (atajos) |
| 21 | + |
| 22 | +import gui |
| 23 | + |
| 24 | +# establecer la configuración regional por defecto: |
| 25 | +import wx, locale |
| 26 | +if sys.platform == "win32": |
| 27 | + locale.setlocale(locale.LC_ALL, 'Spanish_Argentina.1252') |
| 28 | +elif sys.platform == "linux2": |
| 29 | + locale.setlocale(locale.LC_ALL, 'es_AR.utf8') |
| 30 | +loc = wx.Locale(wx.LANGUAGE_DEFAULT, wx.LOCALE_LOAD_DEFAULT) |
| 31 | + |
| 32 | +# importar el módulo principal de pyafipws para remito electrónico: |
| 33 | + |
| 34 | +from cot import COT |
| 35 | + |
| 36 | +# --- here goes your event handlers --- |
| 37 | + |
| 38 | + |
| 39 | +# --- gui2py designer generated code starts --- |
| 40 | + |
| 41 | +with gui.Window(name='mywin', title=u'COT: Remito Electr\xf3nico ARBA', |
| 42 | + resizable=True, height='450px', left='180', top='24', |
| 43 | + width='550px', bgcolor=u'#E0E0E0', fgcolor=u'#4C4C4C', |
| 44 | + image='', ): |
| 45 | + gui.StatusBar(name='statusbar', ) |
| 46 | + with gui.Panel(label=u'', name='panel', image='', ): |
| 47 | + gui.TextBox(name='usuario', left='299', top='10', width='105', |
| 48 | + value=u'20267565393', ) |
| 49 | + gui.TextBox(name='clave', password=True, left='455', top='10', |
| 50 | + width='75', ) |
| 51 | + gui.Line(name='line_25_556', height='3', left='24', top='390', |
| 52 | + width='499', ) |
| 53 | + gui.Button(label=u'Salir', name='salir', left='440', top='394', |
| 54 | + width='85', onclick='import sys; sys.exit(0)', ) |
| 55 | + gui.ComboBox(name=u'url', |
| 56 | + text=u'http://cot.test.arba.gov.ar/TransporteBienes/SeguridadCliente/presentarRemitos.do', |
| 57 | + height='29', left='79', top='42', width='250', |
| 58 | + bgcolor=u'#FFFFFF', |
| 59 | + data_selection=u'http://cot.test.arba.gov.ar/TransporteBienes/SeguridadCliente/presentarRemitos.do', |
| 60 | + fgcolor=u'#4C4C4C', |
| 61 | + items=[u'https://cot.arba.gov.ar/TransporteBienes/SeguridadCliente/presentarRemitos.do', u'http://cot.test.arba.gov.ar/TransporteBienes/SeguridadCliente/presentarRemitos.do'], |
| 62 | + selection=1, |
| 63 | + string_selection=u'http://cot.test.arba.gov.ar/TransporteBienes/SeguridadCliente/presentarRemitos.do', ) |
| 64 | + gui.Label(name='lblTest_273_363', height='17', left='234', top='15', |
| 65 | + width='58', text=u'Usuario:', ) |
| 66 | + gui.Label(name='lblTest_273', height='17', left='410', top='14', |
| 67 | + width='58', text=u'Clave:', ) |
| 68 | + gui.Gauge(name='gauge', height='18', left='20', top='365', |
| 69 | + width='507', ) |
| 70 | + gui.Label(id=228, name='lblTest_228', height='17', left='341', |
| 71 | + top='47', width='32', text=u'Carpeta:', ) |
| 72 | + with gui.ListView(id=213, name=u'remitos', height='74', left='20', |
| 73 | + top='180', width='510', item_count=0, sort_column=0, ): |
| 74 | + gui.ListColumn(name=u'nro', text=u'N\xb0 \xdanico Remito', |
| 75 | + width=250, ) |
| 76 | + gui.ListColumn(name=u'proc', text=u'Procesado', ) |
| 77 | + with gui.ListView(name=u'archivos', height='99', left='21', top='77', |
| 78 | + width='509', item_count=0, sort_column=2, ): |
| 79 | + gui.ListColumn(name=u'txt', text='Archivo TXT', width=200, ) |
| 80 | + gui.ListColumn(name=u'xml', text='Archivo XML', ) |
| 81 | + gui.ListColumn(name=u'cuit', text='CUIT Empresa', ) |
| 82 | + gui.ListColumn(name=u'nro', text=u'N\xb0 Comprobante', ) |
| 83 | + gui.ListColumn(name=u'md5', text=u'C\xf3digo Integridad', ) |
| 84 | + with gui.ListView(id=309, name=u'errores', height='99', left='20', |
| 85 | + top='259', width='510', item_count=0, sort_column=0, ): |
| 86 | + gui.ListColumn(name=u'codigo', text=u'C\xf3digo', width=100, ) |
| 87 | + gui.ListColumn(name=u'descripcion', text=u'Descripci\xf3n Error', |
| 88 | + width=400, ) |
| 89 | + gui.TextBox(id=488, mask='date', name='fecha', |
| 90 | + left='101', top='10', width='127', enabled=False, |
| 91 | + value=datetime.date(2014, 4, 5), ) |
| 92 | + gui.CheckBox(label=u'Fecha:', name=u'filtrar_fecha', height='24', |
| 93 | + left='22', top='11', width='73', |
| 94 | + tooltip=u'filtrar por fecha', ) |
| 95 | + gui.Label(id=2084, name='lblTest_228_2084', height='17', left='24', |
| 96 | + top='47', width='32', text=u'URL:', ) |
| 97 | + gui.ComboBox(id=961, name=u'carpeta', text=u'datos', height='29', |
| 98 | + left='412', top='42', width='118', bgcolor=u'#FFFFFF', |
| 99 | + data_selection=u'datos', fgcolor=u'#4C4C4C', |
| 100 | + items=[u'datos', u'procesados'], selection=0, |
| 101 | + string_selection=u'datos', ) |
| 102 | + gui.Button(label=u'Procesar', name=u'procesar', left='20', top='394', |
| 103 | + tooltip="Presentar el remito en ARBA", |
| 104 | + width='85', default=True, fgcolor=u'#4C4C4C', ) |
| 105 | + gui.CheckBox(label=u'Auto:', name=u'auto', height='24', |
| 106 | + left='320', top='396', width='73', value=True, |
| 107 | + tooltip=u'procesar automaticamente', ) |
| 108 | + gui.Button(label=u'Mover Procesados', name=u'mover', left='112', |
| 109 | + top='394', width='166', fgcolor=u'#4C4C4C', ) |
| 110 | + |
| 111 | +# --- gui2py designer generated code ends --- |
| 112 | + |
| 113 | +# get a reference to the Top Level Window (used by designer / events handlers): |
| 114 | +mywin = gui.get("mywin") |
| 115 | +panel = mywin['panel'] |
| 116 | + |
| 117 | +# Manejo simple de claves: |
| 118 | + |
| 119 | +passwd_db = shelve.open("passwd") |
| 120 | + |
| 121 | +def getpass(username): |
| 122 | + password = passwd_db.get(str(username)) |
| 123 | + if not password: |
| 124 | + password = gui.prompt(message=u"Ingrese contraseña", |
| 125 | + title="Usuario: %s" % username, |
| 126 | + password=True) or "" |
| 127 | + return password |
| 128 | + |
| 129 | +def setpass(username, password): |
| 130 | + passwd_db[str(username)] = password |
| 131 | + |
| 132 | +def grabar_clave(evt): |
| 133 | + setpass(panel['usuario'].value, panel['clave'].value) |
| 134 | + |
| 135 | +# asignar controladores |
| 136 | + |
| 137 | +cot = COT() |
| 138 | + |
| 139 | +def listar_archivos(evt=None): |
| 140 | + print("listando...", datetime.datetime.now()) |
| 141 | + # cargar listado de archivos a procesar (y su correspondiente respuesta): |
| 142 | + lv = panel['archivos'] |
| 143 | + lv.clear() |
| 144 | + panel['remitos'].clear() |
| 145 | + panel['errores'].clear() |
| 146 | + # obtengo el fitlro de fecha (si esta habilitado): |
| 147 | + if panel['filtrar_fecha'].value: |
| 148 | + fecha = panel['fecha'].value.strftime("%Y%m%d") |
| 149 | + else: |
| 150 | + fecha = None |
| 151 | + carpeta = panel['carpeta'].text or "." |
| 152 | + for fn in os.listdir(carpeta): |
| 153 | + if fnmatch.fnmatch(fn, 'TB_???????????_*.txt'): |
| 154 | + # filtro por fecha (si esta tildado): |
| 155 | + # TB_20111111112_000000_20080124_000001.txt |
| 156 | + fecha_fn = fn[22:30] |
| 157 | + if fecha and fecha != fecha_fn: |
| 158 | + continue |
| 159 | + txt = fn |
| 160 | + xml = os.path.splitext(fn)[0] + ".xml" |
| 161 | + if not os.path.exists(os.path.join(carpeta, xml)): |
| 162 | + xml = "" |
| 163 | + elif panel['auto'].value: |
| 164 | + continue |
| 165 | + lv.items[fn] = {'txt': txt, 'xml': xml} |
| 166 | + |
| 167 | +def procesar_archivos(evt=None): |
| 168 | + print("procesando...", datetime.datetime.now()) |
| 169 | + # establezco la barra de progreso con la cantidad de archivos: |
| 170 | + panel['gauge'].max = len(panel['archivos'].items) |
| 171 | + # recorro los archivos a procesar: |
| 172 | + for i, item in enumerate(panel['archivos'].items): |
| 173 | + panel['gauge'].value = i + 1 |
| 174 | + procesar_archivo(item, enviar=True) |
| 175 | + |
| 176 | + if panel['auto'].value: |
| 177 | + mover_archivos() |
| 178 | + print("re-agendando...", datetime.datetime.now()) |
| 179 | + gui.call_later(5000, listar_archivos) |
| 180 | + gui.call_later(10000, procesar_archivos) |
| 181 | + |
| 182 | + |
| 183 | +def cargar_archivo(evt): |
| 184 | + # obtengo y proceso el archivo seleccionado: |
| 185 | + item = evt.target.get_selected_items()[0] |
| 186 | + procesar_archivo(item) |
| 187 | + |
| 188 | +def abrir_archivo(evt): |
| 189 | + # obtengo y proceso el archivo seleccionado: |
| 190 | + item = evt.target.get_selected_items()[0] |
| 191 | + fn = os.path.join(panel['carpeta'].text, item['txt']) |
| 192 | + try: |
| 193 | + os.startfile(fn) |
| 194 | + except AttributeError: |
| 195 | + os.system("""gedit "%s" """ % fn) |
| 196 | + |
| 197 | +def procesar_archivo(item, enviar=False): |
| 198 | + "Enviar archivo a ARBA y analizar la respuesta" |
| 199 | + |
| 200 | + # establezco credenciales: |
| 201 | + cuit = item['txt'][3:14] |
| 202 | + cot.Usuario = panel['usuario'].value = cuit |
| 203 | + cot.Password = panel['clave'].value = getpass(cuit) |
| 204 | + cot.Conectar(panel['url'].text, trace=True) |
| 205 | + |
| 206 | + # obtengo la ruta al archivo de texto y xml |
| 207 | + carpeta = panel['carpeta'].text |
| 208 | + fn = os.path.join(carpeta, item['txt']) |
| 209 | + xml = item['xml'] |
| 210 | + if xml: |
| 211 | + xml = os.path.join(carpeta, xml) |
| 212 | + elif not enviar: |
| 213 | + return |
| 214 | + |
| 215 | + # llamada al webservice: |
| 216 | + cot.PresentarRemito(fn, testing=xml) |
| 217 | + |
| 218 | + # grabo el xml devuelto: |
| 219 | + if not xml: |
| 220 | + xml = os.path.splitext(fn)[0] + ".xml" |
| 221 | + with open(xml, "w") as f: |
| 222 | + f.write(cot.XmlResponse) |
| 223 | + |
| 224 | + if cot.Excepcion and enviar and not panel['auto'].value: |
| 225 | + gui.alert(cot.Traceback, cot.Excepcion) |
| 226 | + |
| 227 | + if cot.TipoError and enviar and not panel['auto'].value: |
| 228 | + gui.alert(cot.MensajeError, "Error %s: %s" % (cot.TipoError, cot.CodigoError)) |
| 229 | + |
| 230 | + # actualizo los datos devueltos en el listado |
| 231 | + item['cuit'] = cot.CuitEmpresa |
| 232 | + item['nro'] = cot.NumeroComprobante |
| 233 | + item['md5'] = cot.CodigoIntegridad |
| 234 | + #assert item['txt'] == cot.NombreArchivo |
| 235 | + |
| 236 | + # limpio, enumero y agrego los remitos para el archivo seleccionado: |
| 237 | + remitos = panel['remitos'] |
| 238 | + item['remitos'] = [] |
| 239 | + panel['errores'].items = [] |
| 240 | + remitos.items = [] |
| 241 | + i = 0 |
| 242 | + while cot.LeerValidacionRemito(): |
| 243 | + print "REMITO", i |
| 244 | + errores = [] |
| 245 | + remito = {'nro': cot.NumeroUnico, 'proc': cot.Procesado, |
| 246 | + 'errores': errores} |
| 247 | + remitos.items[i] = remito |
| 248 | + item['remitos'].append(remito) |
| 249 | + i += 1 |
| 250 | + while cot.LeerErrorValidacion(): |
| 251 | + print "Error Validacion:", "|", cot.CodigoError, "|", cot.MensajeError |
| 252 | + errores.append({'codigo': cot.CodigoError, |
| 253 | + 'descripcion': cot.MensajeError}) |
| 254 | + |
| 255 | +def cargar_errores(evt): |
| 256 | + # obtengo el remito seleccionado: |
| 257 | + item = evt.target.get_selected_items()[0] |
| 258 | + # limpio, enumero y agrego los errores para el remito seleccionado: |
| 259 | + errores = panel['errores'] |
| 260 | + errores.items = [] |
| 261 | + for i, error in enumerate(item['errores']): |
| 262 | + print i, error |
| 263 | + errores.items[i] = error |
| 264 | + |
| 265 | +def filtro_fecha(evt): |
| 266 | + panel['fecha'].enabled = evt.target.value |
| 267 | + listar_archivos() |
| 268 | + |
| 269 | + |
| 270 | +def mover_archivos(evt=None): |
| 271 | + carpeta = panel['carpeta'].text |
| 272 | + if carpeta != "datos" and evt: |
| 273 | + gui.alert("No se puede mover archivos de carpeta %s" % carpeta) |
| 274 | + |
| 275 | + i = 0 |
| 276 | + print panel['archivos'].items |
| 277 | + for item in panel['archivos'].items: |
| 278 | + procesado = all([remito.get('proc', 'NO') == 'SI' |
| 279 | + for remito in item.get('remitos', [])]) |
| 280 | + if procesado and item.get('remitos'): |
| 281 | + for fn in (item['txt'], item['xml']): |
| 282 | + fn0 = os.path.join("datos", fn) |
| 283 | + fn1 = os.path.join("procesados", fn) |
| 284 | + try: |
| 285 | + os.rename(fn0, fn1) |
| 286 | + i += 1 |
| 287 | + except Exception, e: |
| 288 | + gui.alert(unicode(e), "No se puede mover %s" % fn) |
| 289 | + if evt: |
| 290 | + gui.alert("Se movieron: %s archivos" % i) |
| 291 | + listar_archivos() |
| 292 | + |
| 293 | + |
| 294 | +panel['archivos'].onitemselected = cargar_archivo |
| 295 | +panel['archivos'].onmousedclick = abrir_archivo |
| 296 | +panel['remitos'].onitemselected = cargar_errores |
| 297 | +panel['filtrar_fecha'].onclick = filtro_fecha |
| 298 | +panel['fecha'].onchange = listar_archivos |
| 299 | +panel['carpeta'].onchange = listar_archivos |
| 300 | +panel['mover'].onclick = mover_archivos |
| 301 | +panel['procesar'].onclick = procesar_archivos |
| 302 | +panel['clave'].onchange = grabar_clave |
| 303 | + |
| 304 | + |
| 305 | +if __name__ == "__main__": |
| 306 | + mywin.show() |
| 307 | + mywin.title = u"%s - %s" % (mywin.title, cot.Version.decode("latin1")) |
| 308 | + mywin['statusbar'].text = "" |
| 309 | + listar_archivos() |
| 310 | + gui.main_loop() |
| 311 | + passwd_db.close() |
| 312 | + |
0 commit comments