Compare commits

...

1326 Commits

Author SHA1 Message Date
El Mau 34a981a2dd Fix: en mercancias en Carta porte al generar el PDF 2024-04-10 20:54:26 -06:00
El Mau 66549e271b Fix: en mercancias en Carta porte al generar el PDF 2024-04-10 20:54:01 -06:00
El Mau 884b10bd79 Fix in js 2024-04-02 10:59:54 -06:00
El Mau fd050159ba Fix in js 2024-04-02 10:59:23 -06:00
El Mau 8199667188 Fix in Carta Porte 3.0 2024-04-02 10:31:58 -06:00
El Mau 05d3f617eb Fix in Carta Porte 2024-04-02 10:31:28 -06:00
El Mau 0ec4f0c2c9 Add support for Carta Porte 3.0 2024-04-02 00:25:41 -06:00
El Mau 29bfb42abc PDF from Carta Porte v3 2024-04-02 00:20:44 -06:00
El Mau 7f129b84b2 Stamp Carta Porte v3 2024-04-01 14:09:57 -06:00
El Mau 13a3c106c6 UI for Carta Porte v3 2024-03-31 22:28:13 -06:00
El Mau 0c7abf634b Update changelog 2024-03-04 14:07:10 -06:00
El Mau f0c4423fc8 Add support for Comercio Exterior 2.0 2024-01-24 15:07:26 -06:00
El Mau d925587a98 PDF from CE in version 2.0 2024-01-24 13:45:23 -06:00
El Mau 510ebdd1e5 Stamp CE in version 2.0 2024-01-21 23:20:27 -06:00
El Mau d2e92098da Import from ODS 2024-01-18 22:29:49 -06:00
El Mau c1ce206835 Import from json 2024-01-17 23:49:58 -06:00
El Mau 1efa280920 Import from json 2024-01-16 22:51:58 -06:00
El Mau 6fd8f9e2ea Add UI for CE 2024-01-14 22:42:26 -06:00
El Mau e3a44e6400 Add filter by day 2023-12-26 21:38:22 -06:00
El Mau 390ec60fdd Add filter by day 2023-12-26 21:37:45 -06:00
El Mau 957160755a Fix issue #98 and #107 2023-12-20 18:55:18 -06:00
El Mau 070ee31a1b Update changelog 2023-12-20 18:54:25 -06:00
El Mau 2620159408 Fix issue #98 2023-12-20 18:51:38 -06:00
El Mau 4d9bc4a172 Fix issue #107 2023-12-20 18:36:44 -06:00
El Mau 17ed56e9d7 Fix for CFDI egreso for General Public 2023-11-05 22:15:17 -06:00
El Mau 5057ac57ae Fix for CFDI egreso for General Public 2023-11-05 22:13:45 -06:00
El Mau 410a7080da Modify name for Public RFC 2023-10-30 16:51:38 -06:00
El Mau edd8d560bd Update changelog and version 2023-10-30 00:12:40 -06:00
El Mau bd1699d082 Add support for change time zone 2023-10-30 00:02:48 -06:00
El Mau 6d4023bb17 Update SATA catalog 2023-10-29 22:40:05 -06:00
Mauricio beda45690f Fix in retention tax for RESICOs 2023-07-06 11:16:04 -06:00
Mauricio 8feba167af Fix in retention tax for RESICOs 2023-07-06 11:15:15 -06:00
Mauricio 4dbb797d99 Fix tax retention in pay complement 2023-06-14 21:42:17 -06:00
Mauricio cd424b71dc Update changelog 2023-06-14 20:20:30 -06:00
Mauricio ea13d11319 Fix - Retencion tax in pay complement 2023-06-14 20:19:21 -06:00
Mauricio d9c863820a Merge pull request 'Añade información de desarrollo al readme' (#105) from categulario/empresa-libre:improve-readme into develop
Reviewed-on: #105
2023-06-06 15:23:46 -06:00
Categulario 6815d6c64c
improve readme with information on how to run locally 2023-06-05 22:34:09 -06:00
Mauricio 402cb7d70c Fix #103 2023-06-05 16:25:37 -06:00
Mauricio a5f1114774 Update changelog 2023-06-05 16:25:07 -06:00
Mauricio 2588a5f0b8 Fix #103 2023-06-05 16:22:53 -06:00
Mauricio eeae157772 Fix in complement pays 2023-05-25 21:59:13 -06:00
Mauricio 7cad4093d4 Fix in global invoice 2023-04-20 13:21:19 -06:00
Mauricio 7caf6423e1 Fix in global invoice 2023-04-20 13:20:48 -06:00
Mauricio 8a3aea41eb Add index in tickets 2023-04-15 19:23:56 -06:00
Mauricio bd49356d3f Add index in tickets 2023-04-15 18:13:45 -06:00
Mauricio 3b54ef0a36 Fix in global invoice 2023-04-14 17:56:12 -06:00
Mauricio 497a4be392 Update version 2023-04-14 17:55:48 -06:00
Mauricio 7fc6264322 Fix in global invoice 2023-04-14 17:55:00 -06:00
Mauricio c5d0e97212 Fix: al obtener la clave del sat al facturar en lote 2023-04-01 15:10:07 -06:00
Mauricio 5bbad9ef58 Fix: al obtener la clave del sat al facturar en lote 2023-04-01 15:09:27 -06:00
Mauricio 8e7aa23d1b Validate exists partner in edit 2023-03-29 17:36:17 -06:00
Mauricio f3d8b1dae5 Fix issue #97 2023-03-29 13:32:31 -06:00
Mauricio 2c684ae6bd Fix issue #97 2023-03-29 13:31:23 -06:00
el Mau febea3f289 Fix #96 2023-03-21 10:56:55 -06:00
el Mau 5facb96662 Fix #96 2023-03-21 10:55:16 -06:00
el Mau 6411c2e51e Fix in taxes in invoice pay 2023-03-13 17:16:30 -06:00
el Mau eeb128fda6 Fix in taxes in invoice pay 2023-03-13 17:16:15 -06:00
el Mau a078e27703 Fix in taxes in invoice pay 2023-03-13 14:01:26 -06:00
el Mau 28ef53043f Fix in taxes in invoice pay 2023-03-13 13:59:24 -06:00
el Mau e956c243f7 Fix in invoice pay 2023-03-10 14:56:19 -06:00
el Mau 1b33413de2 Fix in invoice pay 2023-03-10 14:55:57 -06:00
el Mau 64541a84b9 Fix in invoice pay with tax IVA 0 2023-03-07 11:53:28 -06:00
el Mau 834c1faae6 Fix in invoice pay with tax IVA 0 2023-03-07 11:53:04 -06:00
el Mau affe57bfcd Add validate for cancel movement bank 2023-03-06 16:10:07 -06:00
el Mau 4dc54ade1e Add validate for cancel movement bank 2023-03-06 16:09:37 -06:00
el Mau e72739a5fe Add cancel invoice pay in admin list 2023-03-06 00:48:01 -06:00
el Mau dd7b9c1680 Add cancel invoice pay in admin list 2023-03-06 00:47:34 -06:00
el Mau 9823bd967f Fix for exempted tax 2023-03-03 01:38:37 -06:00
el Mau aee467d98f Fix for exempted tax 2023-03-03 01:38:19 -06:00
el Mau 64917527f7 Validate tax object 2023-03-01 22:24:26 -06:00
el Mau 5f56179ebb Validate tax object 2023-03-01 22:24:00 -06:00
el Mau 955a0348f8 Can select month in global invoice 2023-03-01 18:45:50 -06:00
el Mau 0826b0cea2 Can select month in global invoice 2023-03-01 18:45:21 -06:00
el Mau 8b0f9fbc64 Can select global options for public invoice 2023-03-01 18:17:56 -06:00
el Mau f3d827364b Can select global options for public invoice 2023-03-01 18:16:52 -06:00
el Mau 50cbb3bae8 Fix in tax IVA retention 2023-03-01 14:32:33 -06:00
el Mau a7c8d80219 Fix in tax IVA retention 2023-03-01 14:32:01 -06:00
el Mau ca9d7a02e3 Update requirements.txt 2023-02-24 14:08:14 -06:00
el Mau 31fcb63e51 Update requirements.txt 2023-02-24 14:07:44 -06:00
el Mau bd7a31ea8a Fix global invoice 2023-02-24 12:45:52 -06:00
el Mau 6a6b6ca395 Fix global invoice 2023-02-23 18:20:10 -06:00
el Mau 3148c5ba82 Fix type change in deposit 2023-02-22 11:07:38 -06:00
el Mau 5f62ad7750 Fix type change in deposit 2023-02-22 11:06:37 -06:00
el Mau ff44162622 Fix type change in deposit 2023-02-21 22:16:25 -06:00
el Mau a37b3a5c23 Fix type change in deposit 2023-02-21 22:09:42 -06:00
el Mau 4fdea6173b Fix invoice pay in others currencies 2023-02-20 19:15:40 -06:00
el Mau 968c2744ec Fix pay invoice in other currencies 2023-02-20 19:10:29 -06:00
el Mau c9d85ca2e7 Fix make PDF from pays 1.0 2023-02-19 14:00:06 -06:00
el Mau 93e64eafd8 Fix make PDF from pays 1.0 2023-02-18 12:56:22 -06:00
el Mau 1984103ff5 Fix make PDF from pays 1.0 2023-02-18 12:40:05 -06:00
el Mau e4ba06c0ff Fix in tax object 04 2023-02-17 14:24:33 -06:00
el Mau 38bb267e55 Fix in tax object 04 2023-02-17 14:24:17 -06:00
el Mau 602467c8e9 Fix in tax object 04 2023-02-17 13:44:42 -06:00
el Mau 4020237f68 Fix in tax object 04 2023-02-17 13:43:43 -06:00
el Mau 933c9820e6 Fix with tax exento 2023-02-16 23:38:03 -06:00
el Mau 51daf0ad5e Stamp with tax exento 2023-02-16 23:09:35 -06:00
el Mau 709c524830 Show message when delete product 2023-02-16 22:30:08 -06:00
el Mau 434e15bb5b Fix in cancel with Finkok 2023-02-08 21:43:38 -06:00
el Mau ae5949c529 Fix in cancel with Finkok 2023-02-08 19:25:53 -06:00
el Mau af5fa3c812 Fix in templates 2023-02-01 22:45:48 -06:00
el Mau 6d399fcb39 Fix in templates 2023-02-01 22:45:24 -06:00
el Mau 8003fc42b9 Add template in json for 4.0 2023-02-01 22:27:13 -06:00
el Mau 7a0fb2b243 Add template in json for 4.0 2023-02-01 22:26:10 -06:00
el Mau 83bebde169 Fix use cfdi P01 2023-02-01 08:56:55 -06:00
el Mau 76d5f51d78 Fix use cfdi P01 2023-02-01 08:56:28 -06:00
el Mau 5f641c632e Get message error in import invoice by lote 2023-01-31 22:07:43 -06:00
el Mau 85eb7cd58c Get message error in import invoice by lote 2023-01-31 22:07:06 -06:00
el Mau 1c11a1b013 Fix get sum by tax ISR 2023-01-30 22:16:07 -06:00
el Mau 701bb68478 Fix sum for ISR tax 2023-01-30 22:10:25 -06:00
el Mau f13491d983 Fix get sum basy pay for IVA 2023-01-30 20:12:09 -06:00
el Mau 4020365616 Fix get base pay 2023-01-30 20:09:57 -06:00
el Mau a90f218c76 Fix node tax in invoice pay 2023-01-27 12:00:36 -06:00
el Mau 37995befd8 Fix node tax in invoice pay 2023-01-27 12:00:09 -06:00
el Mau 746c827492 Validate regimen fiscal in pay invoice 2023-01-23 16:18:11 -06:00
el Mau 3cea54c07c Validate regimen fiscal in pay invoice 2023-01-23 16:17:47 -06:00
el Mau 661eff1dc3 Validate regimen fiscal in pay invoice 2023-01-23 16:05:38 -06:00
el Mau cd11456ec3 Validate regimen fiscal in pay invoice 2023-01-23 16:05:22 -06:00
el Mau 2526a8f5aa Validate regimen fiscal in pay invoice 2023-01-23 15:39:04 -06:00
el Mau 0a13004419 Validate regimen fiscal in pay invoice 2023-01-23 15:38:28 -06:00
el Mau 296874a9b1 Admin regimenes 2023-01-22 12:57:35 -06:00
el Mau 34b1af1319 Admin regimenes 2023-01-21 23:19:44 -06:00
el Mau 5fbf80fb92 Show keys in invoice 2023-01-21 20:19:28 -06:00
el Mau b56fee5f73 Show key by regimen in invoice 2023-01-21 20:06:31 -06:00
el Mau c9ca45b60d Add keys in regimenes fiscales 2023-01-21 19:46:06 -06:00
el Mau 86e7f50621 Fix in validate CP 2023-01-13 22:25:48 -06:00
el Mau 2cef108856 Fix in validate CP 2023-01-13 22:21:03 -06:00
el Mau 260ba62b2e Fix in validate CP 2023-01-12 12:02:02 -06:00
el Mau 76d15e482b Fix in validate CP 2023-01-12 12:01:37 -06:00
el Mau 8e2446e8f5 Fix in complement pay without taxes 2023-01-12 00:33:39 -06:00
el Mau 482a0385cd Fix in complement pay without taxes 2023-01-12 00:33:11 -06:00
el Mau 29e02e649d Fix error in nomina 2023-01-11 00:27:42 -06:00
el Mau 5aece843bf Fix error in nomina 2023-01-11 00:24:35 -06:00
el Mau d47e059d93 CFDI 4.0 2023-01-10 00:07:17 -06:00
el Mau 89f79de9b6 Add template for donatarias 2023-01-10 00:06:40 -06:00
el Mau fbaa85f13a Fix #84 2023-01-09 23:11:52 -06:00
el Mau bed6039fff Fix #84 2023-01-09 17:46:40 -06:00
el Mau acbaecbfc5 Fix in preinvoice 2023-01-09 15:18:08 -06:00
el Mau 8dddb7b293 Update changelog 2023-01-08 19:29:36 -06:00
el Mau 24777f691e CFDI 4.0 2023-01-07 07:37:32 -06:00
el Mau 1d270aa478 Add templates 2023-01-06 21:42:46 -06:00
el Mau 8872af8c50 Fix in unit in invoice 2023-01-06 19:04:50 -06:00
el Mau d57ded161c Proporcional taxes in pays complements 2023-01-04 18:29:30 -06:00
el Mau 0c8d6d3cea Fix #83 2023-01-04 13:02:33 -06:00
el Mau f14c1f74d8 PDF for pays complement 2023-01-02 22:59:39 -06:00
el Mau 210f50a7d7 Add months in global invoice 2023-01-02 14:56:34 -06:00
el Mau 5418b23200 Plantillas 2023-01-01 22:58:58 -06:00
el Mau a13c7c0a1d Factura global 2022-12-31 17:30:01 -06:00
el Mau a6276a33fb Update temaple for Leyendas Fiscales 2022-10-27 22:37:05 -05:00
el Mau e3bd1e8871 Fix - Query SAT 2022-10-25 20:22:48 -05:00
el Mau 2ddb3d6b75 Fix - Cancel v4.0 2022-10-25 14:59:37 -05:00
Mauricio Baeza 4cda9f7f21 Update CP Db 2022-08-02 22:13:05 -05:00
Mauricio Baeza 7c184c9513 Update CP db 2022-08-01 21:31:44 -05:00
Mauricio Baeza aea8aeaecf Fix in import employes 2022-06-17 13:30:21 -05:00
Mauricio Baeza e08e4e038f Set name client to upper case 2022-06-09 13:50:33 -05:00
Mauricio Baeza 496dcf7162 Add some complements 2022-06-05 20:24:08 -05:00
Mauricio Baeza f8cd99084a Stamp Nomina with Cfdi 4.0 2022-06-04 21:52:28 -05:00
Mauricio Baeza 1e824bd841 Fix set current regimen in edit 2022-06-02 22:46:31 -05:00
Mauricio Baeza bc0cd9f3e1 Fix get data foro version 3.3 2022-05-31 22:14:17 -05:00
Mauricio Baeza ae259e461b Add template for CFDI 4.0 2022-05-30 13:30:47 -05:00
Mauricio Baeza 1a507d2eeb Add template for CFDI 4.0 2022-05-29 22:57:26 -05:00
Mauricio Baeza f766f92618 Minor changes 2022-05-25 23:18:21 -05:00
Mauricio Baeza ad56356a6f Quitar referencia a paquetes 2022-05-16 14:44:42 -05:00
El Mau c6cf33c4c4 Obtener datos de periodicidad para factura glbal 2022-04-18 21:45:30 -05:00
El Mau dbc3910297 Guardar periodicidad de factura global 2022-04-17 16:33:50 -05:00
El Mau 6c78a282fe Agregar periodicidad en UI para factura del día 2022-04-17 16:01:05 -05:00
El Mau f8c2c5e2d6 Agregar objeto de impuestos a productos 2022-04-16 21:58:42 -05:00
El Mau 7d40e79f3c Quitar validacion de empaques 2022-04-15 22:13:49 -05:00
El Mau a36034a476 Agregar controles para objeto de impuesto en productos 2022-04-14 23:01:02 -05:00
El Mau de2ab5cedd Obtener regimen fiscal del receptor al facturar desde tickets 2022-04-11 22:47:37 -05:00
El Mau b60e2d6a37 Timbrado con público en general 2022-04-07 21:31:42 -05:00
El Mau d9021cca4a Seleccionar regimen para público en general 2022-04-05 22:05:16 -05:00
El Mau 7bbef31936 Regresar regimenes del cliente al seleccionar 2022-04-03 23:00:02 -05:00
El Mau 74e7f12088 Regresar CP del cliente al refacturar 2022-04-02 23:13:37 -06:00
El Mau 20c0757f04 Regresar productos en orden al refacturar 2022-04-02 23:13:37 -06:00
El Mau 3a5fbc609b Regresar productos en orden al refacturar 2022-04-01 09:50:10 -06:00
El Mau 5e888d2337 Regresar productos en orden al refacturar 2022-04-01 09:49:15 -06:00
El Mau d97fd4867a Cambiar URLs para timbrado con Comercio Digital 2022-03-29 18:27:40 -06:00
El Mau e58b8f90af Cambiar demo de Finkok a https 2022-03-29 17:29:21 -06:00
El Mau 50d4a3f4b7 Soporte inicial para CFDI 4.0 2022-03-29 15:40:14 -06:00
El Mau 1978edfaf0 Tablas para borrar 2022-03-28 22:40:41 -06:00
El Mau 7ca87d1811 Timbrado de Complemento de Pagos 2.0 2022-03-28 22:40:41 -06:00
El Mau a7bc6d6f6c Agregar xlst para pagos 2.0 2022-03-28 22:40:41 -06:00
El Mau 1991c68b3b Soporte basico para Comercio Exterior 2022-03-28 22:39:55 -06:00
El Mau bebcd29710 Agregar cadena para Cfdi 4 2022-03-28 22:37:08 -06:00
El Mau de856f28ab Soporte basico para complemento Comercio Exterior 2022-03-28 21:34:10 -06:00
El Mau e726cda085 Soporte basico para complemento Comercio Exterior 2022-03-28 21:33:39 -06:00
El Mau eee386a97b Soporte basico para complemento Comercio Exterior 2022-03-17 21:22:27 -06:00
El Mau ae21403c59 Error al timbrar nomina 2022-03-10 20:26:27 -06:00
El Mau 110d41cf6e Actualizar VERSION 2022-03-10 20:25:51 -06:00
El Mau bd6fb65a1b Fix - Problema al timbra nómina con Comercio Digital 2022-03-10 19:24:29 -06:00
El Mau b68d79d0ca Fix issue #54 2022-02-18 14:59:01 -06:00
El Mau 670aca3886 Fix - Issue #54 2022-02-18 14:57:13 -06:00
El Mau 68c0203039 Fix issue #53 2022-02-15 23:51:27 -06:00
El Mau b2cdd0efbe Fix - Issue #53 2022-02-15 23:50:02 -06:00
El Mau 51af15f311 Error al generar Carta Porte sin remolque 2022-02-01 21:31:49 -06:00
El Mau edf35aa5ce Error al cancelar con Finkok 2022-02-01 21:31:06 -06:00
El Mau b19d087d10 Error al generar Carta Porte sin remolque 2022-01-31 18:48:43 -06:00
El Mau 64ef75fbf5 Mejoras en Carta Porte 2022-01-30 14:50:05 -06:00
El Mau d6a8c1e3fa Actualizar lista de cambios 2022-01-30 14:48:32 -06:00
El Mau a7080bf954 Agregar validación para distancia en origen de Carta Porte 2022-01-30 14:47:34 -06:00
El Mau 3ddf99075e Reporte #49, arreglado 2022-01-29 20:47:30 -06:00
El Mau bd6ba5d4d0 Mejoras en Carta Porte 2022-01-27 22:18:28 -06:00
El Mau 6a18934b19 Actualizar lista de cambios 2022-01-27 22:17:41 -06:00
El Mau 61548c9135 Buscar Estado y Municipio por CP en Carta Porte 2022-01-27 22:14:54 -06:00
El Mau fe3f743e32 Fix - Al generar PDF de carta porte sin remolque 2022-01-27 18:34:55 -06:00
El Mau c83e6981ae Agregar tipos de persimo SCT para Carta Porte 2022-01-27 13:23:00 -06:00
El Mau 39b9af125c Fix - Issue #45 2022-01-27 13:10:16 -06:00
El Mau dd606be46b Arreglado el error al cancelar con CD 2022-01-26 22:32:38 -06:00
El Mau 34df8882b3 Actualizar lista de cambios 2022-01-26 22:28:15 -06:00
El Mau e992103c57 Fix - Issue #44 2022-01-26 18:12:18 -06:00
El Mau 8f74d24b14 Fix - Issue #42 2022-01-26 18:01:27 -06:00
El Mau 480ec57d1e Error al timbrar Carta Porte 2022-01-25 19:33:05 -06:00
El Mau a38247727c Error al timbrar Carta Porte 2022-01-25 19:12:41 -06:00
El Mau 702aa264d8 Agregar certificado original 2022-01-24 17:05:41 -06:00
El Mau 0422eece40 Prueba de cancelación con UUID 2022-01-24 15:45:40 -06:00
El Mau 12774af7ac Quitar nodo KeyInfo del XML de cancelación 2022-01-24 12:47:57 -06:00
El Mau b16eabd773 Quitar nodo KeyInfo del XML de cancelación 2022-01-24 12:43:28 -06:00
El Mau 1bab1cb23c Quitar todos los saltos de línea del XML de cancelación 2022-01-24 12:37:42 -06:00
El Mau 15a05e38f7 Agregar, de nuevo KeyInfo del XML de cancelación 2022-01-24 12:31:10 -06:00
El Mau a0a8e0ce62 Quitar KeyInfo del XML de cancelación 2022-01-24 12:21:21 -06:00
El Mau 825e23e369 Mostrar el request enviado 2022-01-24 12:15:27 -06:00
El Mau 121831a139 Quitar saltos de línea en XML de cancelación 2022-01-24 12:08:17 -06:00
El Mau c09a5749da Quitar declaración de enconding en XML de cancelación 2022-01-24 12:04:26 -06:00
El Mau 01ccbcabd5 Cambio de NameSpace en XML de cancelación 2022-01-24 11:32:01 -06:00
El Mau cc0eee1443 Fix - Issue #40 2022-01-23 23:11:08 -06:00
El Mau 7a88c4b97f Fix - Issue #40 2022-01-23 23:10:28 -06:00
El Mau 0e7db2b711 Verificar cancelación con certificados en CD 2022-01-22 22:16:52 -06:00
El Mau 65851182c3 Verificar cancelación con certificados en CD 2022-01-22 22:02:02 -06:00
El Mau 2de98b9b92 Verificar cancelación con certificados en CD 2022-01-22 21:54:31 -06:00
El Mau 5a24bc159d Verificar cancelación con certificados en CD 2022-01-22 21:35:53 -06:00
El Mau f84721716c Verificar cancelación con certificados en CD 2022-01-22 21:21:55 -06:00
El Mau 8b91532b82 Verificar cancelación con certificados en CD 2022-01-22 21:11:18 -06:00
El Mau 0a98338fad Fix - Issue #40 2022-01-22 20:24:06 -06:00
El Mau 7bcf6d6e3c Agregar columna método de pago al listado de facturas 2022-01-22 20:17:42 -06:00
El Mau dbc8717575 Verificar cancelación con certificados en CD 2022-01-22 20:04:12 -06:00
El Mau 0adcd7f30f Verificar cancelación con certificados en CD 2022-01-22 20:01:53 -06:00
El Mau d237b33020 Verificar cancelación con certificados en CD 2022-01-22 19:47:41 -06:00
El Mau 5b997f7858 Verificar cancelaion con certificados en CD 2022-01-22 19:34:40 -06:00
El Mau a609c0c6f5 Actualizar cancelación con XML firmado con CD 2022-01-21 17:09:29 -06:00
El Mau d70cae64b7 Actualizar cancelación con XML firmado con CD 2022-01-21 17:00:59 -06:00
El Mau 9daf07693a Corregir error al enviar correo 2022-01-20 17:04:35 -06:00
El Mau e40acd28ba Corregir el error al enviar correo 2022-01-20 17:02:11 -06:00
El Mau 88dd9ca04b Corregir el error al enviar correo 2022-01-20 13:22:51 -06:00
El Mau d2f879c224 Importar carta porte desde json 2022-01-20 00:10:22 -06:00
El Mau 4922010caf Importar carta porte desde json 2022-01-20 00:06:05 -06:00
El Mau ff8bc31f50 Agregar opción STARTTLS para envío de correo 2022-01-19 15:03:26 -06:00
El Mau e795e87461 Agregar opción STARTTLS para envío de correo 2022-01-19 14:58:30 -06:00
El Mau d2c361e174 Representación impresa para Carta Porte 2022-01-19 13:30:22 -06:00
El Mau 6b0ca817a3 Agregar plantilla predeterminada para Carta Porte 2022-01-19 13:26:43 -06:00
El Mau 84fd8f57df Actualizar lista de cambios 2022-01-19 13:23:30 -06:00
El Mau f105f2a0fa Regimen fiscal desde emisor para CFDIs de pago 2022-01-19 13:10:30 -06:00
El Mau ea145c630b Primera versión completa de la representación impresa de la Carta Porte 2022-01-19 00:55:37 -06:00
El Mau 88bb6d9411 Agregar ubicaciones a representación impresa de Carta Porte 2022-01-19 00:17:29 -06:00
El Mau 0498217b65 Iniciar representación impresa de Carta Porte 2022-01-18 23:28:52 -06:00
El Mau 581e8bbdc5 Se agrega nodo para remolques 2022-01-17 21:49:35 -06:00
El Mau c79e8492d3 Copiar mercancias del CFDI 2022-01-16 22:20:45 -06:00
El Mau f9df0d34d0 Agregar eventos para agregar mercancias 2022-01-15 21:52:41 -06:00
El Mau f06022ac69 Agregar botón de comando para agregar mercacías en Carta Porte 2022-01-14 21:54:17 -06:00
El Mau 074cdc475f Agregar nodo para domicilio en Carta Porte 2022-01-12 21:42:02 -06:00
El Mau 0f041ed975 Agregar nodo para domicilio en Carta Porte 2022-01-11 21:49:28 -06:00
El Mau 6de30c3417 Actualizar lista de cambios 2022-01-10 14:28:16 -06:00
El Mau 8bbd2ba62b Actualizar lista de cambios 2022-01-10 14:10:17 -06:00
El Mau 00c58d9b41 Nuevo método para subir las plantillas 2022-01-10 13:39:35 -06:00
El Mau a3d291c893 Primera versión mínima de Carta Porte timbrada 2022-01-09 23:52:05 -06:00
El Mau 86bc032413 Nuevos controles para subir las plantillas 2022-01-08 21:24:19 -06:00
El Mau a91feb15d7 Agregar nuevos regimenes fiscales 2022-01-07 22:59:33 -06:00
El Mau 3ffc54f966 Modificar selector emergente para el calendario 2022-01-06 21:15:11 -06:00
El Mau b3663dfcc6 Modificar selector emergente para el calendario 2022-01-05 09:42:40 -06:00
El Mau 3cc11430d7 Cambio de formato del QR para plantilla json 2022-01-04 22:40:41 -06:00
El Mau 4ae5197ced Cambios en cancelación para Comercio 2022-01-04 14:29:00 -06:00
El Mau bf601dfcef Cambios en cancelación para Finkok 2022-01-03 23:06:55 -06:00
El Mau 481594839d Guardar datos para carta porte 2022-01-03 19:14:20 -06:00
El Mau 7b2467f99a Iniciar validaciones para carta porte 2022-01-01 21:48:02 -06:00
El Mau 2f22ad4cc8 Nuevos controles para carta porte terminada 2021-12-30 19:41:40 -06:00
El Mau 46d6329754 Resuelto: ticket #29 2021-12-30 11:56:22 -06:00
El Mau 2a2689a61b Sincronizar productos para carta porte 2021-12-29 22:02:17 -06:00
El Mau 76663fdd67 Agregar evento al cambiar de pestaña 2021-12-28 22:26:05 -06:00
El Mau 54ab86a089 Ocultar columna temporalmente 2021-12-27 21:33:34 -06:00
El Mau 7fe5251153 Agregar controles para Carta Porte 2021-12-25 23:00:23 -06:00
El Mau fe4829c6b8 Agregar controles para Carta Porte 2021-12-25 17:48:08 -06:00
El Mau bb90b3f6d7 Nuevos controles para carta porte 2021-12-21 23:01:44 -06:00
El Mau f4779dbb99 Validar que se seleccionen los dos archivos de los certificados 2021-12-17 21:14:23 -06:00
El Mau 47ec5ad360 Resolver conflictos 2021-12-12 22:30:15 -06:00
El Mau 80957afb84 Actualizar lista de cambios 2021-12-12 21:51:34 -06:00
El Mau b9af99aad6 Cancelar con certificados solo en modo depuración 2021-12-12 14:56:59 -06:00
El Mau 43e932e445 Consultar estatus del SAT en nómina 2021-12-12 14:46:53 -06:00
El Mau 8a151d42ad Corregir error al cancelar nomina 2021-12-12 13:51:51 -06:00
El Mau db77f6972d Corregir error al generar factura del día por ticket 2021-12-10 14:11:54 -06:00
El Mau ef2314880f Cambiar tipo de campo al campo Folio de la tabla Facturas 2021-12-09 22:56:29 -06:00
El Mau 1a1cb1173e Agregar XLST para Carta Porte 2021-12-04 22:05:20 -06:00
El Mau fe26744a8f Mover solo los tickets seleccionados al facturarlos 2021-12-04 21:56:00 -06:00
El Mau 69402e03bb Agregar archivos requeridos para CartaPorte 2.0 2021-12-03 21:41:37 -06:00
El Mau 4a84654117 Validar existencia por almacen 2021-11-29 21:57:32 -06:00
El Mau 81428c4f8e Cantidad a decimal al ajustar inventario 2021-11-28 15:29:57 -06:00
El Mau 28ef107949 Agregar cancelación con certificados con el pac CD 2021-11-26 13:14:43 -06:00
El Mau e372f064b4 Agregar declaración de encoding XML para cancelación 2021-11-25 16:41:30 -06:00
El Mau 46efb3693c Agregar declaración de encoding XML para cancelación 2021-11-25 14:40:37 -06:00
El Mau d2bacc3b6b Agregar declaración de encoding XML para cancelación 2021-11-25 12:51:49 -06:00
El Mau 264090f2a6 Remover nodo KeyValue en XML para cancelación 2021-11-25 10:22:23 -06:00
El Mau 425cd53df1 Remover nodo KeyValue en XML para cancelación 2021-11-24 16:02:48 -06:00
El Mau b7e8bb3cff Cambio en el formato del número de serie del certificado en el XML de cancelación 2021-11-24 15:53:40 -06:00
El Mau 1df477e0b1 Modificaciones menores a la plantilla XML para cancelación 2021-11-24 14:55:41 -06:00
El Mau 9b09fbbcd1 Resuelto - establecer 0 cuando descuento es None 2021-11-22 19:06:57 -06:00
El Mau f793cd2240 Resuelto - Obtener id de almacen origen 2021-11-22 09:16:28 -06:00
El Mau 54f2a574a3 Agregar método para importar existencias 2021-11-21 22:26:34 -06:00
El Mau 219f7c6712 Comentar lineas al firmar XML 2021-11-20 22:08:05 -06:00
El Mau 342f64a18b Resuelto - Quitar saltos de línea en XML firmado para cancelar 2021-11-18 21:45:54 -06:00
El Mau e880256cde Actualizar requerimientos 2021-11-18 21:35:09 -06:00
El Mau 3a1cb8d142 Actualizar vínculos en main 2021-11-17 19:38:09 -06:00
El Mau 42a840f85c Actualizar vínculos en main 2021-11-17 18:28:43 -06:00
Mauricio Baeza 2e5f4ac27e Get details from ticket 2021-10-30 15:08:32 -05:00
Mauricio Baeza 98f091a347 Start event for ticket from ticket 2021-10-29 22:06:53 -05:00
Mauricio Baeza 82ec2faac5 Fix link to blog 2021-10-28 21:23:06 -05:00
Mauricio Baeza 11c0cc4899 Add button for regenerate ticket 2021-10-28 21:20:58 -05:00
Mauricio Baeza 1c9c829296 Add link to repository 2021-10-26 23:17:01 -05:00
Mauricio Baeza 03f26b65e4 Add link to repository 2021-10-24 21:35:27 -05:00
Mauricio Baeza 00f2b2b126 Start new doc 2021-10-23 22:03:43 -05:00
Mauricio Baeza c77438216d Start new doc 2021-10-23 21:46:04 -05:00
Mauricio Baeza b02b2b0e63 Change label in certificates 2021-10-23 21:14:15 -05:00
Mauricio Baeza 75788474ba Add controller for warehouse movement 2021-10-14 19:20:23 -05:00
Mauricio Baeza 866e8c40b9 Add UI for warehouse movement 2021-10-13 21:13:08 -05:00
Mauricio Baeza c64de2bbb9 Update inventory in cancel tickets 2021-10-05 15:11:27 -05:00
Mauricio Baeza cd35884999 Add warehouse in detail tickets 2021-10-04 22:21:49 -05:00
Mauricio Baeza c4e29e47fa Update inventory in cancel invoice 2021-10-03 22:21:01 -05:00
Mauricio Baeza 00cbe4d557 Save warehouse in product details 2021-10-03 22:12:05 -05:00
Mauricio Baeza a95a6842f9 Save warehouse in product 2021-10-02 22:23:51 -05:00
Mauricio Baeza 30a4a276dd Update inventory in tickets 2021-09-26 21:20:08 -05:00
Mauricio Baeza 416bc65174 Can products without taxes 2021-09-14 21:53:00 -05:00
Mauricio Baeza 3dd88f14d6 Update version 2021-09-14 21:51:58 -05:00
Mauricio Baeza fb24346601 Can add or edit products without taxes 2021-09-14 21:49:21 -05:00
Mauricio Baeza f792b79655 Get exists by warehouse 2021-09-05 20:29:08 -05:00
Mauricio Baeza 2055a904f6 Add dialog for show exists in warehouse 2021-09-05 19:48:05 -05:00
Mauricio Baeza 7a1fd8fcaf Add command button for show details exists in warehouses 2021-09-04 21:59:24 -05:00
Mauricio Baeza cd22a5a24d Add new documentation 2021-09-03 22:13:25 -05:00
Mauricio Baeza 25e86cd28c Remove modifi precition in decimal context 2021-09-01 21:45:21 -05:00
Mauricio Baeza 4aad1a94eb Replace save for update in invoice 2021-08-31 21:47:05 -05:00
Mauricio Baeza 229be3b415 Fix pass id product in update inventory 2021-08-30 23:23:53 -05:00
Mauricio Baeza bc53171969 Update inventory 2021-08-30 22:57:31 -05:00
Mauricio Baeza 61a8937eb3 Add product by warehouse 2021-08-23 15:49:29 -05:00
Mauricio Baeza be75abeb54 Get wharehouse in products 2021-08-22 22:33:04 -05:00
Mauricio Baeza 2c314a3891 Add select warehouse in add product 2021-08-11 22:10:23 -05:00
Mauricio Baeza 7fc1d42381 Set branch by user 2021-08-08 21:51:12 -05:00
Mauricio Baeza 0957cf673f Add dialog for set branch user 2021-08-04 23:00:23 -05:00
Mauricio Baeza f99adaf109 Clean branch for users 2021-08-03 22:13:19 -05:00
Mauricio Baeza 91cfbb0508 Add column 'In branch' for users 2021-08-03 20:08:57 -05:00
Mauricio Baeza 4f18e1e01f Show or hide column for super user 2021-08-01 23:03:32 -05:00
Mauricio Baeza 7e7b08b1c0 Add products without taxes 2021-07-26 22:40:59 -05:00
Mauricio Baeza 4006322f30 Fix issue #12 2021-07-26 22:30:37 -05:00
Mauricio Baeza 935483f7ee Fix issue #11 2021-07-26 22:08:20 -05:00
Mauricio Baeza 80b766783d Save warehouse in sucursal 2021-07-25 21:58:23 -05:00
Mauricio Baeza 8822687308 Show warehouse in sucursal 2021-07-25 21:45:59 -05:00
Mauricio Baeza 0512e0f6dd Add and delete warehouse 2021-07-25 21:18:01 -05:00
Mauricio Baeza 93f0d09f4d Fix name error in warehouse 2021-07-24 22:41:06 -05:00
Mauricio Baeza 870d95cb5a Get wharehouse 2021-07-23 22:29:22 -05:00
Mauricio Baeza 9fa35d5592 Add wharehouse 2021-07-23 22:01:59 -05:00
Mauricio Baeza 2bfe86973a Add new table WhareHouseProduct 2021-07-23 18:43:26 -05:00
Mauricio Baeza 896be35e3b Hide or show controls for warehouses 2021-07-22 22:25:07 -05:00
Mauricio Baeza f2af10c2df Add UI grid for warehouse 2021-07-22 21:45:36 -05:00
Mauricio Baeza 8f7aaac563 Add options for multi stock UI 2021-07-22 21:24:04 -05:00
Mauricio Baeza 8a615144a6 Only admins can add inventory 2021-07-21 22:04:13 -05:00
Mauricio Baeza 227204abec Only admins can add inventory 2021-07-21 21:37:31 -05:00
Mauricio Baeza 637c69b9c7 Update changelog 2021-07-10 13:25:04 -05:00
Mauricio Baeza 4f3bdbda95 Add inventory entries manually 2021-07-08 12:56:40 -05:00
Mauricio Baeza 34c467d26e Add validation for new stock 2021-07-05 15:56:00 -05:00
Mauricio Baeza d61672dac4 Add UI windows for add inventory manually 2021-07-05 15:21:03 -05:00
Mauricio Baeza 1aa6540ac1 Change pyqrcode for segno 2021-06-29 19:07:55 -05:00
Mauricio Baeza 1b87591f62 Add inventory entries 2021-06-17 22:33:06 -05:00
Mauricio Baeza 986020a4a3 Add partner products 2021-06-17 21:45:40 -05:00
Mauricio Baeza 3514944f5d Add products in inventory entries 2021-06-16 23:36:25 -05:00
Mauricio Baeza 9be913c47d Add inventory entries post 2021-06-15 23:45:11 -05:00
Mauricio Baeza b9d87f0343 Validate add products 2021-06-14 23:41:39 -05:00
Mauricio Baeza 585632882d Validate add products 2021-06-13 23:55:32 -05:00
Mauricio Baeza 920ff62e98 Add new table PartnerProducts 2021-06-13 23:12:07 -05:00
Mauricio Baeza dc61d3b010 Parse details from CFDI 2021-06-10 21:39:15 -05:00
Mauricio Baeza 62a0a82699 Refactory class for read and write CFDIs 2021-06-08 22:54:59 -05:00
Mauricio Baeza 702ac88b38 Refactory method for import file 2021-06-07 22:45:00 -05:00
Mauricio Baeza 535cc9d527 Add button for import products from CFDI 2021-06-06 22:33:46 -05:00
Mauricio Baeza cf35cd08cd Validate stock in ticket 2021-06-01 19:45:42 -05:00
Mauricio Baeza e9eeab8c2c Validate stock when invoice 2021-06-01 19:34:34 -05:00
Mauricio Baeza a518278b55 Ticket #5 2021-05-31 13:01:03 -05:00
Mauricio Baeza 0442019ea5 Ticket #5 2021-05-31 12:59:45 -05:00
Mauricio Baeza fa94e36e7f Delete sucursales 2021-05-25 21:37:56 -05:00
Mauricio Baeza 348be949ac Add sucursales 2021-05-25 20:36:27 -05:00
Mauricio Baeza 565f36be3a Option cancel only by admins 2021-05-24 12:54:11 -05:00
Mauricio Baeza 7062151e79 Actualizar archvio de cambios 2021-05-24 12:33:56 -05:00
Mauricio Baeza 5f1edc76ca Add RFC test 2021-05-12 19:43:35 -05:00
Mauricio Baeza 5729a1f83e Cancel by admin 2021-04-20 21:41:08 -05:00
Mauricio Baeza 1a9087b13a Fix ticket #4 2021-04-13 22:29:29 -05:00
Mauricio Baeza a2670cb92c Fix ticket #4 2021-04-13 22:29:04 -05:00
Mauricio Baeza a8667a5d4f Fix in validate cert 2021-02-26 14:32:43 -06:00
Mauricio Baeza 952dcba7ae Fix issue #3 2021-02-12 19:37:11 -06:00
Mauricio Baeza 676ff29b57 Fix - issue #3 2021-02-12 18:19:40 -06:00
Mauricio Baeza 12a24c1117 Fix issue #2 2021-02-12 12:23:34 -06:00
Mauricio Baeza e59941db34 Update version and changelog 2021-02-12 12:23:20 -06:00
Mauricio Baeza 0e881eb17a Fix get version in PDF nomina 2021-02-12 12:04:31 -06:00
Mauricio Baeza fa01e6cf01 Fix issue #1 2021-02-11 18:10:37 -06:00
Mauricio Baeza 51e2464141 Update version and changelog 2021-02-11 18:09:58 -06:00
Mauricio Baeza 3e43cc3b46 Fix - issue #1 2021-02-11 18:07:51 -06:00
Mauricio Baeza b5bdcbbeb3 Import clients form CSV 2021-02-10 23:02:12 -06:00
Mauricio Baeza 4f71dd0de3 Clean review methods 2021-02-10 23:01:30 -06:00
Mauricio Baeza 720c9f0cb1 Update changelog and version 2021-02-10 22:54:16 -06:00
Mauricio Baeza 66d740e8d1 Report log 2021-02-10 22:50:05 -06:00
Mauricio Baeza 892906c46f Import clients 2021-02-10 22:47:00 -06:00
Mauricio Baeza aa68225571 Read csv to dict 2021-02-10 22:34:34 -06:00
Mauricio Baeza 4f40bc3bfa Add option for import clients 2021-02-10 22:01:39 -06:00
Mauricio Baeza b03fbebe09 Fix get status sat, issue #422 2021-02-09 23:04:49 -06:00
Mauricio Baeza 3cdbe14287 Update changelog 2021-02-09 23:04:31 -06:00
Mauricio Baeza 0bbf0b438f Add try except 2021-02-09 23:04:02 -06:00
Mauricio Baeza c98cdd7fff Fix get status sat, issue #422 2021-02-09 23:01:28 -06:00
Mauricio Baeza f0b5fa5ae9 Fix - issue #422 2021-02-09 23:01:08 -06:00
Mauricio Baeza 537cecbb0b Fix get status sat, issue #422 2021-02-09 22:49:57 -06:00
Mauricio Baeza 7f44f7f26d Update changelog 2021-02-09 22:46:36 -06:00
Mauricio Baeza c1e1c5ad7c Fix - issue #422 2021-02-09 22:44:26 -06:00
Mauricio Baeza 3c574425eb Move get status sat 2021-02-09 22:34:15 -06:00
Mauricio Baeza 5b8cd0ecb2 Fix show version of Empresa Libre 2021-02-02 22:10:13 -06:00
Mauricio Baeza 05a51741b6 Fix - show version of EmpresaLibre 2021-02-02 22:09:12 -06:00
Mauricio Baeza b3e2671359 Update template 2021-02-02 21:49:10 -06:00
Mauricio Baeza 5c047ee9d5 Update changelog 2021-01-19 12:16:41 -06:00
Mauricio Baeza b680d728a7 Update changelog 2021-01-18 19:28:15 -06:00
Mauricio Baeza 431422361b Fix passed argument to pac 2021-01-18 19:06:59 -06:00
Mauricio Baeza 85e58c2728 Fix passed argument to pac 2021-01-18 18:41:03 -06:00
Mauricio Baeza 739ac08205 Add migrate auth 2021-01-13 19:15:38 -06:00
Mauricio Baeza 64adba1588 Add migrate auth 2021-01-13 19:14:54 -06:00
Mauricio Baeza de06f706a6 Fix get version in proforma 2021-01-11 14:43:50 -06:00
Mauricio Baeza 6785dfebf7 Fix get version in proforma 2021-01-11 14:43:28 -06:00
Mauricio Baeza aaccece897 Fix get folios 2021-01-10 19:18:48 -06:00
Mauricio Baeza 94f5df0723 Fix get folios 2021-01-10 19:18:13 -06:00
Mauricio Baeza f813fb4dd8 Fix set error in pac Finkok 2021-01-10 15:24:35 -06:00
Mauricio Baeza 49d5838822 Fix set error in oac Finkok 2021-01-10 15:24:11 -06:00
Mauricio Baeza 51b4386b24 Fix add backend in cert 2021-01-10 14:23:58 -06:00
Mauricio Baeza 38f3c125cd Fix add backend in cert 2021-01-10 14:23:39 -06:00
Mauricio Baeza e678083eea Fix add backend in cert 2021-01-10 14:21:51 -06:00
Mauricio Baeza b612991476 Fix add backend in cert 2021-01-10 14:21:32 -06:00
Mauricio Baeza 32679fced3 Fix in function name export cert 2021-01-10 14:17:32 -06:00
Mauricio Baeza 8e4bf56846 Fix in function name export cert 2021-01-10 14:17:01 -06:00
Mauricio Baeza 0084197906 Fix in get year for preinvoices 2021-01-08 20:53:29 -06:00
Mauricio Baeza ace4ef21a7 Fix get years in preinvoices 2021-01-08 19:58:14 -06:00
Mauricio Baeza 17fa99e45b Add migrate certificates 2021-01-06 22:15:30 -06:00
Mauricio Baeza e547ce0113 Change name from migrate cert 2021-01-06 22:14:59 -06:00
Mauricio Baeza 858fd829aa Update changelog 2021-01-06 22:13:02 -06:00
Mauricio Baeza 788a1723b4 Remove class for import from FacturaLibre 2021-01-06 21:44:02 -06:00
Mauricio Baeza 4b9df44dac Add migrate certificates 2021-01-06 21:30:31 -06:00
Mauricio Baeza cca598310b Fix in import conf for pacs 2021-01-05 18:53:13 -06:00
Mauricio Baeza 39071be43b Fix import conf in pacs 2021-01-05 18:43:34 -06:00
Mauricio Baeza e633f8e2bf Fix import conf in pacs 2021-01-05 18:16:18 -06:00
Mauricio Baeza 21fd903943 Refactory stamp and cancel 2021-01-05 17:32:38 -06:00
Mauricio Baeza fec1c8523a Update changelog 2021-01-05 17:32:03 -06:00
Mauricio Baeza 0a04ec6c26 Cancel old with Finkok 2021-01-03 19:44:52 -06:00
Mauricio Baeza a7945dba58 Cancel by rfc of pac 2021-01-02 22:23:33 -06:00
Mauricio Baeza 348a7f6ecb Add pac Finkok refactory 2021-01-02 18:16:15 -06:00
Mauricio Baeza cf189b08fa Add try in cert 2020-12-31 21:55:01 -06:00
Mauricio Baeza 9423aeef6c Cancel cfdi of nomina with xml signed 2020-12-31 21:11:22 -06:00
Mauricio Baeza 90eec635ce Cancel cfdi of pay with xml signed 2020-12-31 21:00:00 -06:00
Mauricio Baeza 8f15961d20 Fix - replace getchildren form list 2020-12-31 20:17:00 -06:00
Mauricio Baeza 8a8f05384b Stamp nomina 2020-12-31 20:10:32 -06:00
Mauricio Baeza a54ad8d760 Cancel sign xml 2020-12-31 15:08:49 -06:00
Mauricio Baeza 0389c0734f Clean methods to import from old FacturaLibre 2020-12-31 12:17:26 -06:00
Mauricio Baeza cb04910a84 Sign in memory 2020-12-31 12:01:41 -06:00
Mauricio Baeza 38c9c676af Remove old class cert 2020-12-31 00:06:50 -06:00
Mauricio Baeza e0d1f40a11 Remove old validate cert 2020-12-31 00:04:19 -06:00
Mauricio Baeza d8ecae2c8f Refactory upload certificates 2020-12-31 00:01:41 -06:00
Mauricio Baeza 85c5a37798 Refactory class certificates 2020-12-30 22:45:57 -06:00
Mauricio Baeza 56e52782f4 Refactory generate files 2020-12-29 22:05:02 -06:00
Mauricio Baeza aae856bb74 New class for Pac Comercio Digital 2020-12-29 21:53:51 -06:00
Mauricio Baeza 95399798f8 Stamp by Pac 2020-12-28 22:28:52 -06:00
Mauricio Baeza 75e4f2e1c0 Fix version in XSLT 2020-12-28 22:22:36 -06:00
Mauricio Baeza f02b6782be Move configuration for PACs 2020-12-28 22:14:30 -06:00
Mauricio Baeza bd7508fe6b Issue #415 2020-12-16 16:04:32 -06:00
Mauricio Baeza 1f3e51861d Update version 2020-09-17 13:53:01 -05:00
Mauricio Baeza 3a51a50c8e Update version 2020-09-17 13:52:31 -05:00
Mauricio Baeza 1c6f9cd5f7 Fix - Namespaces for IEDU 2020-09-17 13:46:41 -05:00
Mauricio Baeza b6c5c72df8 Fix - Namespaces for IEDU 2020-09-17 13:46:23 -05:00
Mauricio Baeza 8d815bf570 Update date in change log 2020-08-25 23:03:12 -05:00
Mauricio Baeza afced92aff Update date in change log 2020-08-25 23:02:54 -05:00
Mauricio Baeza 443b916056 Fix - Validate namespace 2020-08-25 15:17:59 -05:00
Mauricio Baeza 1d18d97b90 Update change log 2020-08-25 15:17:10 -05:00
Mauricio Baeza 43808efc91 Fix namespace in nomina 2020-08-25 14:15:07 -05:00
Mauricio Baeza 3ef195ed48 Mostrar totales 2020-04-16 22:35:41 -05:00
Mauricio Baeza 980a7aafdb Fix: nomina stamp asimilados 2020-03-30 19:04:50 -06:00
Mauricio Baeza 1e95b180e8 Fix: nomina stamp asimilados 2020-03-30 19:04:33 -06:00
Mauricio Baeza 267a96ebbc Global invoice by ticket 2020-03-08 15:27:53 -06:00
Mauricio Baeza 5e8d4d5fd9 Fix make PDF 2020-03-08 15:26:53 -06:00
Mauricio Baeza 8a293110ab Fix make PDF 2020-03-08 15:26:44 -06:00
Mauricio Baeza 4fcd7e18c0 Global invoice by ticket 2020-03-04 22:36:20 -06:00
Mauricio Baeza ed23a3531c Uncancel ticket when cancel invoice 2020-03-03 22:47:40 -06:00
Mauricio Baeza 82af00fedf Global invoice, get max payment type 2020-03-03 22:24:00 -06:00
Mauricio Baeza 763f3c21fb Support for tax legends 2020-03-02 17:27:28 -06:00
Mauricio Baeza 6b4eb36795 Tax legends in PDF 2020-03-02 17:27:02 -06:00
Mauricio Baeza 2877a68b92 Stamp fiscal leyends 2020-03-01 23:18:26 -06:00
Mauricio Baeza cca836bb6a Add UI for fiscal leyends 2020-02-29 23:51:14 -06:00
Mauricio Baeza 26e4ce3d13 Options for show documents 2020-02-25 21:52:17 -06:00
Mauricio Baeza 3166723cdc Options for show documents 2020-02-25 21:51:48 -06:00
Mauricio Baeza a202f12a17 Add filter by sucursal 2020-02-24 22:35:24 -06:00
Mauricio Baeza bf3ed47fac Update changelog 2020-02-24 22:34:54 -06:00
Mauricio Baeza b2f3cdd66e Filter by sucursal 2020-02-23 22:02:46 -06:00
Mauricio Baeza a17345c0fd Fix stamp nomina 2020-02-09 22:34:58 -06:00
Mauricio Baeza 7d7ca4eeba Fix stamp nomina 2020-02-09 22:34:36 -06:00
Mauricio Baeza d509bfb8be Fix new validate nomina SAT 2020-01-29 16:03:20 -06:00
Mauricio Baeza 0a006f4508 Fix new validate nomina SAT 2020-01-29 16:03:02 -06:00
Mauricio Baeza 71bd536cb1 Update catalog SAT 2020-01-27 22:40:57 -06:00
Mauricio Baeza 068c57c018 Add update SAT 2020-01-27 22:39:39 -06:00
Mauricio Baeza e94cda3577 Fix import conf.py 2020-01-24 10:02:24 -06:00
Mauricio Baeza f102cb7c01 Fix import conf.py 2020-01-24 10:01:55 -06:00
Mauricio Baeza 92e6a317ad Get folios by Pac 2020-01-22 22:30:11 -06:00
Mauricio Baeza 77b35f8322 Add new PAC 2020-01-22 15:25:40 -06:00
Mauricio Baeza 3f0daec6ad Change argument user for stamp 2020-01-22 13:21:53 -06:00
Mauricio Baeza a641024a86 Select Pac in admin 2020-01-22 12:48:15 -06:00
Mauricio Baeza f9a73dfd66 Add new PAC 2020-01-21 23:25:25 -06:00
Mauricio Baeza 2cc6e7046a Add new PAC 2020-01-21 23:25:13 -06:00
Mauricio Baeza fe09860922 Update catalogos SAT 2020-01-12 22:40:45 -06:00
Mauricio Baeza aeb045e76a Check unique clients 2020-01-11 22:14:38 -06:00
Mauricio Baeza 1c55fbd7c7 Fix #355 2020-01-07 21:48:57 -06:00
Mauricio Baeza e1b7e525df Fix #355 2020-01-07 00:34:11 -06:00
Mauricio Baeza 6f3d9cc3fa Fix - al crear PDF 2019-10-28 20:04:28 -06:00
Mauricio Baeza cff2294601 Fix - al generar PDF 2019-10-28 19:35:03 -06:00
Mauricio Baeza ce0e960534 Merge branch 'develop'
Fix #369
2019-08-29 09:13:43 -05:00
Mauricio Baeza af6b13fa67 Update version 2019-08-28 14:30:57 -05:00
Mauricio Baeza df901090a2 Fix #369 2019-08-28 14:28:48 -05:00
Mauricio Baeza a87c78fc6b Start changes for uncancel 2019-07-11 13:34:59 -05:00
Mauricio Baeza abbb4cc608 Merge branch 'develop'
Envío de nómina por correo
Actualización de catalogos del SAT
2019-04-23 23:12:25 -05:00
Mauricio Baeza a67d174782 Update Changelog 2019-04-23 23:11:49 -05:00
Mauricio Baeza 63ae4a5127 Modify Middleware 2019-04-17 22:38:51 -05:00
Mauricio Baeza 4574ff744f Envío de nómina por correo 2019-04-02 09:25:40 -06:00
Mauricio Baeza 163e0f50c9 Envío de nómina por correo 2019-04-01 23:09:32 -06:00
Mauricio Baeza 692477f41a Merge branch 'develop'
Actualización de catálogos del SAT en Nómina
2019-03-24 21:49:26 -06:00
Mauricio Baeza ae351b6148 Actualización de catálogos del SAT de Nómina 2019-03-24 20:40:47 -06:00
Mauricio Baeza 519a61e132 Merge branch 'develop'
Permitir editar la descripción en un movimiento bancario
Fix #343
2019-03-08 22:10:19 -06:00
Mauricio Baeza d7260a8a95 Permitir editar la descripción de un movimiento bancario 2019-03-08 22:06:16 -06:00
Mauricio Baeza 485dc4154e Fix #343 2019-03-08 19:40:53 -06:00
Mauricio Baeza 4bff2703a4 Merge branch 'develop'
Fix - Send pay invoice
2019-03-04 19:44:59 -06:00
Mauricio Baeza 2ee2b8be43 Fix - Send pay invoice 2019-03-04 19:44:42 -06:00
Mauricio Baeza 6cef8fe669 Merge branch 'develop'
Fix - In filter invoice by dates
2019-03-04 19:22:56 -06:00
Mauricio Baeza aad48639ee Fix - In filter invoice by dates 2019-03-04 19:22:28 -06:00
Mauricio Baeza 27a29d331c Delete files in render PDF 2019-03-03 23:01:31 -06:00
Mauricio Baeza 7a709f056c Merge branch 'categulario/empresa-libre-feature/virtualenv' into develop
Agregar env a gitignore
2019-02-25 13:18:40 -06:00
Mauricio Baeza 49f1c24df5 Mezclar gitignore 2019-02-25 13:14:52 -06:00
Mauricio Baeza 4a38410e8f Validación para que ISR sea de solo retención 2019-02-22 12:24:52 -06:00
Mauricio Baeza 2cacb786b6 Merge branch 'develop'
Error al enviar prefactura por correo
2019-02-20 23:40:07 -06:00
Mauricio Baeza 7f33252fa1 Error al enviar prefactura por correo 2019-02-20 23:39:47 -06:00
Mauricio Baeza 79cea5b092 Merge branch 'develop'
Soporte para complemento de divisas
2019-02-17 22:12:39 -06:00
Mauricio Baeza 9ea396b133 Cambio al generar un zip 2019-02-17 22:12:01 -06:00
Mauricio Baeza 4c2ea774b4 Descargar nómina en lote 2019-02-17 13:29:10 -06:00
Mauricio Baeza da41bb272e Filtro de facturas incluye cliente 2019-02-16 22:38:58 -06:00
Mauricio Baeza 8d7b42e368 Actulizar lista de cambios 2019-02-16 22:11:20 -06:00
Mauricio Baeza e292ba88b8 Mejora #329 2019-02-16 22:07:19 -06:00
Mauricio Baeza 7e73541c96 Buscar facturas por notas 2019-02-15 17:51:13 -06:00
Mauricio Baeza a0e1f83c25 Complemento divisas en PDF 2019-02-15 14:38:41 -06:00
Mauricio Baeza 8a4021ce49 Complemento divisas 2019-02-14 22:50:17 -06:00
Mauricio Baeza a4997be78d Actualizar vinculos 2019-02-14 20:17:03 -06:00
Mauricio Baeza 95d3345418 Notificación de acceso por correo 2019-02-05 22:12:19 -06:00
Mauricio Baeza f761ab7a5a Mostrar cantidad de empaques en PDF 2019-02-04 22:13:11 -06:00
Mauricio Baeza 5cdbb0aaa7 Agregar funciones para encriptar 2019-02-03 23:32:48 -06:00
Mauricio Baeza c9f9ea526f Cantidad de empaques al facturar 2019-02-03 22:43:27 -06:00
Mauricio Baeza 06170cb054 Cantidad por empaque en productos 2019-02-03 22:20:54 -06:00
Mauricio Baeza 43684b7fe9 Merge branch 'develop'
Fix - Al cancelar nómina
2019-01-23 13:33:32 -06:00
Mauricio Baeza d48122608b Fix - Al cancelar nómina 2019-01-23 13:33:11 -06:00
Mauricio Baeza 3d300146f1 Merge branch 'develop'
Permitir capturar folio manualmente
2019-01-17 22:10:17 -06:00
Mauricio Baeza 5ed281fd9d Permitir capturar folio manualmente 2019-01-17 22:00:07 -06:00
Mauricio Baeza dbf3a342bf Merge branch 'develop'
Guardar logos de emisor en carpeta correcta
2019-01-16 23:38:05 -06:00
Mauricio Baeza 43e643cccd Guardar logos del emisor en carpeta correcta 2019-01-16 23:37:51 -06:00
Mauricio Baeza 454de7bf1e Guardar logos del emisor en carpeta correcta 2019-01-15 23:16:34 -06:00
Mauricio Baeza 997dfe90ba Merge branch 'develop'
Generar PDF desde plantilla JSON
2019-01-15 00:51:05 -06:00
Mauricio Baeza 812e510660 Actualizar CHANGELOG.md 2019-01-15 00:47:15 -06:00
Mauricio Baeza 538a9f39ce Leyenda cancelada en PDF a partir de JSON 2019-01-15 00:45:08 -06:00
Mauricio Baeza cf0c4b8622 PDF a partir de JSON 2019-01-14 02:07:42 -06:00
Mauricio Baeza 7864e236db PDF a partir de JSON 2019-01-10 23:37:37 -06:00
Mauricio Baeza 00bdd2b1a2 Subir plantillas HTML y CSS 2018-11-20 23:47:53 -06:00
Mauricio Baeza 0d52e9b570 Factura HTML v1 2018-11-20 00:03:07 -06:00
Mauricio Baeza 7dc65a2a2b Soporte para factura en HTML 2018-11-19 01:03:48 -06:00
Mauricio Baeza ff0ee73a73 Merge branch 'develop'
Error al importar nómina y factura en lote
2018-11-07 19:15:54 -06:00
Mauricio Baeza 2d045af037 Error al importar nómina y factura en lote 2018-11-07 19:14:54 -06:00
Mauricio Baeza 4660e1d4e0 Merge branch 'develop'
Error: Consulta de  estatus en el SAT
2018-11-06 22:57:06 -06:00
Mauricio Baeza 68ed8ee0db Error: Consulta de estatus en el SAT 2018-11-06 22:56:43 -06:00
Mauricio Baeza 47ed1cb72e Separar documentación 2018-10-31 17:15:53 -06:00
Mauricio Baeza dc8aeaebba Merge branch 'develop'
Mejora: Permitir importar CFDI 3.2
2018-10-30 16:37:15 -06:00
Mauricio Baeza d2dc80a0c9 Actualizar version 1.23.0 2018-10-30 16:25:17 -06:00
Mauricio Baeza e83281bdee Mejora: Se importan CFDI 3.2 2018-10-30 16:21:00 -06:00
Mauricio Baeza 0bddc189b4 Merge branch 'develop'
Mejora al generar factura en lote
2018-10-27 01:15:42 -05:00
Mauricio Baeza c2c8a10dec Mejora al generar factura en lote 2018-10-27 01:15:20 -05:00
Mauricio Baeza 1dddbac0f2 Permitir cambiar descrición al facturar en lote 2018-10-25 14:59:22 -05:00
Mauricio Baeza bbaf709e0e Merge branch 'develop'
Error unicode
2018-10-24 00:02:08 -05:00
Mauricio Baeza c42bc817b7 Actualizar version 2018-10-23 23:57:36 -05:00
Mauricio Baeza ba91d28fbd Error unicode 2018-10-23 23:43:23 -05:00
Mauricio Baeza 2765958a23 Merge branch 'develop'
Error #307
2018-10-14 22:24:34 -05:00
Mauricio Baeza c319ee454e Actualizar version 2018-10-14 22:24:07 -05:00
Mauricio Baeza 9f919d77bc Error #307 2018-10-14 14:37:12 -05:00
Mauricio Baeza 2090aa743d Merge branch 'develop'
Error #287
Mejora - Facturas de pago con datos bancarios
2018-10-12 01:23:17 -05:00
Mauricio Baeza af5d526bad Error #287 2018-10-12 01:14:01 -05:00
Mauricio Baeza 4eb4cc8fa5 Error #287 2018-10-10 22:25:10 -05:00
Mauricio Baeza 4007923f84 Actualizar dpcumentación 2018-10-09 00:03:20 -05:00
Mauricio Baeza 4a5bf615c4 Cuentas de banco para clientes nuevos 2018-10-08 14:13:16 -05:00
Mauricio Baeza 63fc8e9a78 Merge branch 'develop'
Error #295
Cuentas de banco para clientes
2018-10-08 01:14:24 -05:00
Mauricio Baeza 4f34599a6f Cuentas de banco para clientes 2018-10-08 01:13:46 -05:00
Mauricio Baeza 5ac614e079 Error #295 2018-10-07 20:29:14 -05:00
Mauricio Baeza 0465b684f0 Mejora #292 2018-10-05 23:13:03 -05:00
Mauricio Baeza d70c35b4de Error #295 2018-10-04 23:25:52 -05:00
Mauricio Baeza 3cc85e2644 Error #295 2018-10-04 17:52:50 -05:00
Mauricio Baeza ae4c4b9355 Actualizar versiona a 1.20.0Error #295 2018-10-04 15:58:40 -05:00
Mauricio Baeza e7666432cb Error #295 2018-10-04 15:56:41 -05:00
Mauricio Baeza 425f2ee079 Merge branch 'develop'
Relacionar facturas v3.2
2018-10-03 23:40:52 -05:00
Mauricio Baeza a6edbaa56f Relacionar facturas v3.2 2018-10-03 23:40:18 -05:00
Mauricio Baeza db435a9b3d Filtrar por pagar facturas v3.2 2018-10-03 23:15:32 -05:00
Mauricio Baeza aee9a83dd2 Importar CFDI sin marcar como pagada 2018-10-03 20:51:26 -05:00
Mauricio Baeza acd8604e92 Merge branch 'develop'
Error #291
2018-10-03 15:23:17 -05:00
Mauricio Baeza eab8c3bb94 Error #291 2018-10-03 15:22:47 -05:00
Mauricio Baeza a15ddd3544 Error #291 2018-09-28 23:04:02 -05:00
Mauricio Baeza 23055b9af5 Error #291 2018-09-28 23:01:04 -05:00
Mauricio Baeza 71d773f999 Merge branch 'develop'
Mejora #280
Mejora #288
Error #290
2018-09-28 02:24:12 -05:00
Mauricio Baeza 65587eb207 Actualizar version y notas de lanzamiento 2018-09-28 02:23:01 -05:00
Mauricio Baeza d795069b38 Mejoras #280 #288 2018-09-28 02:17:17 -05:00
Mauricio Baeza 17a9b00cb5 Error #290 2018-09-27 19:12:33 -05:00
Mauricio Baeza 2e98d69685 Merge branch 'develop'
Error #282
2018-09-27 00:37:29 -05:00
Mauricio Baeza 0b73efaf9b Doc + Facturas en otras monedas 2018-09-27 00:31:36 -05:00
Mauricio Baeza a7d0339660 Error #282 2018-09-26 23:22:29 -05:00
Mauricio Baeza 135d469f3e Merge branch 'develop'
Soporte para facturas de pagos con documentos en otras monedas
2018-09-26 01:30:34 -05:00
Mauricio Baeza 43a50b833f Doc + Bancos y complemento de pagos 2018-09-26 01:26:23 -05:00
Mauricio Baeza 44cf4089f7 Mostrar moneda al timbrar factura nueva 2018-09-25 22:03:16 -05:00
Mauricio Baeza accd49d101 Error #278 2018-09-25 15:38:13 -05:00
Mauricio Baeza 65d68db2d7 Actualizar version a 1.17.0 2018-09-25 01:45:40 -05:00
Mauricio Baeza 73fa31abc8 Mejora #278 2018-09-25 01:36:24 -05:00
Mauricio Baeza 278bd06b72 Agregar tipo de cambio en facturas relacionadas otras monedas 2018-09-25 01:24:37 -05:00
Mauricio Baeza 12d26e01f9 Eliminar factura de pago cuando no esta timbrada 2018-09-23 22:36:16 -05:00
Mauricio Baeza 6d3625caf7 Doc Actualizar 2018-09-22 00:32:38 -05:00
Mauricio Baeza 760d33a389 Iniciar documentación 2018-09-20 20:50:13 -05:00
Mauricio Baeza 18b1880772 Merge branch 'develop'
Aactualizar versión 1.16.1
2018-09-18 10:58:04 -05:00
Mauricio Baeza 933068069c Actualizar versión 1.16.1 2018-09-18 10:57:50 -05:00
Mauricio Baeza 66111d9080 Merge branch 'develop'
Error #268
2018-09-18 10:52:34 -05:00
Mauricio Baeza 525394dbed Error #268 2018-09-18 10:35:07 -05:00
Mauricio Baeza 127ceceada Merge branch 'develop'
Se puede modidicar el saldo del cliente
Se muestra la cantidad de facturas de pago en los movimientos
2018-09-16 22:10:15 -05:00
Mauricio Baeza a05b370c33 Actualizar versión 2018-09-16 22:06:06 -05:00
Mauricio Baeza 8efe19fd24 Mejora - #262 2018-09-16 20:41:47 -05:00
Mauricio Baeza c8371a272f Mejora - #262 2018-09-16 01:17:18 -05:00
Mauricio Baeza 25cf581fcb Mejora - #248 2018-09-16 00:34:35 -05:00
Mauricio Baeza b6b5aa3447 Merge branch 'develop'
Fix # 257
2018-09-12 23:23:58 -05:00
Mauricio Baeza 0379fd6a6a Fix - #259 2018-09-12 22:19:51 -05:00
Mauricio Baeza 30fab23c0d Fix - #257 2018-09-12 17:59:17 -05:00
Mauricio Baeza b8f5dbd421 Fix - #260 2018-09-12 17:42:52 -05:00
Mauricio Baeza 63b9d6729b Fix - #259 2018-09-12 15:41:34 -05:00
Mauricio Baeza 5e6d86cb6b Fix - #257 2018-09-12 13:35:03 -05:00
Mauricio Baeza b0de7655cc Merge branch 'develop'
Personalizar plantilla para factura de pago
2018-09-10 23:08:38 -05:00
Mauricio Baeza 12297b9479 Personalizar plantilla para factura de pago 2018-09-10 23:07:52 -05:00
Mauricio Baeza dea511d161 Merge branch 'develop'
Fix - Al agregar cuenta de banco
2018-09-10 17:35:28 -05:00
Mauricio Baeza faa07f937f Fix - Al agregar cuenta de banco 2018-09-10 17:34:54 -05:00
Mauricio Baeza 9081d54865 Merge branch 'develop'
Cancelar factura de pago
2018-09-10 00:18:39 -05:00
Mauricio Baeza ca4810bd3d Pasar RFC al migrar tablas 2018-09-10 00:17:17 -05:00
Mauricio Baeza aece36905d Actualizar saldo de cuenta al cancelar movimiento 2018-09-10 00:05:24 -05:00
Mauricio Baeza 1ee3f81dcc Evitar cancelar movimiento con factura de pago 2018-09-10 00:00:52 -05:00
Mauricio Baeza 0ec7989c75 Cancelar CFDI de pago 2018-09-07 00:08:16 -05:00
Mauricio Baeza e5a389a9dd Fix #256 2018-09-06 18:15:54 -05:00
Mauricio Baeza dd0585853a Fix #251 2018-09-05 23:47:55 -05:00
Mauricio Baeza 3dd73c1467 Fix #249 2018-09-05 22:49:53 -05:00
Mauricio Baeza f0ab9246e0 Merge branch 'develop'
Fix - al migrar tablas
2018-09-03 02:18:29 -05:00
Mauricio Baeza 80b87348f0 Fix - al migrar tablas 2018-09-03 02:18:07 -05:00
Mauricio Baeza 8de2b20dcf Merge branch 'develop'
Soporte para Factura de pagos
2018-09-03 00:55:28 -05:00
Mauricio Baeza 6c0fc15053 Factura de pago, establecer serie y folio inicial 2018-09-02 22:33:53 -05:00
Mauricio Baeza e8f27904d9 Factura de pago, envío por correo 2018-09-02 20:55:47 -05:00
Mauricio Baeza d1b94cab57 Fix - Al cambiar cuenta de banco 2018-09-01 10:47:03 -05:00
Mauricio Baeza 31c6261183 Actualizar readme 2018-08-30 23:37:10 -05:00
Mauricio Baeza 118fa41d9b Configurar valores predeterminados para CFDI de pagos 2018-08-30 20:49:45 -05:00
Mauricio Baeza b202827ee0 Merge branch 'pagos' into develop
Soporte para complemento de pagos
2018-08-30 19:54:46 -05:00
Mauricio Baeza fd75da65fc Generate PDF from cfdi pay 2018-08-30 19:24:33 -05:00
Mauricio Baeza 9dc2c72de7 Add template cfdi pay 2018-08-30 11:25:40 -05:00
Mauricio Baeza fdbfbb2bc0 Stamp XML cfdi pay 2018-08-30 00:13:12 -05:00
Mauricio Baeza 6a724ac845 Generate XML cfdi pay 2018-08-28 01:02:35 -05:00
Mauricio Baeza 282f31497f Save cfdi pay 2018-08-27 00:53:52 -05:00
Mauricio Baeza 54c01de410 Add new fields to cfdipagos 2018-08-24 23:04:10 -05:00
Mauricio Baeza 6339e11c37 Get related invoice 2018-08-24 00:52:59 -05:00
Mauricio Baeza 7896d66448 UI para complemento de pago 2018-08-23 00:55:45 -05:00
Mauricio Baeza 0f02411de6 Merge branch 'update' into develop
Actualizar webix y lokyjs
2018-08-22 22:12:47 -05:00
Mauricio Baeza c438e7dc1d Actualizar webix y lokyjs 2018-08-22 00:01:07 -05:00
Mauricio Baeza 4608246b7d Merge branch 'develop'
Quitar columnas en tabla facturaspagos
Actualizar version y lista de cambios
2018-08-21 18:52:55 -05:00
Mauricio Baeza ea18996111 Actualizar versión y lista de cambios 2018-08-21 18:52:36 -05:00
Mauricio Baeza 7c00839b1f Fix - Quitar columna 'cancelado' en tabla 'facturaspagos' 2018-08-21 18:49:53 -05:00
Mauricio Baeza 94240f2356 Merge branch 'develop'
Error al cerrar plantilla
2018-08-09 15:51:34 -05:00
Mauricio Baeza 4ab3b22f29 Error al cerrar plantilla 2018-08-09 15:51:11 -05:00
Mauricio Baeza 88085bfa06 Fix - redondeo en retenciones 2018-08-03 23:01:21 -05:00
Mauricio Baeza 3163fef0d5 Merge branch 'develop'
Cambio en consulta de timbres
2018-07-25 16:28:22 -05:00
Mauricio Baeza cb6e4e21cf Cambio en consulta de timbres 2018-07-25 16:27:53 -05:00
Mauricio Baeza 32ec443a19 Merge branch 'develop'
Precios con cuatro decimales. Ticket #229
2018-07-10 23:37:35 -05:00
Mauricio Baeza c85af83275 Precios con cuatro decimales 2018-07-10 23:34:32 -05:00
Mauricio Baeza 64caf4f415 Merge branch 'develop'
Actualizar versión
2018-07-08 23:46:21 -05:00
Mauricio Baeza 064a65c0ae Actualizar version 2018-07-08 23:45:39 -05:00
Mauricio Baeza 13836add29 Merge branch 'develop'
Fix - Ticket #231
2018-07-08 23:43:19 -05:00
Mauricio Baeza 0bf0769ad7 Fix - Ticket #231 2018-07-08 23:41:13 -05:00
Mauricio Baeza f3309429e6 Merge branch 'develop'
Actualizar versión
2018-07-05 23:56:08 -05:00
Mauricio Baeza 3ce31258e2 Actualizar versión 2018-07-05 23:55:53 -05:00
Mauricio Baeza f9e4bced15 Merge branch 'develop'
Fix - Ticket #230
2018-07-05 23:53:02 -05:00
Mauricio Baeza a04f92fab8 Fix - Ticket #230 2018-07-05 23:52:09 -05:00
Mauricio Baeza c3d92ff45e Merge branch 'develop'
Fix - Al mostrar el título
2018-06-25 22:58:03 -05:00
Mauricio Baeza a8405a98fb Fix - Al mostrar el título 2018-06-25 22:56:02 -05:00
Mauricio Baeza 3424f7f78a Merge branch 'develop'
Add - Ticket #223
Fix - Ticket #224
2018-06-18 14:11:26 -05:00
Mauricio Baeza dc1f48cc0b Ticket #223, Fix Ticket #224 2018-06-18 14:10:52 -05:00
Mauricio Baeza 8c874dd8dd Fix - Ticket #223 2018-06-15 00:05:55 -05:00
Mauricio Baeza de40cb0453 Merge branch 'develop'
Fix - Ticket #222
2018-06-14 22:22:30 -05:00
Mauricio Baeza 0caea7b0ce Fix - Ticket #222 2018-06-14 22:20:55 -05:00
Mauricio Baeza da84fbafb6 Merge branch 'develop'
- Se permiten 4 decimales en Tipo de cambio
- Se agrega el campo {total_cantidades} al generar el PDF
- Se agrega opción para generar respaldos de la BD en MV
- Fix: Al generar con complemento EDU
2018-06-03 22:45:23 -05:00
Mauricio Baeza 43d4032b98 Ajuste en pie de lista de facturas 2018-06-03 22:39:37 -05:00
Mauricio Baeza cf5517d1ab Fix - Al generar complemento EDU 2018-06-03 00:00:04 -05:00
Mauricio Baeza 32926740be Merge branch 'develop'
Actualizar lista de cambios
2018-05-23 23:47:38 -05:00
Mauricio Baeza 7348b1bbba Actualizar lista de cambios 2018-05-23 23:47:18 -05:00
Mauricio Baeza 57dce9670b Merge branch 'develop'
Soporte para truncar impuestos
2018-05-23 23:44:38 -05:00
Mauricio Baeza 31627cca95 Soporte para truncar impuestos 2018-05-23 23:42:57 -05:00
Mauricio Baeza 03ac074c66 Merge branch 'develop'
Fix - Formato de celdas en conceptos
2018-05-08 13:22:43 -05:00
Mauricio Baeza a83d628a75 Fix - Formato de celdas en conceptos 2018-05-08 13:20:18 -05:00
Mauricio Baeza 1f043746dd Merge branch 'develop'
Fix - Nómina separación
2018-04-09 21:43:09 -05:00
Mauricio Baeza 95fa7aff14 Actualizar lista de cambios 2018-04-09 21:42:49 -05:00
Mauricio Baeza 66b5ceab1f Fix - Nómina separación 2018-04-09 00:47:14 -05:00
Mauricio Baeza 5b15166c7a Merge branch 'develop'
Fix - Issue #211
2018-03-29 16:20:56 -06:00
Mauricio Baeza 790e8ed7d6 Fix - Issue #211 2018-03-26 22:38:19 -06:00
Mauricio Baeza 0a48580f0c Merge branch 'develop'
Fix - Issue #209
2018-03-20 22:51:47 -06:00
Mauricio Baeza 3869d4b48b Fix - Issue #209 2018-03-20 22:51:08 -06:00
Mauricio Baeza 2f797af8b2 Merge branch 'develop'
Ejecutar LibreOffice como otra instancia
2018-03-09 00:23:46 -06:00
Mauricio Baeza 001673cdb4 Ejecutar LibreOffice como otra instancia 2018-03-08 14:29:24 -06:00
Mauricio Baeza e2da894d8b Descancelar ticket al eliminar factura 2018-03-07 23:52:03 -06:00
Mauricio Baeza 3e3c4be720 Fix - Issue #157 2018-03-07 23:24:35 -06:00
Mauricio Baeza aac5c04a3f Merge branch 'develop'
Fix - CFDI sin folio
2018-03-06 21:33:05 -06:00
Mauricio Baeza facba9b629 Fix - cfdi sin folio 2018-03-06 21:25:32 -06:00
Mauricio Baeza 42d6e74dcd Tickets facturados 2018-03-06 18:18:56 -06:00
Mauricio Baeza 348f2d4443 Merge branch 'develop'
Quitar registro patronal en contrato 09, 10 y 99
2018-03-06 10:42:01 -06:00
Mauricio Baeza 27e131c057 Quitar registro patronal en contrato 09, 10 y 99 2018-03-06 10:41:38 -06:00
Mauricio Baeza 313af2cce1 Merge branch 'develop'
Fix - Issue #199
2018-03-01 22:29:34 -06:00
Mauricio Baeza afb3621a29 Fix - Issue #199 2018-03-01 22:29:16 -06:00
Mauricio Baeza 7308ec86ae Merge branch 'develop'
Traslados
2018-03-01 22:07:37 -06:00
Mauricio Baeza 175269cd3a Fix - Prefacturas con descuentos 2018-03-01 13:50:27 -06:00
Mauricio Baeza 3ef8807ee9 Validar usuario 2018-03-01 13:32:47 -06:00
Mauricio Baeza ef076a9085 Descuento cero en traslados 2018-03-01 00:03:53 -06:00
Mauricio Baeza 2d45a8258f Mostrar unidad predeterminada en productos 2018-02-24 20:04:44 -06:00
Mauricio Baeza 900176a13b Editar código de barras en productos 2018-02-24 19:33:33 -06:00
Mauricio Baeza 853a29de84 Refacturar facturas grandes 2018-02-24 17:34:55 -06:00
Mauricio Baeza 7bc4f4631d Refacturar 2018-02-23 23:58:13 -06:00
Mauricio Baeza 8acc4fe6bc Fix - Issue #194 2018-02-23 10:41:37 -06:00
Mauricio Baeza cc56139665 Refacturar 2018-02-23 01:46:42 -06:00
Mauricio Baeza 99abfd5859 CFDI de traslado en ceros 2018-02-22 18:27:05 -06:00
Mauricio Baeza dc68b54f08 Merge branch 'develop'
Timbrar XML grandes con Ecodex
2018-02-22 15:12:19 -06:00
Mauricio Baeza 01ed2d9090 Mejorar el tiempo para generar el PDF 2018-02-22 15:12:04 -06:00
Mauricio Baeza 765e449d8f Timbrar con Ecodex 2018-02-22 14:37:40 -06:00
Mauricio Baeza b90e0a26ba Merge branch 'develop'
Quitar forma de pago en PDF
2018-02-20 18:05:01 -06:00
Mauricio Baeza cee51df84a Quitar forma de pago en PDF 2018-02-20 18:04:37 -06:00
Mauricio Baeza 9ae0d7d924 Merge branch 'develop'
Quitar forma de pago en traslado
2018-02-20 16:07:46 -06:00
Mauricio Baeza ff125d4623 Quitar forma de pago en traslado 2018-02-20 16:07:23 -06:00
Mauricio Baeza 07d51db625 Merge branch 'develop'
Fix - Issue #191
2018-02-20 11:03:40 -06:00
Mauricio Baeza 952782f430 Fix - Issue #191 2018-02-20 11:03:21 -06:00
Mauricio Baeza a903f967b2 Fix - Issue #191 2018-02-20 10:11:48 -06:00
Mauricio Baeza eedd253e6a Merge branch 'develop'
Timbrar a extranjeros
2018-02-18 23:57:58 -06:00
Mauricio Baeza 9605a96e7b Actualizar lista de cambios 2018-02-18 23:57:05 -06:00
Mauricio Baeza ac339e0ea8 Timbrar a extranjeros 2018-02-17 21:33:02 -06:00
Mauricio Baeza 362bec1fc3 Guardar camos vacíos en Socios 2018-02-17 18:47:37 -06:00
Mauricio Baeza 9d2b79b53b Tablas para permisos 2018-02-17 14:37:05 -06:00
Mauricio Baeza 73eb2f0b8c Merge branch 'develop'
Fix - Issue #190
2018-02-16 22:17:18 -06:00
Mauricio Baeza 8e75a6cc16 Fix - Issue #190 2018-02-16 22:16:46 -06:00
Mauricio Baeza fd334eade4 Merge branch 'develop'
Fix - Issue #189
2018-02-16 14:55:54 -06:00
Mauricio Baeza cf1eeac217 Fix - Issue #189 2018-02-16 14:55:31 -06:00
Mauricio Baeza 7bbfcd6cae Merge branch 'develop'
Fix - Issue #188
2018-02-16 13:54:22 -06:00
Mauricio Baeza c45d95d762 Fix - Issue #188 2018-02-16 13:52:30 -06:00
Mauricio Baeza 6381975518 Merge branch 'develop'
Obtener timbres disponibles
2018-02-13 23:15:18 -06:00
Mauricio Baeza 9f6e9e36fe Obtener timbres disponibles 2018-02-13 23:12:21 -06:00
Mauricio Baeza edd1be81ed Merge branch 'develop'
Fix - Issue #185
2018-02-13 12:07:29 -06:00
Mauricio Baeza cb7b7b0a1c Verificar XML en el SAT 2018-02-13 12:05:44 -06:00
Mauricio Baeza a1a17732e8 Fix - Issue #185 2018-02-13 11:42:38 -06:00
Mauricio Baeza e7a4d38681 Merge branch 'develop'
Fix - Issue #172
2018-02-12 23:52:00 -06:00
Mauricio Baeza ac4df43835 Fix - Issue #172 2018-02-12 23:51:42 -06:00
Mauricio Baeza 9a0f74eb52 Merge branch 'develop'
Fix - Issue #136
2018-02-12 23:14:57 -06:00
Mauricio Baeza ddb3588995 Fix - Issue #136 2018-02-12 23:12:50 -06:00
Mauricio Baeza cf73669e3a Merge branch 'develop'
Activar, desactivar cuentas de banco
2018-02-12 21:49:48 -06:00
Mauricio Baeza c38631daba Activar, desactivar cuentas de banco 2018-02-12 21:49:25 -06:00
Mauricio Baeza 3db6775f57 Merge branch 'develop'
Fix - Issue #174
2018-02-12 17:44:46 -06:00
Mauricio Baeza 75f2d9d662 Fix - Issue #174 2018-02-12 17:42:23 -06:00
Mauricio Baeza 535958f3c1 Fix - Issue #183 2018-02-12 16:01:46 -06:00
Mauricio Baeza 8a03dba323 Merge branch 'develop'
Fix - Precio con impuestos al facturar
2018-02-10 12:13:13 -06:00
Mauricio Baeza d6079a6a43 Fix - Precio con impuestos al facturar 2018-02-10 12:12:49 -06:00
Mauricio Baeza 874bf11447 Merge branch 'develop'
Fix - En notas
2018-02-10 11:00:46 -06:00
Mauricio Baeza 9d3818e4ce Fix - en unidades en notas 2018-02-10 11:00:30 -06:00
Mauricio Baeza 8db62db775 Fix - en notas 2018-02-10 10:57:40 -06:00
Mauricio Baeza bb3f6d3aed Merge branch 'develop'
Permitir repetir productos al facturar
2018-02-09 20:38:46 -06:00
Mauricio Baeza cdd97416ab Facturar en lote 2018-02-09 20:34:54 -06:00
Mauricio Baeza ebc544c8d8 XML en UTF8 al enviar por correo 2018-02-09 15:20:06 -06:00
Mauricio Baeza 5c3fb3e30f Borrar archivos temporales 2018-02-08 22:24:32 -06:00
Mauricio Baeza 3c76d3a55f Fix - Cuando no hay emisor en Nómina 2018-02-08 17:58:24 -06:00
Mauricio Baeza 59d7e9d29d Cambiar unidad al facturar 2018-02-08 12:33:50 -06:00
Mauricio Baeza 6847222ed6 Cambiar unidad al facturar 2018-02-08 01:35:35 -06:00
Mauricio Baeza 89098eb21a Facturar mismo producto varias veces 2018-02-08 01:05:30 -06:00
Mauricio Baeza fc55005b1a Facturar mismo producto varias veces 2018-02-08 01:04:58 -06:00
Mauricio Baeza 0ee99296a6 Merge branch 'develop'
Cambiar orden en movimientos bancarios
2018-02-07 21:36:56 -06:00
Mauricio Baeza f3435b7b0c Cambiar orden en movimientos bancarios 2018-02-07 21:36:38 -06:00
Mauricio Baeza e1424e2e70 Merge branch 'develop'
Quitar namespace de Nómina cuando no se usa
2018-02-07 12:54:41 -06:00
Mauricio Baeza 316eb14e8c Quitar namespace de Nómina cuando no se usa 2018-02-07 12:54:19 -06:00
Mauricio Baeza 5bfcc4ca07 Merge branch 'develop'
Importar XML
2018-02-07 00:48:26 -06:00
Mauricio Baeza 7185517a29 Importar XML 2018-02-07 00:48:06 -06:00
Mauricio Baeza b2148e1789 Merge branch 'develop'
Plantilla Nómina #173
2018-02-06 12:07:54 -06:00
Mauricio Baeza d622975a89 Plantilla Nómina #173 2018-02-06 12:07:35 -06:00
Mauricio Baeza badf1b0f5f Merge branch 'develop'
Borrar temporales
2018-02-06 00:01:27 -06:00
Mauricio Baeza d3419b5878 Borrar temporales 2018-02-05 23:59:49 -06:00
Mauricio Baeza f90539df82 Fix - Issue #161 2018-02-05 22:55:46 -06:00
Mauricio Baeza 1c65205bea Merge branch 'develop'
Generar PDF de Nómina
2018-02-03 01:26:26 -06:00
Mauricio Baeza 3c5c597b81 Generar PDF de Nómina 2018-02-03 01:23:05 -06:00
Mauricio Baeza 4bd7e335d8 Merge branch 'develop'
Fix Issue #170
2018-02-02 13:49:09 -06:00
Mauricio Baeza acb3d3f40c Fix Issue #170 2018-02-02 13:46:15 -06:00
Mauricio Baeza 8118ac306c Actualizar gitignore 2018-02-01 19:54:54 -06:00
Mauricio Baeza 09ccffdc62 Merge branch 'develop'
Eliminar tabla de estatod del SAT
2018-02-01 18:50:53 -06:00
Mauricio Baeza 17f6bdef98 Eliminar tabla de estatod del SAT 2018-02-01 18:50:33 -06:00
Mauricio Baeza 0207b9fa78 Merge branch 'develop'
Fix - Issue #160
2018-01-31 23:41:28 -06:00
Mauricio Baeza 34cb6a4a16 Fix - Issue #160 2018-01-31 23:40:57 -06:00
Mauricio Baeza 785cb32773 Merge branch 'develop'
Fix - Issue #167
2018-01-31 22:46:51 -06:00
Mauricio Baeza c13a0a9628 Fix - Issue #167 2018-01-31 22:46:29 -06:00
Mauricio Baeza 77ff862400 Merge branch 'develop'
Fix - Fecha de ingreso vacía
2018-01-31 22:41:09 -06:00
Mauricio Baeza f55a78060d Fix - Fecha de ingreso vacía 2018-01-31 22:40:47 -06:00
Mauricio Baeza 781623a64a Fix - Issue #166 2018-01-31 22:12:56 -06:00
Mauricio Baeza 1f40c42ba0 Fix - Issue #165 2018-01-31 21:51:22 -06:00
Mauricio Baeza 2272a4b6c7 Fix - Issue #164 2018-01-31 20:35:26 -06:00
Mauricio Baeza da730801d6 Merge branch 'develop'
Importar nóminas canceladas
2018-01-31 16:13:38 -06:00
Mauricio Baeza 3df8e353dd Importar nóminas canceladas 2018-01-31 16:13:17 -06:00
Mauricio Baeza 13d82f1df8 Merge branch 'develop'
Fix - Al generar PDF de una línea
Fix - Al mostrar buscar alumno
2018-01-31 01:47:19 -06:00
Mauricio Baeza 87e37c58f8 Fix - Mostrar buscar alumno 2018-01-31 01:47:01 -06:00
Mauricio Baeza 47b56bab90 Fix - Al generar PDF con una sola línea 2018-01-31 01:42:51 -06:00
Mauricio Baeza dcfc80edc8 Merge branch 'develop'
Cookies no seguras cuando no hay HTTPS
2018-01-30 23:41:05 -06:00
Mauricio Baeza f3a5aa4c06 Cookies no seguras cuando no hay HTTPS 2018-01-30 23:40:33 -06:00
Mauricio Baeza 3376505ea1 Merge branch 'develop'
Actualizar changelog y requerimientos
2018-01-30 22:17:33 -06:00
Mauricio Baeza abc1ae154b Actualizar changelog y requerimientos 2018-01-30 22:17:05 -06:00
Mauricio Baeza 21eafd9e4a Merge branch 'develop'
Timbrado de Nómina
Complemento EDU
2018-01-30 22:01:48 -06:00
Mauricio Baeza f28ab95b50 Nómina - Borrar en lote 2018-01-30 21:43:09 -06:00
Mauricio Baeza f9f80a52e7 Nómina - Obtener serie y folio de configuración 2018-01-30 20:27:50 -06:00
Mauricio Baeza 4bd5a71008 Fix - Issue #158 2018-01-30 18:55:00 -06:00
Mauricio Baeza 2b7a51db97 Mensaje cuando no hay documentos por timbrar 2018-01-30 18:37:41 -06:00
Mauricio Baeza b56907e74e Cambiar restricción en tabla Puestos 2018-01-30 13:59:27 -06:00
Mauricio Baeza 363f8bfce3 Cambiar version en xslt iedu 2018-01-30 13:21:45 -06:00
Mauricio Baeza b7b96611a6 Fix - Al eliminar nivel educativo 2018-01-30 10:10:09 -06:00
Mauricio Baeza 5c05417999 Fix - Al eliminar nivel educativo 2018-01-30 09:59:28 -06:00
Mauricio Baeza 06c32e56a8 UI para importar XML 2018-01-30 00:41:43 -06:00
Mauricio Baeza e67a1756a8 Fix - al generar xml 2018-01-29 23:51:08 -06:00
Mauricio Baeza e33e3379d7 Importar catalogos del SAT para nómina 2018-01-29 23:34:06 -06:00
Mauricio Baeza 7d386eca62 Nomina en develop 2018-01-29 22:43:43 -06:00
Mauricio Baeza 5466726898 Obtener registro de nómina 2018-01-29 21:57:44 -06:00
Mauricio Baeza 069136d4c5 Validar celdas con formato texto 2018-01-29 20:42:13 -06:00
Mauricio Baeza 2141f84402 EDU - Quitar RFC como obligatorio 2018-01-29 14:23:37 -06:00
Mauricio Baeza 049f812459 EDu - Quitar RFC como obligatorio 2018-01-29 14:23:12 -06:00
Mauricio Baeza 0e909628dc Complemento EDU 2018-01-29 13:21:48 -06:00
Mauricio Baeza 7a573b8eda Complemento EDU 2018-01-29 13:21:06 -06:00
Mauricio Baeza 85ff55a2db Editar alumnos 2018-01-29 10:43:25 -06:00
Mauricio Baeza c21d14fe35 Agregar alumnos 2018-01-29 01:55:42 -06:00
Mauricio Baeza b4f7749c6b Niveles educativos 2018-01-29 00:05:54 -06:00
Mauricio Baeza 1cfea6978b Timbrar y cancelar nómina 2018-01-28 21:35:10 -06:00
Mauricio Baeza 6a66b15f56 Importar nómina 2018-01-28 03:12:35 -06:00
Mauricio Baeza 776c90a467 Refactorizar generación de PDF 2018-01-27 02:45:45 -06:00
Mauricio Baeza cd8bd1ceeb Import nomina from template 2018-01-26 14:15:21 -06:00
Mauricio Baeza b4cab5aa2f Add button for import nomina 2018-01-26 14:15:21 -06:00
Mauricio Baeza 1155b96eeb Eliminar empleado 2018-01-26 14:15:21 -06:00
Mauricio Baeza d2f4d7c49d Actualizar empleados 2018-01-26 14:15:21 -06:00
Mauricio Baeza 6202f9d424 Importar empleados 2018-01-26 14:15:21 -06:00
Mauricio Baeza 6b24d7819d Cambios en develop 2018-01-26 14:15:21 -06:00
Mauricio Baeza 33994b6970 Importar empleados 2018-01-26 14:15:21 -06:00
Mauricio Baeza 9c6659193c Interfaz para nómina 2018-01-26 14:15:21 -06:00
Mauricio Baeza 133aed9c85 Tablas para Nómina 2018-01-26 14:15:21 -06:00
Mauricio Baeza 012331b523 Validar clave SAT al importar productos 2018-01-26 14:01:27 -06:00
Mauricio Baeza 9785c7a72c Validar clave SAT al importar productos 2018-01-26 14:01:06 -06:00
Mauricio Baeza 64f5dd9170 Fix - Issue #153 2018-01-26 13:46:56 -06:00
Mauricio Baeza 8e4f43136c Fix - Issue #153 2018-01-26 13:46:16 -06:00
Mauricio Baeza e1b975f0f9 Change in default template 2018-01-25 23:10:40 -06:00
Mauricio Baeza 8aba4efc12 Change in default template 2018-01-25 23:10:06 -06:00
Mauricio Baeza b73d0d6284 Config title app 2018-01-25 23:06:23 -06:00
Mauricio Baeza a01a085916 Config title app 2018-01-25 23:06:03 -06:00
Mauricio Baeza 83dff5bc5f Fix - Issue #149 2018-01-25 22:07:30 -06:00
Mauricio Baeza 8bbbd9cdfb Fix - Issue #149 2018-01-25 22:07:11 -06:00
Mauricio Baeza ac481862d9 Fix - Issue #150 2018-01-25 21:43:38 -06:00
Mauricio Baeza de4499f238 Fix - Issue #150 2018-01-25 21:43:09 -06:00
Mauricio Baeza 14f17a6188 Fix - Issue #148 2018-01-25 15:44:40 -06:00
Mauricio Baeza 506cbde15b Fix - Issue #148 2018-01-25 15:44:22 -06:00
Mauricio Baeza 7e91476e47 Fix - Issue #147 2018-01-25 12:51:16 -06:00
Mauricio Baeza 4e3aa6f8a2 Fix - Issue #147 2018-01-25 12:50:41 -06:00
Mauricio Baeza e728d948d0 Fix - Issue #147 2018-01-25 12:07:23 -06:00
Mauricio Baeza e1ab743c29 Fix - Issue #147 2018-01-25 12:07:00 -06:00
Mauricio Baeza 5a791eec7c Fix - Al agregar empresa en MV 2018-01-25 10:22:22 -06:00
Mauricio Baeza 2c6e70a4b9 Fix - Al agregar empresa en MV 2018-01-25 10:21:56 -06:00
Mauricio Baeza 6687473931 Fix - Issue #146 2018-01-25 01:21:19 -06:00
Mauricio Baeza 675855c73b Fix - Init session 2018-01-25 00:52:45 -06:00
Mauricio Baeza 8da034885e Fix - Init session 2018-01-25 00:39:26 -06:00
Mauricio Baeza 7455c7de35 Fix - Init session 2018-01-25 00:39:04 -06:00
Mauricio Baeza 4cc5231ea9 Fix - Connection without session 2018-01-24 23:45:18 -06:00
Mauricio Baeza 0ce48a5977 Fix - Connection without session 2018-01-24 23:44:55 -06:00
Mauricio Baeza e326490e7f Fix - Show values session 2018-01-24 23:37:31 -06:00
Mauricio Baeza 4ff9a03664 Fix - Show values session 2018-01-24 23:37:11 -06:00
Mauricio Baeza e6d616a8ec Fix - Fields in send mail 2018-01-24 23:30:56 -06:00
Mauricio Baeza 8ef8fdb714 Fix - Fields in send mail 2018-01-24 23:30:33 -06:00
Mauricio Baeza 45acb380b6 Fix - userobj in session 2018-01-24 23:11:19 -06:00
Mauricio Baeza 5259fe9c80 Fix - userobj in session 2018-01-24 23:10:58 -06:00
Mauricio Baeza 33b5966a86 Quitar validacion cliente especial 2018-01-24 22:09:05 -06:00
Mauricio Baeza 39a64f1437 Quitar validacion cliente especial 2018-01-24 22:07:30 -06:00
Mauricio Baeza 1b710e729a Validacion cliente especial 2018-01-24 16:15:51 -06:00
Mauricio Baeza 853d8f7881 Validacion cliente especial 2018-01-24 16:15:29 -06:00
Mauricio Baeza 648c85095f Tasa nueva 2018-01-24 14:56:00 -06:00
Mauricio Baeza 35475694aa Tasa nueva 2018-01-24 14:55:42 -06:00
Mauricio Baeza 8889375a11 Tasa nueva 2018-01-24 14:51:25 -06:00
Mauricio Baeza 88b7fb0dc3 Tasa nueva 2018-01-24 14:50:51 -06:00
Mauricio Baeza c1cb34ba3e Fix - Issue #143 2018-01-23 16:26:34 -06:00
Mauricio Baeza 49563020e4 Fix - Issue #143 2018-01-23 16:25:01 -06:00
Mauricio Baeza deeeabd376 Mantener orden de productos al facturar una prefactura 2018-01-23 16:13:49 -06:00
Mauricio Baeza a0db6d1f13 Fix - Issue #143 2018-01-23 16:10:45 -06:00
Mauricio Baeza d10a7c1641 Fix - Issue #143 2018-01-23 16:10:22 -06:00
Mauricio Baeza 99e843db15 Fix - Issue #141 2018-01-23 00:20:23 -06:00
Mauricio Baeza 2495997dad Fix - Issue #141 2018-01-23 00:19:57 -06:00
Mauricio Baeza 67ae4d0319 Capturar precio con impuestos la facturar 2018-01-22 20:06:49 -06:00
Mauricio Baeza cbc3ac0791 Capturar precio con impuestos la facturar 2018-01-22 20:06:22 -06:00
Mauricio Baeza 43cc6e0cc6 Agregar impuesto IEPS 8 2018-01-22 14:52:02 -06:00
Mauricio Baeza 5cfbb70c4c Agregar impuesto IEPS 8 2018-01-22 14:51:32 -06:00
Mauricio Baeza 3804378611 Agregar RFC sin crear base de datos 2018-01-22 13:13:50 -06:00
Mauricio Baeza d44d0ea263 Agregar RFC sin crear base de datos 2018-01-22 13:13:21 -06:00
Mauricio Baeza 71a292796c Ajuste de impuestos con 4 decimales 2018-01-21 21:39:02 -06:00
Mauricio Baeza edcd1ae04e Ajuste de impuestos con 4 decimales 2018-01-21 21:38:29 -06:00
Mauricio Baeza 046a61a2f2 Fix - abrir prefacturas grandes 2018-01-21 17:21:39 -06:00
Mauricio Baeza a946a1aa9a Fix - impuestos en prefactura 2018-01-21 17:04:21 -06:00
Mauricio Baeza a1a37d17ed Fix - Issue #139 2018-01-21 14:09:43 -06:00
Mauricio Baeza f4863db8ea Fix - Issue #139 2018-01-21 14:08:49 -06:00
Mauricio Baeza 2c5603d81b Tickets, mover cursor a buscar 2018-01-20 18:49:07 -06:00
Mauricio Baeza 9fcd8c0503 Tickets, mover cursor a buscar 2018-01-20 18:48:46 -06:00
Mauricio Baeza a1cc42f02e Abrir PDFs grandes 2018-01-20 01:33:29 -06:00
Mauricio Baeza 6a32760b05 Abrir PDFs grandes 2018-01-20 01:33:02 -06:00
Mauricio Baeza a9a0f32504 Facturar productos en lote 2018-01-19 22:16:46 -06:00
Mauricio Baeza 7873a4376c Facturar productos en lote 2018-01-19 22:16:19 -06:00
Mauricio Baeza 095e2e9dad Importar productos en lote 2018-01-19 15:42:23 -06:00
Mauricio Baeza 65a3bc931f Importar productos en lote 2018-01-19 15:42:00 -06:00
Mauricio Baeza 05a6357b7b Ajuste en plantilla 2018-01-18 22:05:31 -06:00
Mauricio Baeza c1c50d5102 Ajuste en plantilla 2018-01-18 22:04:06 -06:00
Mauricio Baeza 82c8f31043 Calculo de impuestos locales antes del descuento 2018-01-18 15:13:19 -06:00
Mauricio Baeza aa66bc47dc Calculo de impuestos locales antes del descuento 2018-01-18 15:12:33 -06:00
Mauricio Baeza 1cd87c81e2 Descargar ODS 2018-01-18 00:15:41 -06:00
Mauricio Baeza 1cc7d1f1b8 Descargar ODS 2018-01-18 00:15:14 -06:00
Mauricio Baeza 06430aa922 Propuesta - Issue #129 2018-01-17 23:40:46 -06:00
Mauricio Baeza 3f835d4d5c Propuesta - Issue #129 2018-01-17 23:40:19 -06:00
Mauricio Baeza bae8af881e Fix - Issue #129 2018-01-17 23:29:56 -06:00
Mauricio Baeza 4fc525246a Fix - Issue #129 2018-01-17 23:29:30 -06:00
Mauricio Baeza e63fb7b5c4 Orden de productos en prefactura 2018-01-17 23:22:09 -06:00
Mauricio Baeza cc10de702b Orden de productos en prefactura 2018-01-17 23:21:23 -06:00
Mauricio Baeza bb7486be94 Edición de notas en facturas 2018-01-16 23:13:50 -06:00
Mauricio Baeza 8f7c1c3a31 Fix - Issue #94 2018-01-16 22:04:27 -06:00
Mauricio Baeza d7d6b6efda Fix - Issue #130 2018-01-16 21:08:30 -06:00
Mauricio Baeza f856d432b2 Fix - Mostrar clave sat al agregar o actualizar producto 2018-01-16 21:00:49 -06:00
Mauricio Baeza af0b2b6c56 Fix - Al obtener nueva clave en productos 2018-01-16 20:52:37 -06:00
Mauricio Baeza 741d0c506f Agregar impuestos locales 2018-01-16 13:26:06 -06:00
Mauricio Baeza 6df74dfc05 Fix - Issue #134 2018-01-16 11:28:59 -06:00
Mauricio Baeza 6125623c5f Fix - al enviar correo 2018-01-16 10:12:53 -06:00
Mauricio Baeza fe949de513 Fix - Issue 120 2018-01-14 20:31:40 -06:00
Mauricio Baeza 51ae946b79 Usos del CFDI ordenar por nombre 2018-01-14 00:39:28 -06:00
Mauricio Baeza 95f0a0b490 Administrar Usos del CFDI 2018-01-14 00:31:59 -06:00
Mauricio Baeza 5aaa6c1d20 Administrar Usos del CFDI 2018-01-14 00:31:36 -06:00
Mauricio Baeza 564d784a63 Cambio de editor en descripción 2018-01-13 18:58:55 -06:00
Mauricio Baeza fe37e92766 Cambio de editor en descripción 2018-01-13 18:39:22 -06:00
Mauricio Baeza 02d59dfc6d Total en prefacturas 2018-01-13 15:25:06 -06:00
Mauricio Baeza 31bfc9e955 Fix - Correo 2018-01-13 13:37:39 -06:00
Mauricio Baeza 99ae0181c1 Fix - Correo 2018-01-13 13:37:19 -06:00
Mauricio Baeza e196a5fac9 Fix - En categorias nulas al importar 2018-01-12 20:14:09 -06:00
Mauricio Baeza ab31399670 Fix - En categorias nulas al importar 2018-01-12 20:13:44 -06:00
Mauricio Baeza 1fe6a6b904 Fix - en impuestos al importar 2018-01-12 19:54:40 -06:00
Mauricio Baeza d470b44339 Fix - en impuestos al importar 2018-01-12 19:52:17 -06:00
Mauricio Baeza 3a9d3c2c58 Fix - cantidad en letras otras monedas 2018-01-12 11:02:39 -06:00
Mauricio Baeza 772d5d0af0 Fix - cantidad en letras otras monedas 2018-01-12 11:02:13 -06:00
Mauricio Baeza f5aa8b7936 Fix - Cantidad en letras cuando no es peso 2018-01-12 10:42:14 -06:00
Mauricio Baeza 1867c4aa03 Fix - Cantidad en letras cuando no es peso 2018-01-12 10:41:45 -06:00
Mauricio Baeza 58f167d7f0 Fix - Issue 120 2018-01-12 10:27:08 -06:00
Mauricio Baeza 98c58a9003 Fix - Issue 120 2018-01-12 10:26:28 -06:00
Mauricio Baeza 52151fcd2b Fix - Issue 117 2018-01-11 18:57:26 -06:00
Mauricio Baeza 03fecfb1e2 Fix - Issue 117 2018-01-11 18:57:07 -06:00
Mauricio Baeza 137f38c773 Cambio de nombre de archivo de productos importados 2018-01-11 16:46:13 -06:00
Mauricio Baeza 069a4cdfa1 Cambio de nombre de archivo de productos importados 2018-01-11 14:58:06 -06:00
Mauricio Baeza fa4a55db19 Agregar ejemplo para configuracion de nginx 2018-01-11 13:41:44 -06:00
Mauricio Baeza 752e0f27ad Agregar ejemplo para configuracion de nginx 2018-01-11 13:41:19 -06:00
Mauricio Baeza 4e98593e61 Agregar ejemplo para servicio en systemd 2018-01-11 13:01:17 -06:00
Mauricio Baeza da0afdbe77 Cambio de rutas en cache de sesiones 2018-01-11 10:48:56 -06:00
Mauricio Baeza 04fda8b9a2 Cambio de rutas en cache de sesiones 2018-01-11 10:20:55 -06:00
Mauricio Baeza 595e00fb13 Aplicar estilo euros 2018-01-11 00:35:55 -06:00
Mauricio Baeza 8642769580 Aplicar estilo euros 2018-01-11 00:35:32 -06:00
Mauricio Baeza 81b34094ee Ocultar datos al facturar 2018-01-10 23:53:41 -06:00
Mauricio Baeza bde6943e73 Ocultar datos al facturar 2018-01-10 23:53:19 -06:00
Mauricio Baeza 234335a37f Fix - Issue 117 2018-01-10 23:42:40 -06:00
Mauricio Baeza e2643c9984 Fix - Issue 117 2018-01-10 23:42:19 -06:00
Mauricio Baeza 1cd9b541a3 Refactorizar notas 2018-01-10 23:39:47 -06:00
Mauricio Baeza ead466e90c Prefacturas, guardar y recuperar notas 2018-01-10 23:39:15 -06:00
Mauricio Baeza 3029f84874 Invoice, guardar notas 2018-01-10 23:21:37 -06:00
Mauricio Baeza 59de554b2b Tickets, ir al final en nota 2018-01-10 22:52:17 -06:00
Mauricio Baeza bee911504b Tickets, guardar notas 2018-01-10 22:03:46 -06:00
Mauricio Baeza 6f9b7df71e Fix - Issue 115 2018-01-10 21:44:41 -06:00
Mauricio Baeza 98e6cc389a Fix - Issue 115 2018-01-10 21:44:15 -06:00
Mauricio Baeza c09494546b Soporte para nueva impresora de tickets 2018-01-10 21:40:19 -06:00
Mauricio Baeza f8932d369d Ticket, mostrar total arriba 2018-01-10 01:35:11 -06:00
Mauricio Baeza 7cfa0f3834 Fix - Fecha vacia al importar 2018-01-09 15:14:04 -06:00
Mauricio Baeza b48d6bd1f5 Fix - Fecha vacia al importar 2018-01-09 15:13:40 -06:00
Mauricio Baeza 87884d0438 Fix - Issue 111 2018-01-08 22:47:22 -06:00
Mauricio Baeza 8048a07852 Fix - Issue 111 2018-01-08 22:46:56 -06:00
Mauricio Baeza a7d694e1dc Fix - Al buscar productos 2018-01-08 22:12:04 -06:00
Mauricio Baeza 5fa6ce9394 Fix - Al buscar productos 2018-01-08 22:11:31 -06:00
Mauricio Baeza 2a141c22f1 Fix - Tasa -16 al importar 2018-01-08 16:19:44 -06:00
Mauricio Baeza 4e428ab1b7 Fix - Tasa -16 al importar 2018-01-08 16:19:21 -06:00
Mauricio Baeza aa6d317c85 Soporte para pedimentos 2018-01-08 16:01:26 -06:00
Mauricio Baeza 4221db615c Soporte para pedimentos 2018-01-08 16:00:55 -06:00
Mauricio Baeza 8c8c3afd30 Fix - Ajuste al agregar producto 2018-01-08 13:57:38 -06:00
Mauricio Baeza 19c0ec91af Fix - Ajuste al agregar producto 2018-01-08 13:57:15 -06:00
Mauricio Baeza 21bf7451cc Soporte para pedimentos 2018-01-08 09:41:10 -06:00
Mauricio Baeza 6de21e75bd Soporte para pedimentos 2018-01-08 09:40:35 -06:00
Mauricio Baeza f5b34eec5b Llevar inventario 2018-01-05 13:33:43 -06:00
Mauricio Baeza 5f6ccf1a77 Fix - Pasar notas de ticket a factura 2018-01-04 21:30:31 -06:00
Mauricio Baeza d88caeb8c6 Fix - Pasar notas de ticket a factura 2018-01-04 21:29:40 -06:00
Mauricio Baeza ca9fe39f5e Fix - al importar producto exento 2018-01-04 21:20:14 -06:00
Mauricio Baeza 4a0fed1630 Fix - Validar impuestos incorrectos 2018-01-04 17:12:22 -06:00
Mauricio Baeza 0d02c8bd3e Fix - Al borrar cuenta predial 2018-01-04 09:18:39 -06:00
Mauricio Baeza 0662a046c2 Fix - Al borrar cuenta predial 2018-01-04 09:18:12 -06:00
Mauricio Baeza 012936cc67 Fix - nuevas unidades 2018-01-03 19:17:56 -06:00
Mauricio Baeza 35806fcfcb Fix - Iusse 88 2018-01-03 15:25:23 -06:00
Mauricio Baeza 0bb7eca83e Fix - Iusse 88 2018-01-03 15:24:33 -06:00
Mauricio Baeza 24ce672dfa Fix - Al importar productos 2018-01-03 11:56:28 -06:00
Mauricio Baeza 49595b6ab0 Fix - Al importar productos 2018-01-03 11:56:06 -06:00
Mauricio Baeza 8993362277 Fix - Al generar ticket 2018-01-03 11:44:13 -06:00
Mauricio Baeza 7b4563ed17 Fix - Al generar tickett 2018-01-03 11:43:43 -06:00
Mauricio Baeza 7e08bfb5a5 Solicitar cantidad en ticket 2018-01-03 00:27:10 -06:00
Mauricio Baeza 233482c16c Solicitar cantidad en ticket 2018-01-03 00:26:44 -06:00
Mauricio Baeza 9e7e7a6a3d Fix - iusse 84 2018-01-02 19:43:28 -06:00
Mauricio Baeza af26b9d62f Fix - iusse 84 2018-01-02 19:43:01 -06:00
Mauricio Baeza 0993bee9ed Reporte en facturas 2018-01-02 18:25:23 -06:00
Mauricio Baeza b401fa21a5 Reporte en facturas 2018-01-02 18:24:55 -06:00
Mauricio Baeza 4632779331 Fix - impuesto ICIC 1 al importar 2018-01-02 14:27:31 -06:00
Mauricio Baeza aeff1a3691 Fix - impuesto ICIC 1 al importar 2018-01-02 14:26:53 -06:00
Mauricio Baeza 1d35ce7324 Fix - unidades al importar 2018-01-02 13:01:05 -06:00
Mauricio Baeza 9742550c27 Fix - unidades al importar 2018-01-02 13:00:39 -06:00
Mauricio Baeza 5c72ff459d Validar serie por usuario/sucursal 2018-01-02 01:38:19 -06:00
Mauricio Baeza ba4c3d9dbd Validar serie por usuario/sucursal 2018-01-02 01:37:52 -06:00
Mauricio Baeza ffda7867a4 Reportes de tickets 2018-01-01 22:31:06 -06:00
Mauricio Baeza d1158bc285 Editar precio en tickets 2018-01-01 22:18:09 -06:00
Mauricio Baeza 5a104b4a57 Imprimir Ticket directamente 2018-01-01 22:14:49 -06:00
Mauricio Baeza ef08e750bb Reportes de Tickets 2018-01-01 21:23:32 -06:00
Mauricio Baeza 1de2ff7255 Configuración de impresora 2018-01-01 17:56:51 -06:00
Mauricio Baeza 14b4421eda Configuración de impresora 2018-01-01 17:55:04 -06:00
Mauricio Baeza afd6c71066 Merge branch 'develop'
Fix - Al importar escpos
2018-01-01 13:31:23 -06:00
Mauricio Baeza 130180ba14 Fix - Al importar escpos 2018-01-01 13:31:01 -06:00
Mauricio Baeza 2944ddc6f1 Merge branch 'develop'
Filtros por fecha y reporte de tickets
2018-01-01 13:18:25 -06:00
Mauricio Baeza 2a92b3df58 Reporte de tickets en PDF 2018-01-01 13:17:36 -06:00
Mauricio Baeza 05c540495e Mostrar plantillas de acuerdo a configuración 2018-01-01 11:54:37 -06:00
Mauricio Baeza caee632ebb Filtro por fechas en tickets 2018-01-01 10:37:16 -06:00
Mauricio Baeza 1c3c75af13 Filtro hoy 2018-01-01 10:25:30 -06:00
Mauricio Baeza fad45cb502 Imprimir ticket 2018-01-01 02:14:30 -06:00
Mauricio Baeza 75d990d741 Merge branch 'develop'
Agregar RFC desde script
2017-12-31 17:32:44 -06:00
Mauricio Baeza 3e323678d7 Agregar RFC desde script 2017-12-31 17:32:22 -06:00
Mauricio Baeza 9376fee407 Generar PDF de Ticket 2017-12-31 00:17:20 -06:00
Mauricio Baeza e39e4c5585 Opción para abrir el PDF al timbrar 2017-12-30 19:32:24 -06:00
Mauricio Baeza 448eeb653c Merge branch 'develop'
Editar clientes y productos con doble click
2017-12-30 17:15:31 -06:00
Mauricio Baeza 5ab7df2fd8 Editar clientes y productos con doble click 2017-12-30 17:15:08 -06:00
Mauricio Baeza 2cc0c9fc44 Merge branch 'develop'
Notas en tickets
2017-12-30 15:17:35 -06:00
Mauricio Baeza 00988fca52 Notas en tickets 2017-12-30 15:17:12 -06:00
Mauricio Baeza 7b1e015020 Merge branch 'develop'
Enviar factura en ZIP
2017-12-30 00:20:12 -06:00
Mauricio Baeza dd2ff53e6a Enviar factura en ZIP 2017-12-30 00:19:48 -06:00
Mauricio Baeza 72a5c72ed3 Merge branch 'develop'
Agregar confirmación de correo
2017-12-29 23:34:02 -06:00
Mauricio Baeza aa9eea9dd5 Agregar confirmación de correo 2017-12-29 23:33:47 -06:00
Mauricio Baeza 82079423c9 Merge branch 'develop'
Fix - Al enviar correo
2017-12-29 23:01:25 -06:00
Mauricio Baeza 524db01ac9 Fix - Al enviar correo 2017-12-29 23:01:04 -06:00
Mauricio Baeza ee8c437532 Merge branch 'develop'
Fix - Mostrar variable al facturar
2017-12-29 16:35:47 -06:00
Mauricio Baeza 42d041c715 Fix - Mostrar variable al facturar 2017-12-29 16:35:24 -06:00
Mauricio Baeza 3626303bc7 Merge branch 'develop'
Fix - Importacón especial de Factura Libre
2017-12-29 14:28:37 -06:00
Mauricio Baeza 67584b2f67 Fix - Importacón especial de Factura Libre 2017-12-29 14:28:15 -06:00
Mauricio Baeza 02d843ac3f Merge branch 'develop'
Notas en facturas
2017-12-29 04:21:45 -06:00
Mauricio Baeza 8032c867c0 Fix - Admin bancos 2017-12-29 04:21:25 -06:00
Mauricio Baeza e1a683ae23 Notas en factura 2017-12-29 04:09:02 -06:00
Mauricio Baeza 71884e9984 Fix - PDF cuando no hay areas de impresion 2017-12-29 02:36:13 -06:00
Mauricio Baeza 7463a692b4 Merge branch 'develop'
Fix - al importar de Factura Libre
2017-12-28 19:03:24 -06:00
Mauricio Baeza c314268b35 Fix - al importar de Factura Libre 2017-12-28 19:03:01 -06:00
Mauricio Baeza 4c85851b45 Merge branch 'develop'
Fix - al importar de Factura Libre
2017-12-28 18:27:15 -06:00
Mauricio Baeza d3afe55eef Fix - al importar de Factura Libre 2017-12-28 18:26:38 -06:00
Mauricio Baeza c027f7a136 Merge branch 'develop'
Fix - Tasa -4 al importar de Factura Libre
2017-12-28 17:31:23 -06:00
Mauricio Baeza 1c8fff1d37 Fix - Tasa -4 al importar de Factura Libre 2017-12-28 17:31:02 -06:00
Mauricio Baeza 5413f80c5a Merge branch 'develop'
Fix - Editar productos
2017-12-28 12:58:56 -06:00
Mauricio Baeza c10039a561 Fix - Editar productos 2017-12-28 12:58:38 -06:00
Mauricio Baeza ac3168e7b0 Merge branch 'develop'
Fix - al crear nuevas empresas
2017-12-28 10:32:24 -06:00
Mauricio Baeza 3c1060252f Fix - al crear nuevos usuarios 2017-12-28 10:32:05 -06:00
Mauricio Baeza 37c3b00362 Merge branch 'develop'
Cambio de método de cancelación. Mensaje al cancelar 3.2.
2017-12-28 01:13:49 -06:00
Mauricio Baeza c58c0ed395 Mensaje de cancelación para 3.2 2017-12-28 01:13:07 -06:00
Mauricio Baeza ce53a65c66 Cambio de método de cancelación 2017-12-28 01:08:49 -06:00
Mauricio Baeza 93e91ec367 Merge branch 'develop'
Fix - Al editar usuario
2017-12-28 00:05:29 -06:00
Mauricio Baeza ea8f88ef1d Fix - Al editar usuario 2017-12-28 00:04:55 -06:00
Mauricio Baeza b2f7cc4341 Merge branch 'develop'
Fix - al mostrar las empresas
2017-12-27 21:19:30 -06:00
Mauricio Baeza ac28ab43f8 Fix - al mostgrar las empresass 2017-12-27 21:19:07 -06:00
Mauricio Baeza 7861974761 Merge branch 'develop'
Punto de venta
2017-12-27 01:19:12 -06:00
Mauricio Baeza d4e9d1701a Actualizar lista de cambios 2017-12-27 01:18:27 -06:00
Mauricio Baeza 12bac7d8a6 Migracion para Usuarios 2017-12-27 01:12:58 -06:00
Mauricio Baeza c7e4febb0c Importación de Factura Libre Gambas completa 2017-12-26 23:55:38 -06:00
Mauricio Baeza 94750e8cac Fix al importar de Factura Libre Gambas 2017-12-26 23:41:00 -06:00
Mauricio Baeza 4699c429fa Importar de Factura Libre Gambas 2017-12-26 20:30:22 -06:00
Mauricio Baeza 160f8c3453 Mensaje para descripciones con más de 1000 caracteres 2017-12-26 00:02:34 -06:00
Mauricio Baeza 28146ed47a Merge branch 'tickets' into develop
Generar tickets
2017-12-25 23:51:28 -06:00
Mauricio Baeza b777e1774f Generar factura de ticket 2017-12-25 23:30:34 -06:00
Mauricio Baeza 937329bd9c UI Ticket a factura 2017-12-24 23:54:19 -06:00
Mauricio Baeza c7bd84f9bf Ocultar método y forma de pago 2017-12-24 22:09:21 -06:00
Mauricio Baeza db4018661c Merge branch 'develop'
Fix 72
2017-12-24 16:44:15 -06:00
Mauricio Baeza ff0a2f27b1 Calcular precio sin impuestos 2017-12-23 23:54:47 -06:00
Mauricio Baeza d5027988dd Unidad predeterminada 2017-12-23 22:58:23 -06:00
Mauricio Baeza c47b242520 Mostrar código de barras en productos 2017-12-23 22:41:44 -06:00
Mauricio Baeza f430947708 Filtrar tickets. Cancelar tickets 2017-12-23 22:25:52 -06:00
Mauricio Baeza 9b4d0bffd7 Generar ticket 2017-12-23 22:25:52 -06:00
Mauricio Baeza ccdfacb476 Cambio de expiracion 2017-12-23 22:25:52 -06:00
Mauricio Baeza 3ef40892cc UI para nuevo ticket 2017-12-23 22:25:52 -06:00
Mauricio Baeza cab7878288 UI para administrar tickets 2017-12-23 22:25:52 -06:00
Mauricio Baeza 50ed11ed7f UI para nuevo ticket 2017-12-23 22:25:52 -06:00
Mauricio Baeza 19c0708d15 UI para administrar tickets 2017-12-23 22:25:52 -06:00
Mauricio Baeza 3a10c146d2 Agregar método alternativo para cancelar 2017-12-23 22:21:35 -06:00
Mauricio Baeza 9796ffb6f3 Ajuste ventana de folios en Admin 2017-12-23 17:54:47 -06:00
Mauricio Baeza 2229f5c63f Fix 72 2017-12-23 11:55:01 -06:00
Mauricio Baeza 3e6dc1eda2 Fix 72 2017-12-23 00:02:25 -06:00
Mauricio Baeza eaa762a7ff Merge branch 'develop'
Actualizar archivos de configuración
2017-12-20 22:04:10 -06:00
Mauricio Baeza c5da06ec31 Actualizar archivos de configuración 2017-12-20 22:01:13 -06:00
Mauricio Baeza 3949a89824 Fix - al importar de Factura Libre 2017-12-20 18:02:05 -06:00
Mauricio Baeza 25a331fa09 Merge branch 'develop'
Agregar nuevas monedas
2017-12-19 11:56:00 -06:00
Mauricio Baeza eed9b1d29c Cambio de expiracion 2017-12-19 11:55:36 -06:00
Mauricio Baeza db6743745b Agregar monedas 2017-12-19 01:22:22 -06:00
Mauricio Baeza 1997db32b0 Actualizar changelod 2017-12-18 02:37:36 -06:00
Mauricio Baeza f0ec3c0344 Actualizar changelod 2017-12-18 02:35:50 -06:00
Mauricio Baeza bc46bf992a Actualizar changelod 2017-12-18 02:30:33 -06:00
Mauricio Baeza 48670592f3 Merge branch 'develop'
Agregar tablas para Alumnos,  Tickets y Rangos de precios
2017-12-18 02:17:00 -06:00
Mauricio Baeza 4db008dd00 Agregar openssl para Windows 2017-12-18 02:15:03 -06:00
Mauricio Baeza ce79d8a77c Agregar tablas para rangos de precios 2017-12-18 02:06:06 -06:00
Mauricio Baeza 7bdcdab7da Agregar tablas para Alumnos y Tickets 2017-12-18 01:46:53 -06:00
Mauricio Baeza 9e150a103e Sincronizar XML localmente 2017-12-17 22:55:32 -06:00
Mauricio Baeza 493f78fda5 Sincronizar PDF localmente 2017-12-17 22:38:19 -06:00
Mauricio Baeza 8fecfb8f56 Fix - A4 in PDF 2017-12-17 22:12:22 -06:00
Mauricio Baeza 57fad9ff09 Fix - A4 in PDF 2017-12-17 22:03:19 -06:00
Mauricio Baeza 677572c0d3 Fix - Obtener carpeta compartida 2017-12-16 23:57:12 -06:00
Mauricio Baeza 9221143b4a Ajustar expiración de sesión 2017-12-16 21:32:36 -06:00
Mauricio Baeza da95cc6bc0 Fix - Issue 61 2017-12-13 23:59:06 -06:00
Mauricio Baeza e8df72b0fc Ajustes en los archivos de ejemplo 2017-12-13 23:59:06 -06:00
Mauricio Baeza ed1f5a470f Statics servidos por Uwsgi en pruebas 2017-12-13 23:59:06 -06:00
Mauricio Baeza 9057c39247 Configuracion para Linux nativa con Uwsgi 2017-12-13 23:59:06 -06:00
Mauricio Baeza 4fc6908a8d Fix - al obtener carpeta compartida 2017-12-13 23:59:06 -06:00
Mauricio Baeza 3a88d82957 Copia local en carpeta compartida 2017-12-13 23:59:06 -06:00
Mauricio Baeza 980bb710ce Fix - Issue 57 2017-12-13 23:59:06 -06:00
Mauricio Baeza 13053b24e3 Fix - Issue 58 2017-12-13 23:59:06 -06:00
Mauricio Baeza 5d1d0c8c79 Fix - Activar tipo de cambio en otras monedas 2017-12-13 23:58:44 -06:00
Mauricio Baeza 4d0d387e19 Fix - Issue 61 2017-12-13 23:45:06 -06:00
Mauricio Baeza 53652804c7 Ajustes en los archivos de ejemplo 2017-12-13 22:33:00 -06:00
Mauricio Baeza 5acb893cf8 Statics servidos por Uwsgi en pruebas 2017-12-13 20:38:31 -06:00
Mauricio Baeza 29ecedda82 Configuracion para Linux nativa con Uwsgi 2017-12-13 20:34:38 -06:00
Mauricio Baeza 4755223c12 Fix - al obtener carpeta compartida 2017-12-13 02:11:14 -06:00
Mauricio Baeza a0c8b2e336 Copia local en carpeta compartida 2017-12-13 01:13:48 -06:00
Mauricio Baeza 9720bece80 Fix - Issue 57 2017-12-13 00:13:34 -06:00
Mauricio Baeza 589792c05f Fix - Issue 58 2017-12-12 23:36:22 -06:00
Mauricio Baeza 92ff62d9f0 Merge branch 'develop'
Mostrar mensaje no autorizado
2017-12-12 17:34:36 -06:00
Mauricio Baeza c02f5dc25f Mostrar mensaje no autorizado 2017-12-12 17:34:07 -06:00
Mauricio Baeza 0f82665722 Agregar tasa de retención 2017-12-11 21:44:40 -06:00
Mauricio Baeza d58ff69597 Merge branch 'develop'
Sincronizar facturas
2017-12-10 20:12:47 -06:00
Mauricio Baeza 858eafd6ab Sincronizar facturas 2017-12-10 19:45:19 -06:00
Mauricio Baeza b01f3e92a9 Merge branch 'develop'
Fix al actualizar contraseña de usuario
2017-12-10 15:51:42 -06:00
Mauricio Baeza b973fecaf3 Fix al actualizar contraseña de usuario 2017-12-10 15:51:22 -06:00
Mauricio Baeza efec0c1396 Merge branch 'develop'
402f54f Sincrionizacion SeaFile partícular
37aea44 Sincrionizacion con SeaFile general
f2c9de3 Backups in thread
f99a0da Generar backups de sqlite
50a8c27 Generar backups de base de datos
6644eef Fix - Issue 48
de675a0 Fix - Mostrar cuenta prefial en prefactura
38f2b5a Fix - Issue 47
bb14b54 Fix - Issue 46
2017-12-10 14:40:42 -06:00
Mauricio Baeza 402f54f9c6 Sincrionizacion SeaFile partícular 2017-12-10 14:35:03 -06:00
Mauricio Baeza 37aea443d6 Sincrionizacion con SeaFile general 2017-12-10 12:12:06 -06:00
Mauricio Baeza f2c9de3660 Backups in thread 2017-12-10 02:04:40 -06:00
Mauricio Baeza f99a0da8d9 Generar backups de sqlite 2017-12-10 01:06:24 -06:00
Mauricio Baeza 50a8c277bf Generar backups de base de datos 2017-12-10 00:59:39 -06:00
Mauricio Baeza 6644eef0ab Fix - Issue 48 2017-12-10 00:26:49 -06:00
Mauricio Baeza de675a0de1 Fix - Mostrar cuenta prefial en prefactura 2017-12-10 00:10:39 -06:00
Mauricio Baeza 38f2b5a610 Fix - Issue 47 2017-12-10 00:00:35 -06:00
Mauricio Baeza bb14b54402 Fix - Issue 46 2017-12-09 23:36:28 -06:00
Mauricio Baeza 37af9178f9 Merge branch 'develop'
Cuenta predial
2017-12-06 23:31:01 -06:00
Mauricio Baeza 815548845c Timbrar y generar PDF con Cuenta Predial 2017-12-06 23:30:40 -06:00
Mauricio Baeza 9c4aebf3d8 Configurar y usar Cuenta Predial 2017-12-06 23:02:52 -06:00
Mauricio Baeza 596f826c3d Merge branch 'develop'
Fix - Issue 43
2017-12-06 21:04:31 -06:00
Mauricio Baeza ef27dd8672 Fix - Issue 43 2017-12-06 21:04:06 -06:00
Mauricio Baeza 551d2c0eff Merge branch 'develop'
Fix - Issue 42
2017-12-06 19:14:10 -06:00
Mauricio Baeza 78c9d07057 Fix - Issue 42 2017-12-06 16:38:42 -06:00
Mauricio Baeza bd73871cb0 Merge branch 'develop'
Fix - Issue 41
2017-12-06 09:50:28 -06:00
Mauricio Baeza 85de4e5d90 Cambiar leyenda al buscar producto por clave 2017-12-06 00:30:30 -06:00
Mauricio Baeza d4af8c7771 Fix - Buscar por clave 2017-12-06 00:24:13 -06:00
Mauricio Baeza 498a496cd9 Fix - Buscar por clave 2017-12-06 00:21:22 -06:00
Mauricio Baeza 88c42a2b1b Merge branch 'develop'
Personalizar contraseña predeterminada
2017-12-05 22:52:49 -06:00
Mauricio Baeza f8afdcc59a Personalizar contraseña predeterminada 2017-12-05 22:39:46 -06:00
Mauricio Baeza 842d8610cc Merge branch 'develop'
Fix issue 40
2017-12-05 00:05:39 -06:00
Mauricio Baeza 0c575d9376 Merge branch 'issue40' into develop
Fix issue 40
2017-12-04 22:48:52 -06:00
Mauricio Baeza 7b495c4bbd Fix - Issue 40 2017-12-04 22:48:18 -06:00
Mauricio Baeza 0f9c03c68b Merge branch 'develop'
Fix - Ir al admin para SU
2017-12-04 00:17:17 -06:00
Mauricio Baeza 9a4a35f03d Fix - Ir al admin para SU 2017-12-04 00:16:53 -06:00
Mauricio Baeza a9c1f1090c Merge branch 'develop'
Fix - Ir al admin para no admins
2017-12-03 23:51:16 -06:00
Mauricio Baeza d80cfd983d Fix - Ocultar icono admin para usuarios no admin 2017-12-03 23:51:02 -06:00
Mauricio Baeza 15a31d41f6 Fix - Usuario no admin 2017-12-03 23:47:41 -06:00
Mauricio Baeza c9ab9c39a1 Merge branch 'develop'
Agregar empresas graficamente
Administrar usuarios en admin
2017-12-03 23:37:07 -06:00
Mauricio Baeza 6db103caba Cambiar iconos en grid de facturas 2017-12-03 23:36:47 -06:00
Mauricio Baeza 3d89724290 Fix - Mostrar usuario en admin 2017-12-03 22:33:21 -06:00
Mauricio Baeza 18a6678b19 Agregada la administración de usuarios 2017-12-03 21:13:31 -06:00
Mauricio Baeza 191774f0e3 Fix - Mostrar empresas en UI 2017-12-03 00:24:23 -06:00
Mauricio Baeza b59c9ae000 Agregar empresas en UI 2017-12-03 00:09:44 -06:00
Mauricio Baeza 5a3421f296 Merge branch 'develop'
Fix - PDF folio en prefacturas
2017-12-02 18:29:54 -06:00
Mauricio Baeza 7a3e12d30c Fix - PDF folio en prefacturas 2017-12-02 18:29:30 -06:00
Mauricio Baeza 4502cc8dff Merge branch 'develop'
Fix - PDF serie y folio en prefacturas
2017-12-02 18:04:19 -06:00
Mauricio Baeza 8de2d61fa4 Fix - PDF serie y folio en prefacturas 2017-12-02 18:03:56 -06:00
Mauricio Baeza ac3fa342d6 Merge branch 'develop'
Fix - donataria PDF version 3.2
2017-12-02 17:46:08 -06:00
Mauricio Baeza c790208706 Fix - donataria PDF version 3.2 2017-12-02 17:45:46 -06:00
Mauricio Baeza 143d9c2f0e Merge branch 'develop'
Fix - Plantilla ODS predeterminada
2017-12-02 01:07:14 -06:00
Mauricio Baeza 5c84f590c4 Fix - Plantilla ODS predeterminada 2017-12-02 00:51:05 -06:00
Mauricio Baeza 95db2cbda7 Merge branch 'develop'
Fix - En sessiones mal cerradas
2017-12-01 12:48:04 -06:00
Mauricio Baeza 739a4a88b2 Fix - En sessiones mal cerradas 2017-12-01 12:45:13 -06:00
Mauricio Baeza 95503188d9 Merge branch 'develop'
Fix - Issues 33 y 34
2017-12-01 11:06:40 -06:00
Mauricio Baeza 848c507e47 Fix - Issue 33 2017-12-01 09:55:55 -06:00
Mauricio Baeza 671f9698cf Fix - Issue 34 2017-12-01 09:21:10 -06:00
Mauricio Baeza b9dde26062 Merge branch 'develop'
Fix - Plantilla ODS predeterminada
2017-11-30 22:31:04 -06:00
Mauricio Baeza 5efc3a9ce7 Fix - Plantilla ODS predeterminada 2017-11-30 21:40:49 -06:00
Mauricio Baeza a30bb4fcd3 Fix - al identificarse con datos inexistentes 2017-11-30 17:34:25 -06:00
Mauricio Baeza e9fa41a439 Merge branch 'develop'
Versión 1.0.0
2017-11-30 00:41:03 -06:00
Mauricio Baeza b3c2e16841 Agregar emisor UI 2017-11-30 00:37:13 -06:00
Mauricio Baeza cb18e3c3d2 Subir base de datos de Factura Libre 2017-11-29 23:57:31 -06:00
Mauricio Baeza c5c79eace8 Cancelar depositos 2017-11-29 21:39:19 -06:00
Mauricio Baeza 5cda707d59 Agregar indice para las cancelaciones en depositos de facturas 2017-11-29 19:41:03 -06:00
Mauricio Baeza f1d47d1119 Agregar deposito con facturas relacionadas 2017-11-29 15:21:51 -06:00
Mauricio Baeza d06a5b8361 Mostrar nombre de empresa 2017-11-26 23:40:14 -06:00
Mauricio Baeza e16ee337a3 Donativo en especien 2017-11-26 13:56:10 -06:00
Mauricio Baeza c0d1c41401 SAT formas de pago en Admin 2017-11-26 13:39:32 -06:00
Mauricio Baeza 0bc0c92d3d Merge branch 'develop'
Plantillas para donativos e INE
2017-11-26 00:21:21 -06:00
Mauricio Baeza e2008525f1 Complemento INE en PDF 2017-11-26 00:15:14 -06:00
Mauricio Baeza 8c9f0092dc Fix - fecha DOF en PDF de donativo 2017-11-25 20:32:36 -06:00
Mauricio Baeza 8d878e3731 PDF de donativo 2017-11-25 20:26:15 -06:00
Mauricio Baeza 010ea37542 Fix - Scroll en emisor 2017-11-25 11:39:01 -06:00
Mauricio Baeza dc1daf2c31 Agregar plantilla donataria 2017-11-25 00:17:46 -06:00
Mauricio Baeza a50ad106d2 Merge branch 'develop'
Fix - bancos y plantillas
2017-11-24 11:51:57 -06:00
Mauricio Baeza 26dc5d3372 Fix - subir plantillas 2017-11-24 10:47:15 -06:00
Mauricio Baeza 96b4244a51 Fix - agregar cuenta de banco 2017-11-24 10:41:28 -06:00
Mauricio Baeza 096c90c26e Merge branch 'develop'
Agregar depósitos
2017-11-23 23:58:14 -06:00
Mauricio Baeza 9cb6dbf0b2 Agregar depósitos 2017-11-23 23:56:03 -06:00
Mauricio Baeza 2e9dadf301 Validar agregar deposito 2017-11-22 19:44:26 -06:00
Mauricio Baeza 0a575afaa9 Obtener solo por pagar 2017-11-22 15:42:59 -06:00
Mauricio Baeza b59f62e4f1 Fix - Uso del CFDI en clientes 2017-11-22 15:28:31 -06:00
Mauricio Baeza 2d749d1564 Fix - UI para depositos 2017-11-22 01:03:42 -06:00
Mauricio Baeza 61ff2ce29a UI para depositos 2017-11-22 00:46:23 -06:00
Mauricio Baeza 2083a19db2 Agregar retiros de banco 2017-11-21 00:48:51 -06:00
Mauricio Baeza 74ac8b9295 Soporte complemento ine 2017-11-20 00:47:23 -06:00
Mauricio Baeza b8cb214b79 Soporte complemento donatarias 2017-11-19 21:59:14 -06:00
Mauricio Baeza b70fac7872 Fix - impuestos locales 2017-11-19 21:26:54 -06:00
Mauricio Baeza 964d53dbae Agregar xslt para donatarias 2017-11-19 14:57:49 -06:00
Mauricio Baeza 364ac55b8b Configurar anticipo y donativo 2017-11-19 14:34:54 -06:00
Mauricio Baeza 9b430b882b Impuestos locales 2017-11-19 00:42:16 -06:00
Mauricio Baeza 5cb7ba2c95 Merge branch 'develop'
Agregar cuentas de banco
2017-11-17 22:39:13 -06:00
Mauricio Baeza ff21b3c8bc Fix - Al borrar impuestos 2017-11-17 21:45:47 -06:00
Mauricio Baeza eb89b8e8fa Fix - Factura con retenciones 2017-11-17 21:23:49 -06:00
Mauricio Baeza bf84b9a49b Fix - Mostrar activo en impuestos 2017-11-17 21:16:01 -06:00
Mauricio Baeza 7cc248da17 Fix - Agregar retencion de impuesto 2017-11-17 21:12:23 -06:00
Mauricio Baeza 7630059361 Validar si existe la factura al importar 2017-11-17 16:28:11 -06:00
Mauricio Baeza b8ab26174d Validar unidad al importar productos 2017-11-17 14:56:39 -06:00
Mauricio Baeza 6954b74677 Crear primer movimiento 2017-11-17 14:13:39 -06:00
Mauricio Baeza 3cf46e69ae Mostrar estado de cuenta 2017-11-17 00:38:06 -06:00
Mauricio Baeza c8f7554ff6 Cuentas Banco UI 2017-11-16 22:49:17 -06:00
Mauricio Baeza 960ba21467 Paginar clientes 2017-11-16 01:17:22 -06:00
Mauricio Baeza 1979597b20 Agregar scroll a formularios 2017-11-15 19:33:41 -06:00
Mauricio Baeza 4e6c5ffa2c Generar archivo para productos 2017-11-15 19:29:51 -06:00
Mauricio Baeza a91157659c Merge branch 'develop'
Agregar y borrar impuestos
2017-11-15 00:13:14 -06:00
Mauricio Baeza cd18ec5079 Agregar y borrar impuestos 2017-11-15 00:12:55 -06:00
Mauricio Baeza 7cf5e86391 Buscar claves SAT 2017-11-14 20:38:20 -06:00
Mauricio Baeza a6cbe97f40 Buscar y agregar nuevas unidades 2017-11-14 20:04:01 -06:00
Mauricio Baeza 26913d260f Merge branch 'develop'
Descuentos
2017-11-13 00:36:28 -06:00
Mauricio Baeza 7da9289082 Descuentos en prefacturas 2017-11-12 23:49:53 -06:00
Mauricio Baeza 28fcb803ab Agregar descuentos 2017-11-12 23:27:40 -06:00
Mauricio Baeza cb616a79c5 Fix - Seleccionar series 2017-11-12 22:06:06 -06:00
Mauricio Baeza 65e8c89a85 Fix - Uso del CFDI 2017-11-12 21:56:45 -06:00
Mauricio Baeza ff5f98f187 Validar y filtrar CFDI de anticipo 2017-11-12 18:50:41 -06:00
Mauricio Baeza 582e318dae Administrar unidades en configuración 2017-11-12 17:49:06 -06:00
Mauricio Baeza a290a034f8 Agregar cuentas de banco del emisor 2017-11-11 23:58:11 -06:00
Mauricio Baeza ea298c52d5 Ajustar margen en tabla bancos 2017-11-11 20:23:06 -06:00
Mauricio Baeza 423937f1ca Agregar SAT Bancos 2017-11-11 20:21:00 -06:00
Mauricio Baeza 4a0089ae76 Merge branch 'develop'
Generar PDF 3.2 y 3.3.
Mostrar CFDI relacionados en PDF
2017-11-11 15:07:07 -06:00
Mauricio Baeza a5f8b62929 Mostrar CFDI relacionados en PDF 2017-11-11 15:03:20 -06:00
Mauricio Baeza a9dcc07427 Fix - Generar XML sin relacionados 2017-11-11 14:27:36 -06:00
Mauricio Baeza 2e06083e29 Generar PDF de CFDI 3.2 2017-11-11 13:42:51 -06:00
Mauricio Baeza 6f30d42d40 Cargar plantillas ODS 2017-11-11 11:40:55 -06:00
Mauricio Baeza f1dff39e1b Validar configuracion mínima para facturar 2017-11-11 11:17:49 -06:00
Mauricio Baeza b997a4703c Generar PDF 3.2 2017-11-09 23:51:54 -06:00
Mauricio Baeza 1a6d0389a2 Merge branch 'develop'
Importar facturas de Factura Libre
2017-11-09 23:22:56 -06:00
Mauricio Baeza 0810ead75f Impotar facturas de Factura Libre 2017-11-09 23:22:36 -06:00
Mauricio Baeza 1490fa5147 Merge branch 'develop'
XML con CFDI relacionados
2017-11-08 23:47:38 -06:00
Mauricio Baeza d61e0e8d8e XML con CFDI relacionados 2017-11-08 23:47:15 -06:00
Mauricio Baeza 7bfa2552eb UI - CFDI relacionados 2017-11-06 23:34:43 -06:00
Mauricio Baeza 9b74a12871 Merge branch 'develop'
Administrar monedas en configuración
2017-11-06 23:00:06 -06:00
Mauricio Baeza 270072b029 Administrar monedas en admin 2017-11-06 22:59:39 -06:00
Mauricio Baeza c8005aaad8 Mostrar cantidad de facturas 2017-11-06 22:36:00 -06:00
Mauricio Baeza 62e0320a55 Merge branch 'develop'
Envio de correo automático
2017-11-06 22:21:36 -06:00
Mauricio Baeza 6476a3997f Envio de correo automático 2017-11-06 22:21:14 -06:00
Mauricio Baeza b56045f97f Merge branch 'develop'
Fix - Timbrar productos exentos
2017-11-06 20:30:30 -06:00
Mauricio Baeza c86e47d3ec Fix - Timbrar productos exentos 2017-11-06 20:30:04 -06:00
Mauricio Baeza fc9a7adb26 Merge branch 'develop'
Fix - Condición de pago en PDF
2017-11-06 19:56:17 -06:00
Mauricio Baeza 367e455ddd Fix - Condición de pago en PDF 2017-11-06 19:55:58 -06:00
Mauricio Baeza 9fc01c54e1 Merge branch 'develop'
Facturar prefacturas
2017-11-05 21:23:55 -06:00
Mauricio Baeza 7a63530ef6 Facturar prefacturas 2017-11-05 21:23:37 -06:00
Mauricio Baeza 6fee4d4174 Merge branch 'develop'
Generar y enviar prefacturas
2017-11-05 20:12:45 -06:00
Mauricio Baeza ceb11a367a Enviar prefactura por correo 2017-11-05 20:10:34 -06:00
Mauricio Baeza bd9f946556 Generar pdf de prefactura 2017-11-05 19:53:27 -06:00
Mauricio Baeza e795494ec3 Generar prefactura 2017-11-05 00:13:48 -06:00
Mauricio Baeza ff2125670e Merge branch 'develop'
Administrar impuestos
2017-11-03 22:23:38 -06:00
Mauricio Baeza 1227e58956 Administrar impuestos 2017-11-03 22:23:15 -06:00
Mauricio Baeza a05b3d56a8 Merge branch 'develop'
Cargar plantilla y logotipo
2017-11-03 20:10:18 -06:00
Mauricio Baeza 4e323bd135 Cargar plantilla 3.3 2017-11-03 20:05:19 -06:00
Mauricio Baeza 3d7fb900bf Cargar logotipo del emisor 2017-11-03 14:09:34 -06:00
Mauricio Baeza e8342de740 Merge branch 'develop'
Fix - Guardar condición de pago en socios
2017-11-02 23:25:03 -06:00
Mauricio Baeza dca5a54668 Fix - Guardar condición de pago en socios 2017-11-02 23:24:31 -06:00
Mauricio Baeza c54c84940f Merge branch 'develop'
Fix - al guardar datos de emisor
2017-11-02 17:13:01 -06:00
Mauricio Baeza 727de2c32f Actualizar version 0.2.1 2017-11-02 17:12:44 -06:00
Mauricio Baeza f9b7f6a077 Fix - al guardar datos de emisor 2017-11-02 17:11:49 -06:00
Mauricio Baeza 4e693312e5 Fix - al obtener el RFC del certificado en Centos7 2017-11-01 14:51:12 -06:00
Mauricio Baeza 81e6a35c66 Merge branch 'develop'
Fix - clave siguiente en productos
2017-11-01 10:42:51 -06:00
Mauricio Baeza 6184a8c892 Fix - clave siguiente en productos 2017-11-01 10:40:28 -06:00
Mauricio Baeza e690aa3ce9 Cursor en buscar cliente al facturar 2017-11-01 10:36:27 -06:00
Mauricio Baeza cd87914a28 Merge branch 'develop'
Fix - Varios productos
2017-10-31 20:09:01 -06:00
Mauricio Baeza 295f1c80c5 Fix - PDF con varios productos 2017-10-31 20:08:18 -06:00
Mauricio Baeza c1f08305c9 Fix - Agregar varios productos 2017-10-31 20:01:03 -06:00
Mauricio Baeza ec44e5cf73 Merge branch 'develop'
Fix - Descripción de producto y folio siguiente en facturas
2017-10-31 15:00:39 -06:00
Mauricio Baeza a948f15453 Fix - Descripción de producto y folio siguiente en facturas 2017-10-31 10:34:34 -06:00
Mauricio Baeza deeadf898a Fix - al importar la configuracion del PAC 2017-10-31 00:13:27 -06:00
Mauricio Baeza 3486272018 Merge branch 'develop'
Fix - Quitar TZ en fechas del certificado
2017-10-30 23:05:06 -06:00
Mauricio Baeza f429eb9c1e Fix - Quitar TZ en fechas del certificado 2017-10-30 23:04:40 -06:00
Mauricio Baeza ac586115a0 Merge branch 'develop'
Fix - Obtener RFC del certificado
2017-10-30 21:58:49 -06:00
Mauricio Baeza a8930f17ed Fix - Obtener RFC del certificado 2017-10-30 21:56:53 -06:00
Mauricio Baeza ae22b47bf4 Merge branch 'develop'
Separar configuracion de uwsgi
2017-10-30 19:59:11 -06:00
Mauricio Baeza 570f81f3eb Separar configuracion de uwsgi 2017-10-30 19:58:31 -06:00
Mauricio Baeza 3901372595 Merge branch 'develop'
Separar configuracion de PACs
2017-10-30 19:47:56 -06:00
Mauricio Baeza 68a4ac46e8 Separar configuracion de PACs 2017-10-30 19:47:26 -06:00
Mauricio Baeza 60528d8be1 Merge branch 'develop'
Facturar y cancelar
2017-10-30 19:02:19 -06:00
Mauricio Baeza 00ff919859 Agregar notas para uwsgi 2017-10-30 19:01:49 -06:00
Mauricio Baeza 1b9e6e1638 Fix - Verificar certificado de usuario en producción 2017-10-30 18:56:50 -06:00
Mauricio Baeza 5b4183133e Fix - Certificado al inicar valores 2017-10-30 15:11:10 -06:00
Mauricio Baeza 9eb5b63dc8 Fix - Guardar certificados 2017-10-30 13:57:02 -06:00
Mauricio Baeza 4f26f820cc UI - Prefacturas 2017-10-30 00:03:02 -06:00
Mauricio Baeza 8a06e3f4bb Fix - Importes grandes en campos Decimales 2017-10-29 22:50:47 -06:00
Mauricio Baeza 9e50033b9f Cancelar facturas con xml firmado 2017-10-29 16:53:10 -06:00
Mauricio Baeza f958227f56 Obtener estatus SAT 2017-10-28 23:37:08 -05:00
Mauricio Baeza 63c7b4e458 Filtro por rango de fechas en facturas 2017-10-28 22:21:39 -05:00
Mauricio Baeza 2f62a2debe Filtro por año y mes en facturas 2017-10-28 21:58:18 -05:00
Mauricio Baeza 14ccf4fe1f Filtro por fechas en UI 2017-10-28 00:30:42 -05:00
Mauricio Baeza 82edc2a75a Validado el timbrado con postgres 2017-10-27 10:27:21 -05:00
Mauricio Baeza 777440fd58 Merge branch 'develop'
Crear tablas con postgres
2017-10-26 23:56:45 -05:00
Mauricio Baeza 6f2cd1facd Crear tablas con postgres 2017-10-26 23:55:52 -05:00
128 changed files with 62780 additions and 49591 deletions

22
.gitignore vendored
View File

@ -2,23 +2,37 @@
__pycache__/ __pycache__/
*.py[cod] *.py[cod]
*$py.class *$py.class
Pipfile*
# Django stuff: # Django stuff:
*.log *.log
local_settings.py local_settings.py
conf.py conf.py
main.ini
source/fixtures source/fixtures
source/media source/media
# Sphinx documentation # Sphinx documentation
docs/
*.ods
*.xlsx *.xlsx
bk/
source/docs/
site/
vedev/
# Virtualenv
.env/
virtual/
env
docs/build
cache/
credenciales.conf credenciales.conf
*.sqlite *.sqlite
*.sql *.sql
*.service
*.orig
rfc.db rfc.db
configpac.py Dockerfile
chuletas/

View File

@ -1,3 +1,829 @@
v 2.3.2 [10-Abr-2024]
- Fix: En las mercancias en la Carta Porte al generar el PDF.
v 2.3.1 [02-Abr-2024]
- Fix: En la cantidad de la mercancia en la Carta Porte.
v 2.3.0 [01-Abr-2024]
- Mejora: Soporte para complemento Carta Porte 3.0
- **IMPORTANTE**: Aunque no lo uses, esto afecta al JS de facturación, por
lo que tienes que forzar el refresco (CTRL+F5) si tienes algún problema.
v 2.2.0 [24-Ene-2024]
- Mejora: Soporte para complemento Comercio Exterior 2.0
- **IMPORTANTE**: Aunque no lo uses, esto afecta al JS de facturación, por
lo que tienes que forzar el refresco (CTRL+F5) si tienes algún problema.
v 2.1.0 [26-Dic-2023]
- Mejora: Se agrega filtro por día en facturas.
v 2.0.9 [20-Dic-2023]
- Fix: Issue 98 y 107
v 2.0.8 [30-Oct-2023]
---------------------
- Fix: Permitir generar CFDI de egreso para facturas globales sin datos globales.
- Fix: Permitir cambiar la zona horaria para cuando se usa en servidor.
Es necesario hacer una migración:
```
cd /opt/empresa-libre
git pull origin master
cd source/app/models
python main.py -bk
python main.py -m -r RFC
```
v 2.0.7 [06-Ju1-2023]
---------------------
- Fix: En tasa de retención de un Resico a una Persona Moral.
v 2.0.6 [14-Jun-2023]
---------------------
- Fix: Al generar complementos de pago con facturas con retención de impuestos.
v 2.0.5 [05-Jun-2023]
---------------------
- Fix: Al generar complementos de pago con facturas con varios impuestos.
- Fix: ticket #103, hay que usar {receptor.correo_facturas} y {receptor.telefonos}
v 2.0.2 [01-Abr-2023]
---------------------
- Fix: Obtener la clave del sat al facturar por lote.
v 2.0.1 [29-Mar-2023]
---------------------
- Fix ticket #97
Es necesario hacer una migración:
```
cd /opt/empresa-libre
git pull origin master
cd source/app/models
python main.py -bk
python main.py -m -r RFC
```
v 2.0.0 [08-Ene-2023]
----------------------
- Liberamos para todos la versión CFDI 4.0
- **IMPORTANTE** NO intentes timbrar si **antes** no has validado en nuestro demo que puedes timbrar tus CFDIs habituales.
* IMPORTANTE:
Es necesario hacer una migración:
```
cd /opt/empresa-libre
git pull origin master
cd source/app/models
python main.py -bk
python main.py -m -r RFC
```
Y reiniciar todo. IMPORTANTE: dependiendo desde que versión actualices, tal vez debas de hacer algún prodecimiento extra. Siempre revisa este historial.
v 2.0.0 [31-Mar-2022]
----------------------
- Primera versión de timbrado con CFDI4
- **IMPORTANTE** NO intentes timbrar si **antes** no has validado en nuestro demo que puedes timbrar tus CFDIs habituales.
v 1.47.0 [28-Mar-2022]
----------------------
- Mejora: Soporte basico para complemento Comercio Exterior.
v 1.46.5 [10-Mar-2022]
----------------------
- Error: Al timbrar nómina con Comercio Digital
v 1.46.4 [18-Feb-2022]
----------------------
- Error: Issue #54
v 1.46.3 [15-Feb-2022]
----------------------
- Error: Issue #53
v 1.46.2 [31-Ene-2022]
----------------------
- Error: Al generar Carta Porte sin remolque.
- Error: Al cancelar con Finkok.
v 1.46.1 [29-Ene-2022]
----------------------
- Error: Issue #49
- Mejora: Agregar validación para distancia en origen de Carta Porte.
v 1.46.0 [27-Ene-2022]
----------------------
- Mejora: Issue #45
- Mejora: Agregar tipos de persimos SCT para Carta Porte
- Mejora: Buscar estado y municipio por CP en Carta Porte
v 1.45.4 [25-Ene-2022]
----------------------
- Error: Al timbrar carta porte.
- Error: Al cancelar con Comercio Digital.
- Error: Issue #42
- Mejora: Issue #44
* IMPORTANTE: Es necesario subir de nuevo tus certificados de sello, **solo** si timbras con Comercio Digital.
v 1.45.3 [23-Ene-2022]
----------------------
- Error: El enviar por correo CFDI de pago. Ticket #40
v 1.45.2 [21-Ene-2022]
----------------------
- Error: Al cancelar un CFDI
v 1.45.1 [20-Ene-2022]
----------------------
- Error: Al enviar correos con la nueva configuración
* IMPORTANTE: Revisa tu configuración de correo para verificar que todo funcione.
v 1.45.0 [20-Ene-2022]
----------------------
- Importar Carta Porte desde archivo JSON
v 1.44.2 [19-Ene-2022]
----------------------
- Agregar opción STARTTLS que requieren algunos servidores de correo
* IMPORTANTE: Revisa tu configuración de correo para verificar si tienes que usar esta opción.
v 1.44.1 [19-Ene-2022]
----------------------
- Correciones en generación de Carta Porte v2.0
- Plantilla para representación impresa de Carta Porte v2.0
v 1.44.0 [10-Ene-2022]
----------------------
- Soporte para Carta Porte v2 con CFDI 3.3
- Soporte para el nuevo esquema de cancelación del SAT
* IMPORTANTE:
Es necesario hacer una migración:
```
cd /opt/empresa-libre
git pull origin master
cd source/app/models
python main.py -bk
python main.py -m -r RFC
```
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.
v 1.42.1 [01-Jun-2021]
----------------------
- Error - Ticket #5
v 1.42.0 [24-May-2021]
----------------------
- Opción para que solo un admin pueda cancelar.
v 1.41.4 [13-Abr-2021]
----------------------
- Error - Ticket #4
v 1.41.3 [12-Feb-2021]
----------------------
- Error - Ticket #3
v 1.41.2 [12-Feb-2021]
----------------------
- Error - Ticket #2
v 1.41.1 [11-Feb-2021]
----------------------
- Error - Ticket #1
v 1.41.0 [10-Feb-2021]
----------------------
- Importar clientes desde archivo CSV
v 1.40.1 [09-Feb-2021]
----------------------
- Fix #422
v 1.40.0 [05-ene-2021]
----------------------
- Error: Al parsear XML en Python 3.9+
- Mejora: Agregar versión de Empresa Libre a plantilla.
- Mejora: Sellado en memoria
- Mejora: Se agrega un segundo PAC y se refactoriza el timbrado.
* **IMPORTANTE**
Es necesario seguir una serie de pasos **obligatorios** para migrar a esta
versión, **no continues hasta seguir paso a paso** estas instrucciones.
**Antes** de comenzar ten a la mano tus certificados de sello para timbrar, es
necesario subirlos de nuevo. **NO actualices si no tienes tus certificados**
con su respectiva contraseña, te quedarás sin poder timbrar.
Estas instrucciones solamente aplican para la maquina virtual con Ubuntu Server,
la ultima y única versión sobre la que se dará soporte.
1. Entra a la parte administrativa y toma de tus credenciales de timbrado en el
menú "Emisor" ficha "Otros Datos", usuario y token de timbrado.
1. Agregar nuevos requerimientos `sudo apt install pkgconf libxml2-dev libxmlsec1-dev libxmlsec1-openssl`
1. Agregar nuevo requerimiento `pip install --user xmlsec`
1. Entrar a la carpeta del sistema: `/opt/empresa-libre`
1. Actualizar `git pull origin master`
1. Entrar a `source/app/controllers/pacs/comerciodigital` y copiar `conf.py.example` a `conf.py`
1. Entrar a `source/app/controllers/pacs/finkok` y copiar `conf.py.example` a `conf.py`
1. Agregar la variable `TOKEN = ''` al archivo `source/app/conf.py` mira el archivo de ejemplo en el mismo directorio.
1. Reiniciar el servicio: `sudo systemctl restart empresalibre`
1. Si no ves los cambios descritos a continuación, fuerza el refresco de tu navegador, generalmente con **CTRL+F5**
1. Sube de nuevo tus certificados en el menú "Emisor" ficha "Certificado".
1. Ve al menú "Opciones", ficha "Otros".
1. Selecciona tu PAC, si tu usuario es un correo electrónico, invariablemente
debes seleccionar Finkok. Si tienes duda ponte en contacto con nosotros.
1. Establece las credenciales del punto 1.
1. Guarda los datos.
v 1.39.1 [17-sep-2020]
----------------------
- Error: Esquema para complemento IEDU
v 1.39.0 [25-ago-2020]
----------------------
- Mejora: Consulta de las facturas de pago en el SAT
- Mejora: Mostrar totales en nómina
- Mejora: Mostrar totales por cantidad al facturar
- Mejora: Validar líneas con importe cero antes de facturar
- Error: Validaciones de namespace en CFDI de nómina por parte del PAC
v 1.38.1 [30-mar-2020]
----------------------
- Error: En nómina al timbrar asimilados
v 1.38.0 [08-mar-2020]
----------------------
- Mejora: Factura global por ticket o nota
- Error: Al generar algunos PDFs
v 1.37.0 [02-mar-2020]
----------------------
- Mejora: Soporte para complemento Leyendas Fiscales
* IMPORTANTE:
Es necesario hacer una migración, y agregar los campos necesarios para mostrar
las leyendas, mira la carpeta pública con la plantilla de ejemplo.
```
git pull origin master
cd source/app/models
python main.py -bk
python main.py -m -r RFC
```
v 1.36.0 [25-feb-2020]
----------------------
- Mejora: Permitir ver a usuarios todos los documentos
v 1.35.0 [24-feb-2020]
----------------------
- Mejora: Filtrado de documentos por sucursal
v 1.34.1 [09-feb-2020]
----------------------
- Error: Al timbrar nómina de asimilados
v 1.34.0 [29-ene-2020]
----------------------
- Error: Al timbrar nómina
v 1.33.2 [27-ene-2020]
----------------------
- Se actualizan métodos de pago en catálogos del SAT
* IMPORTANTE:
Es necesario actualizar los catálogos del SAT
```
git pull origin master
cd source/app/models
python main.py -bk
python main.py -us
```
v 1.33.0 [22-ene-2020]
----------------------
- Mejora: Cambio del mensaje para cuando se intenta dar de alta un cliente ya existente.
- Mejora: Solo los admins pueden ver la nómina.
- Se agrega un segundo PAC
- Se actualizan los catálogos del SAT
* IMPORTANTE:
Es necesario actualizar los catálogos del SAT
```
git pull origin master
cd source/app/models
python main.py -bk
python main.py -us
```
v 1.32.0 [05-ene-2020]
----------------------
- Mejora: Recuperar facturas no aceptadas para cancelación por el receptor
v 1.31.2 [28-oct-2019]
----------------------
- Error: Al generar PDF con tags en las series
v 1.31.1 [28-ago-2019]
----------------------
- Error: Al agregar nuevo impuesto #369
v 1.31.0 [23-abr-2019]
----------------------
- Error: Validar cantidad o valor unitario cero en tickets
- Mejora: Envío de nómina por correo al empleado
v 1.30.0 [24-mar-2019]
----------------------
- Mejora: Se actualizan los catálogos de Nómina
* IMPORTANTE:
Es necesario realizar una migración, después de actualizar.
```
git pull origin master
cd source/app/models
python main.py -bk
python main.py -m
```
v 1.29.0 [08-mar-2019]
----------------------
- Error: Al mostrar detalle en facturas importadas #343
- Mejora: Editar la descripción de un movimiento bancario.
v 1.28.2 [04-mar-2019]
----------------------
- Error: Al enviar facturas de pago
v 1.28.1 [04-mar-2019]
----------------------
- Error: Al buscar facturas por fechas
v 1.28.0 [17-feb-2019]
----------------------
- Mejora: Manejo de empaques para mensajeria
- Mejora: Usar concepto personalizado en deducciones de nómina 004 Otros
- Mejora: Búsqueda en notas
- Mejora: Soporte para el complemento de Divisas
- Mejora: Descarga de nómina en lote
* IMPORTANTE:
Es necesario realizar una migración, después de actualizar.
```
git pull origin master
cd source/app/models
python main.py -bk
python main.py -m
```
Es necesario agregar un nuevo requerimiento.
```
sudo pip install cryptography
```
**IMPORTANTE** Si envias tus facturas por correo directamente, es necesario
volver a capturar la contraseña de tu servidor de correo y guardar los datos
nuevamente.
v 1.27.1 [23-ene-2019]
----------------------
- Error: Al cancelar nómina
v 1.27.0 [17-ene-2019]
----------------------
- Mejora: Permitir capturar folio manualmente
v 1.26.1 [16-ene-2019]
----------------------
- Error: Guardar logos de emisor
v 1.26.0 [15-ene-2019]
----------------------
- Mejora: Generar PDF desde plantilla JSON
v 1.25.0 [07-nov-2018]
----------------------
- Fix: Al importar días de pago en nómina
- Fix: Al importar descripción en factura en lote
v 1.24.0 [06-nov-2018]
----------------------
- Fix: Consulta estatus SAT
v 1.23.0 [30-oct-2018]
----------------------
- Mejora: Permitir importar CFDI 3.2
v 1.22.0 [25-oct-2018]
----------------------
- Mejora: Permitir cambiar descripción al facturar en lote
v 1.21.2 [23-oct-2018]
----------------------
- Error unicode
v 1.21.1 [14-oct-2018]
----------------------
- Error #307
v 1.21.0 [12-oct-2018]
----------------------
- Error #287
- Mejora: Complemento de pago con datos de cuentas
* IMPORTANTE: Es necesario realizar una migración, despues de actualizar.
v 1.20.0 [08-oct-2018]
----------------------
- Error #295
- Mejora: Cuentas de banco para clientes
* IMPORTANTE: Es necesario realizar una migración, despues de actualizar.
v 1.19.1 [03-oct-2018]
----------------------
- Error #291
- Error al generar PDF de factura de pago con relacionados sin serie
- Error al relacionar facturas versión 3.2
v 1.19.0 [28-sep-2018]
----------------------
- Mejora #280
- Mejora #288
- Error #290
* IMPORTANTE: Es necesario realizar una migración, despues de actualizar la rama principal.
```
git pull origin master
cd source/app/models
python main.py -bk
python main.py -m
```
v 1.18.0 [27-sep-2018]
----------------------
- Fix #282 Factura de pago en otras monedas
v 1.17.0 [25-sep-2018]
----------------------
- Fix - Al generar factura de pago con documentos relacionados en otras monedas
- Fix - Al generar factura de pago sin serie en documentos relacionados
- Fix #278
* IMPORTANTE: Es necesario realizar una migración, despues de actualizar la rama principal.
```
git pull origin master
cd source/app/models
python main.py -bk
python main.py -m
```
v 1.16.1 [18-sep-2018]
----------------------
- Error https://gitlab.com/mauriciobaeza/empresa-libre/issues/268
- IMPORTANTE: Actualizar si usas cuatro decimales en impuestos
v 1.16.0 [16-sep-2018]
----------------------
- Se puede editar el saldo de un cliente
- Se muestra la cantidad de facturas de pago en los movimientos
v 1.15.0 [12-sep-2018]
----------------------
- Se pueden mover las facturas con doble clic en los movimientos de banco.
- Fix - Al sumar las facturas en los depósitos
- Fix - Al importar los pedimentos en facturas por lotes
- Fix - Al guardar los datos del emisor
v 1.14.0 [10-sep-2018]
----------------------
- Personalizar plantilla para factura de pago
- Fix - Mostrar serie y folio capturado para factura de pago
- Fix - Agregar nueva cuenta de banco
v 1.13.0 [10-sep-2018]
----------------------
- Cancelar factura de pago
* IMPORTANTE: Es necesario realizar una migración, despues de actualizar la rama principal.
```
git pull origin master
cd source/app/models
python main.py -bk
python main.py -m
```
v 1.12.0 [31-ago-2018]
----------------------
- Soporte para facturas (complemento) de pago.
* IMPORTANTE: Es necesario realizar una migración, despues de actualizar la rama principal.
```
git pull origin master
cd source/app/models
python main.py -bk
python main.py -m
```
v 1.11.1 [21-ago-2018]
----------------------
- Fix - Quitar columna en tabla facturaspagos.
* IMPORTANTE: Es necesario realizar una migración, despues de actualizar la rama principal.
```
git pull origin master
cd source/app/models
python main.py -m
```
v 1.11.0 [25-jul-2018]
----------------------
- Se cambia la forma de consultar los folios restantes. Es indispensable actualizar a esta versión para ver tus timbres restantes.
v 1.10.0 [10-jul-2018]
----------------------
- Ahora se pueden manejar precios con cuatro decimales.
v 1.9.3 [08-jul-2018]
---------------------
- Fix: Al refacturar conceptos con descuento
v 1.9.2 [05-jul-2018]
---------------------
- Fix: Al generar el reporte de facturas en PDF
v 1.9.1 [25-jun-2018]
---------------------
- Fix: Al mostrar el título de la aplicación
- Se agrega el registro de acción al borrar una factura
v 1.9.0 [18-jun-2018]
---------------------
- Se agrega la vista del detalle de facturas
- Fix: Al timbrar nómina
v 1.8.1 [14-jun-2018]
---------------------
- Fix: Se agrega una barra de desplazamiento al buscar productos o clientes
- Se cambia el servidor de consulta de timbres
v 1.8.0 [03-jun-2018]
---------------------
- Se permiten 4 decimales en Tipo de cambio
- Se agrega el campo {total_cantidades} al generar el PDF
- Se agrega opción para generar respaldos de la BD en MV
- Fix: Al generar con complemento EDU
v 1.7.0 [23-may-2018]
---------------------
- Se agrega soporte para truncar impuestos locales, para las estulticias de los "ingenieros" de las dependencias de gobierno
v 1.6.1 [09-abr-2018]
---------------------
- Fix: Nómina con separación
v 1.6.0 [18-feb-2018]
---------------------
- Facturacion a extranjeros
* IMPORTANTE: Es necesario realizar una migración, despues de actualizar la rama principal.
```
git pull origin master
cd source/app/models
python main.py -m
```
v 1.5.0 [30-ene-2018]
---------------------
- Timbrado de Nómina
* IMPORTANTE: Es necesario realizar una migración, despues de actualizar la rama principal.
```
git pull origin master
cd source/app/models
python main.py -m
```
v 1.4.0 [01-ene-2018]
---------------------
- Impresión de tickets
v 1.3.0 [27-Dic-2017]
---------------------
- Punto de venta
* IMPORTANTE: Es necesario realizar una migración, despues de actualizar la rama principal.
```
git pull origin master
cd source/app/models
python main.py -m
```
v 1.2.0 [18-Dic-2017]
---------------------
* IMPORTANTE: Es necesario actualizar la base de datos, despues de actualizar la rama principal.
```
git pull origin master
cd source/app/models
python main.py -bd
```
v 0.1.0 [26-Oct-2017] v 0.1.0 [26-Oct-2017]
--------------------- ---------------------
- Generar y timbrar con CFDI 3.3 - Generar y timbrar con CFDI 3.3

View File

@ -13,9 +13,3 @@
* Propon nuevas funcionalidades * Propon nuevas funcionalidades
* Difunde Empresa Libre * Difunde Empresa Libre
### Prioridades
1. Darle continuidad a la facturación de los clientes
2. Cubrir todas las funcionalidades que cubre ahora Factura Libre
3. Agregar nuevas funcionalidades

View File

@ -8,21 +8,53 @@
Este proyecto está en continuo desarrollo, contratar un esquema de soporte, Este proyecto está en continuo desarrollo, contratar un esquema de soporte,
nos ayuda a continuar su desarrollo. Ponte en contacto con nosotros para nos ayuda a continuar su desarrollo. Ponte en contacto con nosotros para
contratar. contratar: administracion ARROBA empresalibre.net
#### Ahora también puede aportar con criptomonedas:
### Requerimientos: G1: `A5DdXxCKPw3QKWVdDVs7CzkNugNUW1sHu5zDJFWxCU2h`
BCH: `qztd3l00xle5tffdqvh2snvadkuau2ml0uqm4n875d`
## Requerimientos:
* Servidor web, recomendado Nginx * Servidor web, recomendado Nginx
* uwsgi * uwsgi
* python3 * python 3.8
* xsltproc
* 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 Debería de funcionar con cualquier combinación servidor-wsgi que soporte
aplicaciones Python. aplicaciones Python.
El sistema tiene soporte para tres bases de datos: SQLite, MySQL y PostgreSQL El sistema tiene soporte solo para PostgreSQL, debes de instalar el servidor de
(recomendado), debes de instalar el servidor de la base de datos y sus drivers la base de datos y su driver respectivo.
respectivos, excepto SQLite que es nativo en Python.
## Configuración para desarrollo local
* Crea un entorno virtual con python 3.8 y actívalo. Por ejemplo con virtualenv:
virtualenv .venv
source .venv/bin/activate
* Instala las dependencias
pip install -r requirements.txt
* Copia y ajusta algunos archivos necesarios.
- `source/app/conf.py`
- `source/app/controllers/pacs/comerciodigital/conf.py.example`
- `source/app/controllers/pacs/finkok/conf.py.example`
* Finalmente ejecutamos la aplicación. Para esto vamos a necesitar un servidor
wsgi como uwsgi:
pip install uwsgi
cd source/app
uwsgi main_debug.ini
* Ahora puedes ver la aplicación en http://localhost:8000

3
TODO.md Normal file
View File

@ -0,0 +1,3 @@
[ ] Permitir más de un remolque en la Carta Porte
[ ] Campos de captura de Comercio Exterior
[ ] Representación impresa de Comercio Exterior

1
VERSION Normal file
View File

@ -0,0 +1 @@
2.3.2

View File

@ -1,14 +1,17 @@
falcon falcon==1.4.1
falcon-multipart falcon-multipart
Beaker Beaker
Mako Mako
peewee peewee==2.10.2
Click
logbook logbook
bcrypt bcrypt
python-dateutil python-dateutil
zeep zeep
chardet chardet
pyqrcode
pypng
reportlab reportlab
psycopg2-binary
cryptography==3.4.8
xmlsec
segno
# python-escpos

View File

@ -1,10 +1,21 @@
#!/usr/bin/env python #!/usr/bin/env python
from peewee import SqliteDatabase
DEBUG = True DEBUG = False
ID_SUPPORT = '' MV = False
DATABASE = None # ~ Es la contraseña predeterminada de los usuarios admin y superadmin al crear
if DEBUG: # ~ una nueva base de datos, personaliza por la que quieras.
DATABASE = SqliteDatabase('empresalibre.sqlite') DEFAULT_PASSWORD = 'salgueiro3.3'
TITLE_APP = 'Empresa Libre'
#~ Establece una ruta accesible para el servidor web
LOG_PATH = '/var/log/empresalibre/empresa-libre.log'
# ~ Establece un token personalizado para encriptar las claves
# ~ from secrets import token_hex
# ~ token_hex(32)
# ~ IMPORTANTE: Cada vez que cambies este valor, debes de subir de nuevo
# ~ tus certificados
TOKEN = ''

View File

@ -1,25 +1,47 @@
#!/usr/bin/env python #!/usr/bin/env python3
# ~ Empresa Libre
# ~ Copyright (C) 2016-2018 Mauricio Baeza Servin (web@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
# ~ the Free Software Foundation, either version 3 of the License, or
# ~ (at your option) any later version.
# ~
# ~ This program is distributed in the hope that it will be useful,
# ~ but WITHOUT ANY WARRANTY; without even the implied warranty of
# ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# ~ GNU General Public License for more details.
# ~
# ~ 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 datetime import datetime
from xml.etree import ElementTree as ET from xml.etree import ElementTree as ET
from xml.dom.minidom import parseString from xml.dom.minidom import parseString
from dateutil import parser
from logbook import Logger from logbook import Logger
#~ from settings import DEBUG
log = Logger('XML') log = Logger('XML')
CFDI_ACTUAL = 'cfdi33' CFDI_ACTUAL = 'cfdi40'
NOMINA_ACTUAL = 'nomina12' NOMINA_ACTUAL = 'nomina12'
PUBLIC = 'PUBLICO EN GENERAL'
CFDI_EGRESO = 'E'
DEFAULT = {
'exportacion': '01',
}
SAT = { SAT = {
'xsi': 'http://www.w3.org/2001/XMLSchema-instance', 'xsi': 'http://www.w3.org/2001/XMLSchema-instance',
'cfdi32': { 'cfdi40': {
'version': '3.2', 'version': '4.0',
'prefix': 'cfdi', 'prefix': 'cfdi',
'xmlns': 'http://www.sat.gob.mx/cfd/3', 'xmlns': 'http://www.sat.gob.mx/cfd/4',
'schema': 'http://www.sat.gob.mx/cfd/3 http://www.sat.gob.mx/sitio_internet/cfd/3/cfdv32.xsd', 'schema': 'http://www.sat.gob.mx/cfd/4 http://www.sat.gob.mx/sitio_internet/cfd/4/cfdv40.xsd',
}, },
'cfdi33': { 'cfdi33': {
'version': '3.3', 'version': '3.3',
@ -27,18 +49,79 @@ SAT = {
'xmlns': 'http://www.sat.gob.mx/cfd/3', 'xmlns': 'http://www.sat.gob.mx/cfd/3',
'schema': 'http://www.sat.gob.mx/cfd/3 http://www.sat.gob.mx/sitio_internet/cfd/3/cfdv33.xsd', 'schema': 'http://www.sat.gob.mx/cfd/3 http://www.sat.gob.mx/sitio_internet/cfd/3/cfdv33.xsd',
}, },
'cfdi32': {
'version': '3.2',
'prefix': 'cfdi',
'xmlns': 'http://www.sat.gob.mx/cfd/3',
'schema': 'http://www.sat.gob.mx/cfd/3 http://www.sat.gob.mx/sitio_internet/cfd/3/cfdv32.xsd',
},
'nomina11': { 'nomina11': {
'version': '1.1', 'version': '1.1',
'prefix': 'nomina', 'prefix': 'nomina',
'xmlns': 'http://www.sat.gob.mx/nomina', 'xmlns': 'http://www.sat.gob.mx/nomina',
'schema': 'http://www.sat.gob.mx/nomina http://www.sat.gob.mx/sitio_internet/cfd/nomina/nomina11.xsd', 'schema': ' http://www.sat.gob.mx/nomina http://www.sat.gob.mx/sitio_internet/cfd/nomina/nomina11.xsd',
}, },
'nomina12': { 'nomina': {
'version': '1.2', 'version': '1.2',
'prefix': 'nomina', 'prefix': 'nomina12',
'xmlns': 'http://www.sat.gob.mx/nomina12', 'xmlns': 'http://www.sat.gob.mx/nomina12',
'schema': 'http://www.sat.gob.mx/nomina12 http://www.sat.gob.mx/sitio_internet/cfd/nomina/nomina12.xsd', 'schema': ' http://www.sat.gob.mx/nomina12 http://www.sat.gob.mx/sitio_internet/cfd/nomina/nomina12.xsd',
}, },
'locales': {
'version': '1.0',
'prefix': 'implocal',
'xmlns': 'http://www.sat.gob.mx/implocal',
'schema': ' http://www.sat.gob.mx/implocal http://www.sat.gob.mx/sitio_internet/cfd/implocal/implocal.xsd',
},
'donativo': {
'version': '1.1',
'prefix': 'donat',
'xmlns': 'http://www.sat.gob.mx/donat',
'schema': ' http://www.sat.gob.mx/donat http://www.sat.gob.mx/sitio_internet/cfd/donat/donat11.xsd',
'leyenda': 'Este comprobante ampara un donativo, el cual será destinado por la donataria a los fines propios de su objeto social. En el caso de que los bienes donados hayan sido deducidos previamente para los efectos del impuesto sobre la renta, este donativo no es deducible. La reproducción no autorizada de este comprobante constituye un delito en los términos de las disposiciones fiscales.',
},
'ine': {
'version': '1.1',
'prefix': 'ine',
'xmlns': 'http://www.sat.gob.mx/ine',
'schema': ' http://www.sat.gob.mx/ine http://www.sat.gob.mx/sitio_internet/cfd/ine/ine11.xsd',
},
'edu': {
'version': '1.0',
'prefix': 'iedu',
'xmlns': 'http://www.sat.gob.mx/iedu',
'schema': ' http://www.sat.gob.mx/iedu http://www.sat.gob.mx/sitio_internet/cfd/iedu/iedu.xsd',
},
'pagos': {
'version': '2.0',
'prefix': 'pago20',
'xmlns': 'http://www.sat.gob.mx/Pagos20',
'schema': ' http://www.sat.gob.mx/Pagos20 http://www.sat.gob.mx/sitio_internet/cfd/Pagos/Pagos20.xsd',
},
'divisas': {
'version': '1.0',
'prefix': 'divisas',
'xmlns': 'http://www.sat.gob.mx/divisas',
'schema': ' http://www.sat.gob.mx/divisas http://www.sat.gob.mx/sitio_internet/cfd/divisas/divisas.xsd',
},
'leyendas': {
'version': '1.0',
'prefix': 'leyendasFisc',
'xmlns': 'http://www.sat.gob.mx/leyendasFiscales',
'schema': ' http://www.sat.gob.mx/leyendasFiscales http://www.sat.gob.mx/sitio_internet/cfd/leyendasFiscales/leyendasFisc.xsd',
},
'cartaporte': {
'version': '3.0',
'prefix': 'cartaporte30',
'xmlns': 'http://www.sat.gob.mx/CartaPorte30',
'schema': ' http://www.sat.gob.mx/CartaPorte30 http://www.sat.gob.mx/sitio_internet/cfd/CartaPorte/CartaPorte30.xsd',
},
'comercioe': {
'version': '2.0',
'prefix': 'cce20',
'xmlns': 'http://www.sat.gob.mx/ComercioExterior20',
'schema': ' http://www.sat.gob.mx/ComercioExterior20 http://www.sat.gob.mx/sitio_internet/cfd/ComercioExterior20/ComercioExterior20.xsd',
}
} }
@ -49,6 +132,19 @@ class CFDI(object):
self._xsi = SAT['xsi'] self._xsi = SAT['xsi']
self._pre = self._sat_cfdi['prefix'] self._pre = self._sat_cfdi['prefix']
self._cfdi = None self._cfdi = None
self._complemento = None
self._impuestos_locales = False
self._donativo = False
self._ine = False
self._edu = False
self._pagos = False
self._is_nomina = False
self._leyendas = False
self._carta_porte = False
self._comercio_exterior = False
self._divisas = ''
self._tipo_de_comprobante = ''
self._exportacion = DEFAULT['exportacion']
self.error = '' self.error = ''
def _now(self): def _now(self):
@ -59,18 +155,27 @@ class CFDI(object):
return '' return ''
self._comprobante(datos['comprobante']) self._comprobante(datos['comprobante'])
if 'global' in datos:
self._informacion_global(datos['global'])
self._relacionados(datos['relacionados'])
self._emisor(datos['emisor']) self._emisor(datos['emisor'])
self._receptor(datos['receptor']) self._receptor(datos['receptor'])
self._conceptos(datos['conceptos']) self._conceptos(datos['conceptos'])
self._impuestos(datos['impuestos']) self._impuestos(datos['impuestos'])
self._locales(datos['impuestos'])
self._donatarias(datos['donativo'])
self._complementos(datos['complementos'])
if 'nomina' in datos: if 'nomina' in datos:
self._nomina(datos['nomina']) self._nomina(datos['nomina'])
if 'complementos' in datos:
self._complementos(datos['complementos'])
return self._to_pretty_xml(ET.tostring(self._cfdi, encoding='utf-8'))
def add_sello(self, sello): xml = self._to_pretty_xml(ET.tostring(self._cfdi, encoding='utf-8'))
return xml
def add_sello(self, sello, cert_txt):
self._cfdi.attrib['Sello'] = sello self._cfdi.attrib['Sello'] = sello
self._cfdi.attrib['Certificado'] = cert_txt
return self._to_pretty_xml(ET.tostring(self._cfdi, encoding='utf-8')) return self._to_pretty_xml(ET.tostring(self._cfdi, encoding='utf-8'))
def _to_pretty_xml(self, source): def _to_pretty_xml(self, source):
@ -79,65 +184,169 @@ class CFDI(object):
return xml return xml
def _validate(self, datos): def _validate(self, datos):
if datos['impuestos']:
if datos['impuestos']['total_locales_trasladados'] or \
datos['impuestos']['total_locales_retenciones']:
self._impuestos_locales = True
if datos['donativo']:
self._donativo = True
self._edu = datos.get('edu', False)
if datos['complementos']:
if 'ine' in datos['complementos']:
self._ine = True
self._pagos = bool(datos['complementos'].get('pagos', False))
self._leyendas = bool(datos['complementos'].get('leyendas', False))
self._carta_porte = bool(datos['complementos'].get('cartaporte', False))
self._comercio_exterior = bool(datos['complementos'].get('comercioe', False))
if self._comercio_exterior:
self._exportacion = datos['complementos']['comercioe'].pop('Exportacion')
self._divisas = datos['comprobante'].pop('divisas', '')
if 'nomina' in datos: if 'nomina' in datos:
self._is_nomina = True
return self._validate_nomina(datos) return self._validate_nomina(datos)
return True return True
def _validate_nomina(self, datos): def _validate_nomina(self, datos):
comprobante = datos['comprobante']
validators = (
('MetodoDePago', 'NA'),
('TipoCambio', '1'),
('Moneda', 'MXN'),
('TipoDeComprobante', 'egreso'),
)
for f, v in validators:
if f in comprobante:
if v != comprobante[f]:
msg = 'El atributo: {}, debe ser: {}'.format(f, v)
self.error = msg
return False
return True return True
def _comprobante(self, datos): def _comprobante(self, datos):
attributes = {} attributes = {}
attributes['xmlns:{}'.format(self._pre)] = self._sat_cfdi['xmlns'] attributes['xmlns:{}'.format(self._pre)] = self._sat_cfdi['xmlns']
attributes['xmlns:xsi'] = self._xsi attributes['xmlns:xsi'] = self._xsi
attributes['xsi:schemaLocation'] = self._sat_cfdi['schema']
attributes.update(datos)
#~ if DEBUG: schema_locales = ''
#~ attributes['Fecha'] = self._now() if self._impuestos_locales:
#~ attributes['NoCertificado'] = CERT_NUM name = 'xmlns:{}'.format(SAT['locales']['prefix'])
attributes[name] = SAT['locales']['xmlns']
schema_locales = SAT['locales']['schema']
schema_donativo = ''
if self._donativo:
name = 'xmlns:{}'.format(SAT['donativo']['prefix'])
attributes[name] = SAT['donativo']['xmlns']
schema_donativo = SAT['donativo']['schema']
schema_ine = ''
if self._ine:
name = 'xmlns:{}'.format(SAT['ine']['prefix'])
attributes[name] = SAT['ine']['xmlns']
schema_ine = SAT['ine']['schema']
schema_edu = ''
if self._edu:
name = 'xmlns:{}'.format(SAT['edu']['prefix'])
attributes[name] = SAT['edu']['xmlns']
schema_edu = SAT['edu']['schema']
schema_divisas = ''
if self._divisas:
name = 'xmlns:{}'.format(SAT['divisas']['prefix'])
attributes[name] = SAT['divisas']['xmlns']
schema_divisas = SAT['divisas']['schema']
schema_nomina = ''
if self._is_nomina:
name = 'xmlns:{}'.format(SAT['nomina']['prefix'])
attributes[name] = SAT['nomina']['xmlns']
schema_nomina = SAT['nomina']['schema']
schema_pagos = ''
if self._pagos:
name = 'xmlns:{}'.format(SAT['pagos']['prefix'])
attributes[name] = SAT['pagos']['xmlns']
schema_pagos = SAT['pagos']['schema']
schema_leyendas = ''
if self._leyendas:
name = 'xmlns:{}'.format(SAT['leyendas']['prefix'])
attributes[name] = SAT['leyendas']['xmlns']
schema_leyendas = SAT['leyendas']['schema']
schema_carta_porte = ''
if self._carta_porte:
name = 'xmlns:{}'.format(SAT['cartaporte']['prefix'])
attributes[name] = SAT['cartaporte']['xmlns']
schema_carta_porte = SAT['cartaporte']['schema']
schema_comercioe = ''
if self._comercio_exterior:
name = 'xmlns:{}'.format(SAT['comercioe']['prefix'])
attributes[name] = SAT['comercioe']['xmlns']
schema_carta_porte = SAT['comercioe']['schema']
attributes['xsi:schemaLocation'] = self._sat_cfdi['schema'] + \
schema_locales + schema_donativo + schema_ine + schema_edu + \
schema_divisas + schema_nomina + schema_pagos + schema_leyendas + \
schema_carta_porte + schema_comercioe
attributes.update(datos)
if not 'Version' in attributes: if not 'Version' in attributes:
attributes['Version'] = self._sat_cfdi['version'] attributes['Version'] = self._sat_cfdi['version']
if not 'Fecha' in attributes: if not 'Fecha' in attributes:
attributes['Fecha'] = self._now() attributes['Fecha'] = self._now()
# ~ cfdi4
if not 'Exportacion' in attributes:
attributes['Exportacion'] = self._exportacion
self._tipo_de_comprobante = attributes['TipoDeComprobante']
self._cfdi = ET.Element('{}:Comprobante'.format(self._pre), attributes) self._cfdi = ET.Element('{}:Comprobante'.format(self._pre), attributes)
return return
def _emisor(self, datos): def _informacion_global(self, datos):
#~ if DEBUG: if not datos:
#~ datos['Rfc'] = RFC_TEST return
node_name = '{}:InformacionGlobal'.format(self._pre)
node = ET.SubElement(self._cfdi, node_name, datos)
return
def _relacionados(self, datos):
if not datos or not datos['tipo'] or not datos['cfdis']:
return
node_name = '{}:CfdiRelacionados'.format(self._pre)
value = {'TipoRelacion': datos['tipo']}
node = ET.SubElement(self._cfdi, node_name, value)
for uuid in datos['cfdis']:
node_name = '{}:CfdiRelacionado'.format(self._pre)
value = {'UUID': uuid}
ET.SubElement(node, node_name, value)
return
def _emisor(self, datos):
node_name = '{}:Emisor'.format(self._pre) node_name = '{}:Emisor'.format(self._pre)
emisor = ET.SubElement(self._cfdi, node_name, datos) emisor = ET.SubElement(self._cfdi, node_name, datos)
return return
def _receptor(self, datos): def _receptor(self, datos):
receptor_name = datos['Nombre'].upper()
if receptor_name == PUBLIC and self._tipo_de_comprobante == CFDI_EGRESO:
receptor_name = datos['Nombre']
datos['Nombre'] = receptor_name
node_name = '{}:Receptor'.format(self._pre) node_name = '{}:Receptor'.format(self._pre)
emisor = ET.SubElement(self._cfdi, node_name, datos) emisor = ET.SubElement(self._cfdi, node_name, datos)
return return
def _conceptos(self, datos): def _conceptos(self, datos):
from xml.sax.saxutils import escape, unescape
conceptos = ET.SubElement(self._cfdi, '{}:Conceptos'.format(self._pre)) conceptos = ET.SubElement(self._cfdi, '{}:Conceptos'.format(self._pre))
# ~ for row in reversed(datos):
for row in datos: for row in datos:
complemento = {} complemento = {}
if 'complemento' in row: if 'complemento' in row:
complemento = row.pop('complemento') complemento = row.pop('complemento')
cuenta_predial = row.pop('CuentaPredial', '')
pedimento = row.pop('Pedimento', '')
student = row.pop('student', '')
taxes = {} taxes = {}
if 'impuestos' in row: if 'impuestos' in row:
@ -161,36 +370,26 @@ class CFDI(object):
ET.SubElement( ET.SubElement(
retenciones, '{}:Retencion'.format(self._pre), retencion) retenciones, '{}:Retencion'.format(self._pre), retencion)
if 'InformacionAduanera' in row: if pedimento:
for field in fields: attributes = {'NumeroPedimento': pedimento}
if field in row['InformacionAduanera']: node_name = '{}:InformacionAduanera'.format(self._pre)
attributes[field] = row['InformacionAduanera'][field] ET.SubElement(concepto, node_name, attributes)
if attributes:
node_name = '{}:InformacionAduanera'.format(self._pre)
ET.SubElement(concepto, node_name, attributes)
if 'CuentaPredial' in row: if cuenta_predial:
attributes = {'numero': row['CuentaPredial']} attributes = {'Numero': cuenta_predial}
node_name = '{}:CuentaPredial'.format(self._pre) node_name = '{}:CuentaPredial'.format(self._pre)
ET.SubElement(concepto, node_name, attributes) ET.SubElement(concepto, node_name, attributes)
if 'autRVOE' in row: if student:
fields = (
'version',
'nombreAlumno',
'CURP',
'nivelEducativo',
'autRVOE',
)
for field in fields:
if field in row['autRVOE']:
attributes[field] = row['autRVOE'][field]
node_name = '{}:ComplementoConcepto'.format(self._pre) node_name = '{}:ComplementoConcepto'.format(self._pre)
complemento = ET.SubElement(concepto, node_name) complemento = ET.SubElement(concepto, node_name)
ET.SubElement(complemento, 'iedu:instEducativas', attributes) ET.SubElement(complemento, 'iedu:instEducativas', student)
return return
def _impuestos(self, datos): def _impuestos(self, datos):
if self._is_nomina or not datos:
return
if not datos: if not datos:
node_name = '{}:Impuestos'.format(self._pre) node_name = '{}:Impuestos'.format(self._pre)
ET.SubElement(self._cfdi, node_name) ET.SubElement(self._cfdi, node_name)
@ -201,6 +400,10 @@ class CFDI(object):
for field in fields: for field in fields:
if field in datos: if field in datos:
attributes[field] = datos[field] attributes[field] = datos[field]
if not attributes:
return
node_name = '{}:Impuestos'.format(self._pre) node_name = '{}:Impuestos'.format(self._pre)
impuestos = ET.SubElement(self._cfdi, node_name, attributes) impuestos = ET.SubElement(self._cfdi, node_name, attributes)
@ -216,93 +419,276 @@ class CFDI(object):
return return
def _nomina(self, datos): def _nomina(self, datos):
sat_nomina = SAT[NOMINA_ACTUAL] pre = SAT['nomina']['prefix']
pre = sat_nomina['prefix']
complemento = ET.SubElement(self._cfdi, '{}:Complemento'.format(self._pre))
emisor = datos.pop('Emisor', None) if self._complemento is None:
receptor = datos.pop('Receptor', None) self._complemento = ET.SubElement(
percepciones = datos.pop('Percepciones', None) self._cfdi, '{}:Complemento'.format(self._pre))
deducciones = datos.pop('Deducciones', None)
attributes = {} emisor = datos.pop('emisor', None)
attributes['xmlns:{}'.format(pre)] = sat_nomina['xmlns'] receptor = datos.pop('receptor', None)
attributes['xsi:schemaLocation'] = sat_nomina['schema'] percepciones = datos.pop('percepciones', None)
attributes.update(datos) deducciones = datos.pop('deducciones', None)
otros_pagos = datos.pop('otros_pagos', ())
incapacidades = datos.pop('incapacidades', ())
if not 'Version' in attributes: nomina = ET.SubElement(
attributes['Version'] = sat_nomina['version'] self._complemento, '{}:Nomina'.format(pre), datos['nomina'])
nomina = ET.SubElement(complemento, '{}:Nomina'.format(pre), attributes)
if emisor: if emisor:
ET.SubElement(nomina, '{}:Emisor'.format(pre), emisor) ET.SubElement(nomina, '{}:Emisor'.format(pre), emisor)
if receptor: if receptor:
ET.SubElement(nomina, '{}:Receptor'.format(pre), receptor) node = ET.SubElement(nomina, '{}:Receptor'.format(pre), receptor)
if percepciones: if percepciones:
detalle = percepciones.pop('detalle', None) details = percepciones.pop('details', None)
percepciones = ET.SubElement(nomina, '{}:Percepciones'.format(pre), percepciones) hours_extra = percepciones.pop('hours_extra', None)
for row in detalle: separacion = percepciones.pop('separacion', None)
ET.SubElement(percepciones, '{}:Percepcion'.format(pre), row) if details:
node = ET.SubElement(nomina, '{}:Percepciones'.format(pre), percepciones)
for row in details:
nodep = ET.SubElement(node, '{}:Percepcion'.format(pre), row)
if row['TipoPercepcion'] == '019' and hours_extra:
for he in hours_extra:
ET.SubElement(nodep, '{}:HorasExtra'.format(pre), he)
hours_extra = None
if separacion:
ET.SubElement(node, '{}:SeparacionIndemnizacion'.format(pre), separacion)
if deducciones: if deducciones:
detalle = deducciones.pop('detalle', None) details = deducciones.pop('details', None)
deducciones = ET.SubElement(nomina, '{}:Deducciones'.format(pre), deducciones) if details:
for row in detalle: deducciones = ET.SubElement(nomina, '{}:Deducciones'.format(pre), deducciones)
ET.SubElement(deducciones, '{}:Deduccion'.format(pre), row) for row in details:
ET.SubElement(deducciones, '{}:Deduccion'.format(pre), row)
if otros_pagos:
node = ET.SubElement(nomina, '{}:OtrosPagos'.format(pre))
for row in otros_pagos:
subsidio = row.pop('subsidio', None)
subnode = ET.SubElement(node, '{}:OtroPago'.format(pre), row)
if subsidio:
ET.SubElement(subnode, '{}:SubsidioAlEmpleo'.format(pre), subsidio)
if incapacidades:
node = ET.SubElement(nomina, '{}:Incapacidades'.format(pre))
for row in incapacidades:
ET.SubElement(node, '{}:Incapacidad'.format(pre), row)
return
def _locales(self, datos):
if not self._impuestos_locales:
return
if self._complemento is None:
self._complemento = ET.SubElement(
self._cfdi, '{}:Complemento'.format(self._pre))
attributes = {}
attributes['version'] = SAT['locales']['version']
if not datos['total_locales_trasladados']:
datos['total_locales_trasladados'] = '0.00'
attributes['TotaldeTraslados'] = datos['total_locales_trasladados']
if not datos['total_locales_retenciones']:
datos['total_locales_retenciones'] = '0.00'
attributes['TotaldeRetenciones'] = datos['total_locales_retenciones']
node = ET.SubElement(
self._complemento, 'implocal:ImpuestosLocales', attributes)
for retencion in datos['locales_retenciones']:
ET.SubElement(node, 'implocal:RetencionesLocales', retencion)
for traslado in datos['locales_trasladados']:
ET.SubElement(node, 'implocal:TrasladosLocales', traslado)
return
def _donatarias(self, datos):
if not datos:
return
if self._complemento is None:
self._complemento = ET.SubElement(
self._cfdi, '{}:Complemento'.format(self._pre))
attributes = {}
attributes['version'] = SAT['donativo']['version']
attributes['leyenda'] = SAT['donativo']['leyenda']
attributes.update(datos)
node = ET.SubElement(self._complemento, 'donat:Donatarias', attributes)
return
def _complemento_comercio_exterior(self, datos):
prefix = SAT['comercioe']['prefix']
emisor = datos.pop('emisor')
propietarios = datos.pop('propietarios', {})
receptor = datos.pop('receptor')
destinatario = datos.pop('destinatario', {})
mercancias = datos.pop('mercancias')
attr = {'Version': SAT['comercioe']['version']}
attr.update(datos)
ce = ET.SubElement(
self._complemento, f'{prefix}:ComercioExterior', attr)
attributes = {}
if 'Curp' in emisor:
attributes = {'Curp': emisor.pop('Curp')}
node = ET.SubElement(ce, '{}:Emisor'.format(prefix), attributes)
ET.SubElement(node, '{}:Domicilio'.format(prefix), emisor)
attributes = {}
if 'NumRegIdTrib' in receptor:
attributes = {'NumRegIdTrib': receptor.pop('NumRegIdTrib')}
node = ET.SubElement(ce, '{}:Receptor'.format(prefix), attributes)
ET.SubElement(node, '{}:Domicilio'.format(prefix), receptor)
node = ET.SubElement(ce, '{}:Mercancias'.format(prefix))
fields = ('Marca', 'Modelo', 'SubModelo', 'NumeroSerie')
for row in mercancias:
detalle = {}
for f in fields:
if f in row and row[f]:
detalle[f] = row[f]
row.pop(f)
concepto = ET.SubElement(node, '{}:Mercancia'.format(prefix), row)
if detalle:
ET.SubElement(
concepto, '{}:DescripcionesEspecificas'.format(prefix), detalle)
return return
def _complementos(self, datos): def _complementos(self, datos):
complemento = ET.SubElement(self._cfdi, '{}:Complemento'.format(self._pre)) if not datos:
if 'ce' in datos: return
pre = 'cce11'
datos = datos.pop('ce')
emisor = datos.pop('emisor')
propietario = datos.pop('propietario')
receptor = datos.pop('receptor')
destinatario = datos.pop('destinatario')
conceptos = datos.pop('conceptos')
attributes = {} if self._complemento is None:
attributes['xmlns:{}'.format(pre)] = \ self._complemento = ET.SubElement(
'http://www.sat.gob.mx/ComercioExterior11' self._cfdi, '{}:Complemento'.format(self._pre))
attributes['xsi:schemaLocation'] = \
'http://www.sat.gob.mx/ComercioExterior11 ' \
'http://www.sat.gob.mx/sitio_internet/cfd/ComercioExterior11/ComercioExterior11.xsd'
attributes.update(datos)
ce = ET.SubElement(
complemento, '{}:ComercioExterior'.format(pre), attributes)
attributes = {} if self._carta_porte:
if 'Curp' in emisor: datos = datos['cartaporte']
attributes = {'Curp': emisor.pop('Curp')} ubicaciones = datos.pop('ubicaciones')
node = ET.SubElement(ce, '{}:Emisor'.format(pre), attributes) mercancias = datos.pop('mercancias', ())
ET.SubElement(node, '{}:Domicilio'.format(pre), emisor) tiposfigura = datos.pop('tiposfigura', ())
if propietario: autotransporte = datos.pop('autotransporte', {})
ET.SubElement(ce, '{}:Propietario'.format(pre), propietario) identificacion = autotransporte.pop('identificacion')
seguros = autotransporte.pop('seguros')
remolques = autotransporte.pop('remolques')
attributes = {} atributos = {'Version': SAT['cartaporte']['version']}
if 'NumRegIdTrib' in receptor: atributos.update(datos)
attributes = {'NumRegIdTrib': receptor.pop('NumRegIdTrib')}
node = ET.SubElement(ce, '{}:Receptor'.format(pre), attributes)
ET.SubElement(node, '{}:Domicilio'.format(pre), receptor)
attributes = {} prefix = SAT['cartaporte']['prefix']
if 'NumRegIdTrib' in destinatario: node_carta = ET.SubElement(self._complemento, f'{prefix}:CartaPorte', atributos)
attributes = {'NumRegIdTrib': destinatario.pop('NumRegIdTrib')}
if 'Nombre' in destinatario: node = ET.SubElement(node_carta, f'{prefix}:Ubicaciones')
attributes.update({'Nombre': destinatario.pop('Nombre')}) for ubicacion in ubicaciones:
node = ET.SubElement(ce, '{}:Destinatario'.format(pre), attributes) domicilio = ubicacion.pop('domicilio', {})
ET.SubElement(node, '{}:Domicilio'.format(pre), destinatario) dt = parser.parse(ubicacion['FechaHoraSalidaLlegada'])
ubicacion['FechaHoraSalidaLlegada'] = dt.isoformat()[:19]
sub_node = ET.SubElement(node, f'{prefix}:Ubicacion', ubicacion)
if domicilio:
ET.SubElement(sub_node, f'{prefix}:Domicilio', domicilio)
attr = mercancias
mercancias = attr.pop('mercancias')
node = ET.SubElement(node_carta, f'{prefix}:Mercancias', attr)
for mercancia in mercancias:
ET.SubElement(node, f'{prefix}:Mercancia', mercancia)
sub_node = ET.SubElement(node, f'{prefix}:Autotransporte', autotransporte)
ET.SubElement(sub_node, f'{prefix}:IdentificacionVehicular', identificacion)
ET.SubElement(sub_node, f'{prefix}:Seguros', seguros)
if remolques:
node_remolques = ET.SubElement(sub_node, f'{prefix}:Remolques')
for remolque in remolques:
ET.SubElement(node_remolques, f'{prefix}:Remolque', remolque)
if tiposfigura:
sub_node = ET.SubElement(node_carta, f'{prefix}:FiguraTransporte')
for figura in tiposfigura:
ET.SubElement(sub_node, f'{prefix}:TiposFigura', figura)
if self._divisas:
atributos = {
'version': SAT['divisas']['version'],
'tipoOperacion': self._divisas,
}
ET.SubElement(self._complemento, 'divisas:Divisas', atributos)
if 'ine' in datos:
atributos = {'Version': SAT['ine']['version']}
ine_key_entidad = datos['ine'].pop('ClaveEntidad', '')
ine_ambito = datos['ine'].pop('Ambito', '')
if ine_key_entidad:
ine_id_conta = datos['ine'].pop('IdContabilidad', '')
atributos.update(datos['ine'])
node_ine = ET.SubElement(self._complemento, 'ine:INE', atributos)
if ine_key_entidad:
attr = {'ClaveEntidad': ine_key_entidad}
if ine_ambito:
attr['Ambito'] = ine_ambito
node_entidad = ET.SubElement(node_ine, 'ine:Entidad', attr)
attr = {'IdContabilidad': ine_id_conta}
ET.SubElement(node_entidad, 'ine:Contabilidad', attr)
if 'pagos' in datos:
datos = datos.pop('pagos')
totales = datos.pop('totales')
relacionados = datos.pop('relacionados')
taxes_pay = datos.pop('taxes_pay')
pre = SAT['pagos']['prefix']
attributes = {'Version': SAT['pagos']['version']}
pagos = ET.SubElement(
self._complemento, '{}:Pagos'.format(pre), attributes)
ET.SubElement(pagos, '{}:Totales'.format(pre), totales)
node_pago = ET.SubElement(pagos, '{}:Pago'.format(pre), datos)
for row in relacionados:
taxes = row.pop('taxes')
node = ET.SubElement(node_pago, f'{pre}:DoctoRelacionado', row)
nodex_tax = None
if taxes['traslados'] or taxes['retenciones']:
node_tax = ET.SubElement(node, f'{pre}:ImpuestosDR')
if taxes['retenciones']:
node = ET.SubElement(node_tax, f'{pre}:RetencionesDR')
for tax in taxes['retenciones']:
ET.SubElement(node, f'{pre}:RetencionDR', tax)
if taxes['traslados']:
node = ET.SubElement(node_tax, f'{pre}:TrasladosDR')
for tax in taxes['traslados']:
ET.SubElement(node, f'{pre}:TrasladoDR', tax)
if taxes_pay['traslados'] or taxes_pay['retenciones']:
node_tax = ET.SubElement(node_pago, f'{pre}:ImpuestosP')
if taxes_pay['retenciones']:
node = ET.SubElement(node_tax, f'{pre}:RetencionesP')
for key, importe in taxes_pay['retenciones'].items():
attr = {'ImpuestoP': key, 'ImporteP': importe}
ET.SubElement(node, f'{pre}:RetencionP', attr)
if taxes_pay['traslados']:
node = ET.SubElement(node_tax, f'{pre}:TrasladosP')
for key, tax in taxes_pay['traslados'].items():
ET.SubElement(node, f'{pre}:TrasladoP', tax)
if 'leyendas' in datos:
pre = SAT['leyendas']['prefix']
attributes = {'version': SAT['leyendas']['version']}
node_leyend = ET.SubElement(
self._complemento, '{}:LeyendasFiscales'.format(pre), attributes)
for leyend in datos['leyendas']:
ET.SubElement(node_leyend, '{}:Leyenda'.format(pre), leyend)
if self._comercio_exterior:
datos = datos.pop('comercioe')
self._complemento_comercio_exterior(datos)
node = ET.SubElement(ce, '{}:Mercancias'.format(pre))
fields = ('Marca', 'Modelo', 'SubModelo', 'NumeroSerie')
for row in conceptos:
detalle = {}
for f in fields:
if f in row:
detalle[f] = row.pop(f)
concepto = ET.SubElement(node, '{}:Mercancia'.format(pre), row)
if detalle:
ET.SubElement(
concepto, '{}:DescripcionesEspecificas'.format(pre), detalle)
return return

View File

@ -1,10 +1,14 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
#~ import falcon import io
import os
import re import re
import smtplib import smtplib
import ssl
import collections import collections
from xml.sax.saxutils import escape
from collections import OrderedDict from collections import OrderedDict
from email.mime.multipart import MIMEMultipart from email.mime.multipart import MIMEMultipart
from email.mime.base import MIMEBase from email.mime.base import MIMEBase
@ -12,6 +16,12 @@ from email.mime.text import MIMEText
from email import encoders from email import encoders
from email.utils import formatdate from email.utils import formatdate
import requests
try:
from escpos import printer
except ImportError:
printer = None
from reportlab.platypus import BaseDocTemplate, Frame, PageTemplate, Image from reportlab.platypus import BaseDocTemplate, Frame, PageTemplate, Image
from reportlab.lib import colors from reportlab.lib import colors
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
@ -22,6 +32,9 @@ from reportlab.platypus import Paragraph, Table, TableStyle, Spacer
from reportlab.pdfgen import canvas from reportlab.pdfgen import canvas
CANCEL = False
#~ https://github.com/kennethreitz/requests/blob/v1.2.3/requests/structures.py#L37 #~ https://github.com/kennethreitz/requests/blob/v1.2.3/requests/structures.py#L37
class CaseInsensitiveDict(collections.MutableMapping): class CaseInsensitiveDict(collections.MutableMapping):
"""A case-insensitive ``dict``-like object. """A case-insensitive ``dict``-like object.
@ -101,12 +114,20 @@ class NumLet(object):
def letras(self): def letras(self):
return self._letras.upper() return self._letras.upper()
#~ def _letters(self, numero, moneda='peso', texto_inicial='-(', def _letters(self, numero, currency='MXN'):
#~ texto_final='/100 m.n.)-', fraccion_letras=False, fraccion=''): # ~ print (currency)
def _letters(self, numero, moneda='peso'): monedas = {
'MXN': 'peso',
'USD': 'dólar',
'EUR': 'euro',
}
moneda = monedas.get(currency, currency)
tf = {
'MXN': 'm.n.',
}
texto_inicial = '-(' texto_inicial = '-('
texto_final = '/100 m.n.)-' texto_final = '/100 {})-'.format(tf.get(currency, currency))
fraccion_letras = False fraccion_letras = False
fraccion = '' fraccion = ''
@ -248,10 +269,18 @@ class SendMail(object):
def _login(self): def _login(self):
try: try:
if self._config['ssl']: if self._config['ssl'] and self._config['starttls']:
self._server = smtplib.SMTP(
self._config['servidor'],
self._config['puerto'], timeout=10)
self._server.ehlo()
self._server.starttls()
self._server.ehlo()
elif self._config['ssl']:
self._server = smtplib.SMTP_SSL( self._server = smtplib.SMTP_SSL(
self._config['servidor'], self._config['servidor'],
self._config['puerto'], timeout=10) self._config['puerto'], timeout=10)
self._server.ehlo()
else: else:
self._server = smtplib.SMTP( self._server = smtplib.SMTP(
self._config['servidor'], self._config['servidor'],
@ -262,6 +291,7 @@ class SendMail(object):
if '535' in str(e): if '535' in str(e):
self._error = 'Nombre de usuario o contraseña inválidos' self._error = 'Nombre de usuario o contraseña inválidos'
return False return False
# ~ print (e)
if '534' in str(e) and 'gmail' in self._config['servidor']: if '534' in str(e) and 'gmail' in self._config['servidor']:
self._error = 'Necesitas activar el acceso a otras ' \ self._error = 'Necesitas activar el acceso a otras ' \
'aplicaciones en tu cuenta de GMail' 'aplicaciones en tu cuenta de GMail'
@ -282,10 +312,15 @@ class SendMail(object):
message['CC'] = options['copia'] message['CC'] = options['copia']
message['Subject'] = options['asunto'] message['Subject'] = options['asunto']
message['Date'] = formatdate(localtime=True) message['Date'] = formatdate(localtime=True)
if options['confirmar']:
message['Disposition-Notification-To'] = message['From']
message.attach(MIMEText(options['mensaje'], 'html')) message.attach(MIMEText(options['mensaje'], 'html'))
for f in options['files']: for f in options['files']:
part = MIMEBase('application', 'octet-stream') part = MIMEBase('application', 'octet-stream')
part.set_payload(f[0]) if isinstance(f[0], str):
part.set_payload(f[0].encode('utf-8'))
else:
part.set_payload(f[0])
encoders.encode_base64(part) encoders.encode_base64(part)
part.add_header( part.add_header(
'Content-Disposition', 'Content-Disposition',
@ -309,6 +344,7 @@ class SendMail(object):
class NumberedCanvas(canvas.Canvas): class NumberedCanvas(canvas.Canvas):
X = 20.59 * cm X = 20.59 * cm
XC = 21.6 * cm / 2 + 1.5 * cm
Y = 1.5 * cm Y = 1.5 * cm
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
@ -326,19 +362,33 @@ class NumberedCanvas(canvas.Canvas):
for state in self._saved_page_states: for state in self._saved_page_states:
self.__dict__.update(state) self.__dict__.update(state)
self.draw_page_number(page_count) self.draw_page_number(page_count)
self.draw_cancel()
canvas.Canvas.showPage(self) canvas.Canvas.showPage(self)
canvas.Canvas.save(self) canvas.Canvas.save(self)
return return
def draw_page_number(self, page_count): def draw_page_number(self, page_count):
self.setFont('Helvetica', 8) self.setFont('Helvetica', 8)
self.setFillColor(colors.darkred) # ~ self.setFillColor(colors.darkred)
text = 'Página {} de {}'.format(self._pageNumber, page_count) text = f'Página {self._pageNumber} de {page_count}'
self.drawRightString(self.X, self.Y, text) self.drawRightString(self.X, self.Y, text)
text = 'Factura elaborada con software libre: www.empresalibre.net'
text = 'Este documento es una representación impresa de un CFDI'
self.drawCentredString(self.XC, self.Y, text)
text = 'Factura elaborada con software libre'
self.drawString(1.5 * cm, 1.5 * cm, text) self.drawString(1.5 * cm, 1.5 * cm, text)
return return
def draw_cancel(self):
global CANCEL
if CANCEL:
self.rotate(45)
self.setFont('Helvetica', 100)
self.setFillColor(colors.red)
text = 'Cancelada'
self.drawCentredString(20 * cm, 3 * cm, text)
return
class TemplateInvoice(BaseDocTemplate): class TemplateInvoice(BaseDocTemplate):
@ -387,6 +437,9 @@ class TemplateInvoice(BaseDocTemplate):
def _emisor(self, styles, data): def _emisor(self, styles, data):
logo_path = data.pop('logo', '') logo_path = data.pop('logo', '')
logo_style = styles.pop('logo', {}) logo_style = styles.pop('logo', {})
logo_path2 = data.pop('logo2', '')
logo_style2 = styles.pop('logo2', {})
sucursales = styles.pop('sucursales', {})
for k, v in styles.items(): for k, v in styles.items():
self._set_text(styles[k], data.get(k, '')) self._set_text(styles[k], data.get(k, ''))
@ -397,6 +450,28 @@ class TemplateInvoice(BaseDocTemplate):
for k in keys: for k in keys:
rect[k] = rect[k] * cm rect[k] = rect[k] * cm
self.canv.drawImage(logo_path, **rect) self.canv.drawImage(logo_path, **rect)
if logo_path2 and logo_style2:
rect = logo_style2['rectangulo']
keys = ('x', 'y', 'width', 'height')
for k in keys:
rect[k] = rect[k] * cm
self.canv.drawImage(logo_path2, **rect)
if sucursales:
for k, sucursal in sucursales.items():
values = sucursal.pop('textos')
x = sucursal['rectangulo']['x']
y = sucursal['rectangulo']['y']
w = sucursal['rectangulo']['width']
h = sucursal['rectangulo']['height']
for v in values:
self._set_text(sucursal, v)
y -= h
sucursal['rectangulo']['x'] = x
sucursal['rectangulo']['y'] = y
sucursal['rectangulo']['width'] = w
sucursal['rectangulo']['height'] = h
return return
def _receptor(self, styles, data): def _receptor(self, styles, data):
@ -418,6 +493,7 @@ class TemplateInvoice(BaseDocTemplate):
def _comprobante1(self, styles, data): def _comprobante1(self, styles, data):
title = styles.pop('titulo', {}) title = styles.pop('titulo', {})
self.canv.setTitle(f"Factura {data.get('seriefolio', '')}")
for k, v in styles.items(): for k, v in styles.items():
self._set_text(styles[k], data.get(k, '')) self._set_text(styles[k], data.get(k, ''))
@ -449,6 +525,12 @@ class TemplateInvoice(BaseDocTemplate):
fields = ('valorunitario', 'importe') fields = ('valorunitario', 'importe')
if field in fields: if field in fields:
return self._currency(value) return self._currency(value)
if field == 'descripcion':
html = '<font color="black" size=7>{}</font>'
style_bt = getSampleStyleSheet()['BodyText']
return Paragraph(html.format(value), style_bt)
return value return value
def _conceptos(self, conceptos): def _conceptos(self, conceptos):
@ -484,8 +566,8 @@ class TemplateInvoice(BaseDocTemplate):
('GRID', (0, 0), (-1, -1), 0.05 * cm, colors.white), ('GRID', (0, 0), (-1, -1), 0.05 * cm, colors.white),
('ALIGN', (1, 0), (-1, -1), 'RIGHT'), ('ALIGN', (1, 0), (-1, -1), 'RIGHT'),
('FONTSIZE', (0, 0), (-1, -1), 8), ('FONTSIZE', (0, 0), (-1, -1), 8),
('BACKGROUND', (1, 0), (-1, -1), colors.linen), ('BACKGROUND', (1, 0), (-1, -1), colors.lightgrey),
('TEXTCOLOR', (1, 0), (-1, -1), colors.darkred), ('TEXTCOLOR', (1, 0), (-1, -1), colors.black),
('FACE', (1, 0), (-1, -1), 'Helvetica-Bold'), ('FACE', (1, 0), (-1, -1), 'Helvetica-Bold'),
] ]
table = Table(rows, colWidths=widths, spaceBefore=0.25*cm) table = Table(rows, colWidths=widths, spaceBefore=0.25*cm)
@ -493,7 +575,7 @@ class TemplateInvoice(BaseDocTemplate):
return table return table
def _comprobante2(self, styles, data): def _comprobante2(self, styles, data):
leyenda = styles.pop('leyenda', {}) leyendas = styles.pop('leyendas', {})
ls = [] ls = []
for k, v in styles.items(): for k, v in styles.items():
@ -501,17 +583,27 @@ class TemplateInvoice(BaseDocTemplate):
if 'spaceBefore' in v['estilo']: if 'spaceBefore' in v['estilo']:
v['estilo']['spaceBefore'] = v['estilo']['spaceBefore'] * cm v['estilo']['spaceBefore'] = v['estilo']['spaceBefore'] * cm
ps = ParagraphStyle(**v['estilo']) ps = ParagraphStyle(**v['estilo'])
p = Paragraph(data[k], ps) p = Paragraph(escape(data[k]), ps)
ls.append(p)
elif k=='formametodopago':
ps = ParagraphStyle(**v['estilo'])
v = f"{data['formadepago']} - {data['metododepago']}"
p = Paragraph(v, ps)
ls.append(p)
elif k=='monedatipocambio':
ps = ParagraphStyle(**v['estilo'])
v = f"{data['moneda']} - {data['tipocambio']}"
p = Paragraph(v, ps)
ls.append(p) ls.append(p)
cbb = Image(data['path_cbb']) cbb = Image(data['cbb'])
cbb.drawHeight = 4 * cm cbb.drawHeight = 4 * cm
cbb.drawWidth = 4 * cm cbb.drawWidth = 4 * cm
style_bt = getSampleStyleSheet()['BodyText'] style_bt = getSampleStyleSheet()['BodyText']
style_bt.leading = 8 style_bt.leading = 8
html_t = '<b><font size=6>{}</font></b>' html_t = '<b><font size=6>{}</font></b>'
html = '<font color="darkred" size=5>{}</font>' html = '<font color="black" size=5>{}</font>'
msg = 'Cadena original del complemento de certificación digital del SAT' msg = 'Cadena original del complemento de certificación digital del SAT'
rows = [ rows = [
(cbb, Paragraph(html_t.format('Sello Digital del CFDI'), style_bt)), (cbb, Paragraph(html_t.format('Sello Digital del CFDI'), style_bt)),
@ -527,13 +619,13 @@ class TemplateInvoice(BaseDocTemplate):
('FONTSIZE', (0, 0), (-1, -1), 6), ('FONTSIZE', (0, 0), (-1, -1), 6),
('SPAN', (0, 0), (0, -1)), ('SPAN', (0, 0), (0, -1)),
('FACE', (1, 0), (1, 0), 'Helvetica-Bold'), ('FACE', (1, 0), (1, 0), 'Helvetica-Bold'),
('BACKGROUND', (1, 1), (1, 1), colors.linen), ('BACKGROUND', (1, 1), (1, 1), colors.lightgrey),
('TEXTCOLOR', (1, 1), (1, 1), colors.darkred), ('TEXTCOLOR', (1, 1), (1, 1), colors.darkred),
('FACE', (1, 2), (1, 2), 'Helvetica-Bold'), ('FACE', (1, 2), (1, 2), 'Helvetica-Bold'),
('BACKGROUND', (1, 3), (1, 3), colors.linen), ('BACKGROUND', (1, 3), (1, 3), colors.lightgrey),
('TEXTCOLOR', (1, 3), (1, 3), colors.darkred), ('TEXTCOLOR', (1, 3), (1, 3), colors.darkred),
('FACE', (1, 4), (1, 4), 'Helvetica-Bold'), ('FACE', (1, 4), (1, 4), 'Helvetica-Bold'),
('BACKGROUND', (1, 5), (1, 5), colors.linen), ('BACKGROUND', (1, 5), (1, 5), colors.lightgrey),
('TEXTCOLOR', (1, 5), (1, 5), colors.darkred), ('TEXTCOLOR', (1, 5), (1, 5), colors.darkred),
('ALIGN', (0, 0), (0, 0), 'CENTER'), ('ALIGN', (0, 0), (0, 0), 'CENTER'),
('VALIGN', (0, 0), (0, 0), 'MIDDLE'), ('VALIGN', (0, 0), (0, 0), 'MIDDLE'),
@ -542,14 +634,14 @@ class TemplateInvoice(BaseDocTemplate):
table.setStyle(TableStyle(table_styles)) table.setStyle(TableStyle(table_styles))
ls.append(table) ls.append(table)
if leyenda: if leyendas:
if 'spaceBefore' in leyenda['estilo']: if 'spaceBefore' in leyendas['estilo']:
leyenda['estilo']['spaceBefore'] = \ leyendas['estilo']['spaceBefore'] = \
leyenda['estilo']['spaceBefore'] * cm leyendas['estilo']['spaceBefore'] * cm
msg = 'Este documento es una representación impresa de un CFDI' for t in leyendas['textos']:
ps = ParagraphStyle(**leyenda['estilo']) ps = ParagraphStyle(**leyendas['estilo'])
p = Paragraph(msg, ps) p = Paragraph(t, ps)
ls.append(p) ls.append(p)
return ls return ls
@ -565,8 +657,10 @@ class TemplateInvoice(BaseDocTemplate):
return self._data return self._data
@data.setter @data.setter
def data(self, values): def data(self, values):
#~ print (values) global CANCEL
# ~ print (values)
self._data = values self._data = values
CANCEL = self._data['cancelada']
rows = self._conceptos(self._data['conceptos']) rows = self._conceptos(self._data['conceptos'])
widths = [2 * cm, 9 * cm, 1.5 * cm, 2 * cm, 2 * cm, 3 * cm] widths = [2 * cm, 9 * cm, 1.5 * cm, 2 * cm, 2 * cm, 3 * cm]
@ -576,13 +670,13 @@ class TemplateInvoice(BaseDocTemplate):
('ALIGN', (0, 0), (-1, 0), 'CENTER'), ('ALIGN', (0, 0), (-1, 0), 'CENTER'),
('TEXTCOLOR', (0, 0), (-1, 0), colors.white), ('TEXTCOLOR', (0, 0), (-1, 0), colors.white),
('FACE', (0, 0), (-1, 0), 'Helvetica-Bold'), ('FACE', (0, 0), (-1, 0), 'Helvetica-Bold'),
('BACKGROUND', (0, 0), (-1, 0), colors.darkred), ('BACKGROUND', (0, 0), (-1, 0), colors.grey),
('FONTSIZE', (0, 1), (-1, -1), 7), ('FONTSIZE', (0, 1), (-1, -1), 7),
('VALIGN', (0, 1), (-1, -1), 'TOP'), ('VALIGN', (0, 1), (-1, -1), 'TOP'),
('ALIGN', (0, 1), (0, -1), 'CENTER'), ('ALIGN', (0, 1), (0, -1), 'CENTER'),
('ALIGN', (2, 1), (2, -1), 'CENTER'), ('ALIGN', (2, 1), (2, -1), 'CENTER'),
('ALIGN', (3, 1), (5, -1), 'RIGHT'), ('ALIGN', (3, 1), (5, -1), 'RIGHT'),
('LINEBELOW', (0, 1), (-1, -1), 0.05 * cm, colors.darkred), ('LINEBELOW', (0, 1), (-1, -1), 0.05 * cm, colors.grey),
('LINEBEFORE', (0, 1), (-1, -1), 0.05 * cm, colors.white), ('LINEBEFORE', (0, 1), (-1, -1), 0.05 * cm, colors.white),
] ]
table_conceptos = Table(rows, colWidths=widths, repeatRows=1) table_conceptos = Table(rows, colWidths=widths, repeatRows=1)
@ -592,7 +686,7 @@ class TemplateInvoice(BaseDocTemplate):
comprobante = self._comprobante2( comprobante = self._comprobante2(
self._custom_styles['comprobante'], self.data['comprobante']) self._custom_styles['comprobante'], self.data['comprobante'])
self._rows = [Spacer(0, 6*cm), table_conceptos, totales] + comprobante self._rows = [Spacer(0, 5.7 * cm), table_conceptos, totales] + comprobante
def render(self): def render(self):
frame = Frame(self.leftMargin, self.bottomMargin, frame = Frame(self.leftMargin, self.bottomMargin,
@ -724,3 +818,303 @@ class ReportTemplate(BaseDocTemplate):
return return
class SeaFileAPI(object):
FILE_DOES_NOT_EXIST = 441
def __init__(self, url, username, password):
self._url = url
self._headers = self._get_auth(username, password)
@property
def is_connect(self):
return bool(self._headers)
def _open(self, path):
return open(path, 'rb')
def _get_auth(self, username, password):
url = self._url + 'auth-token/'
data = {
'username': username,
'password': password,
}
resp = requests.post(url, data=data)
if resp.status_code != requests.codes.ok:
msg = 'Token Error: {}'.format(resp.status_code)
print (msg)
return {}
headers = {'Authorization': 'Token {}'.format(resp.json()['token'])}
return headers
def _get_upload_link(self, repo_id):
if not self._headers:
return ''
url = '{}repos/{}/upload-link/'.format(self._url, repo_id)
resp = requests.get(url, headers=self._headers)
if resp.status_code != requests.codes.ok:
msg = 'Error: {}'.format(resp.status_code)
print (msg)
return ''
return resp.json()
def _get_update_link(self, repo_id):
if not self._headers:
return ''
url = '{}repos/{}/update-link/'.format(self._url, repo_id)
data = {'p': '/'}
resp = requests.get(url, data=data, headers=self._headers)
if resp.status_code != requests.codes.ok:
msg = 'Error: {}'.format(resp.status_code)
print (msg)
return ''
return resp.json()
def _decrypt(self, repo_id, password):
if not self._headers:
return False
url = '{}repos/{}/'.format(self._url, repo_id)
data = {'password': password}
resp = requests.post(url, data=data, headers=self._headers)
if resp.status_code != requests.codes.ok:
msg = 'Error: {}'.format(resp.status_code)
print (msg)
return False
return True
def upload_file(self, path_file, repo_id, relative_path, password=''):
if not self._headers:
return False
if password:
if not self._decrypt(repo_id, password):
return False
upload_link = self._get_upload_link(repo_id)
if isinstance(path_file, tuple):
obj_file = path_file[0]
filename = path_file[1]
else:
obj_file = self._open(path_file)
_, filename = self._info_path(path_file)
data = {
# ~ 'filename': filename,
'parent_dir': relative_path,
'relative_path': '',
}
files = {'file': (filename, obj_file)}
resp = requests.post(
upload_link, data=data, files=files, headers=self._headers)
if resp.status_code != requests.codes.ok:
msg = 'Upload Code: {}\n{}'.format(resp.status_code, resp.text)
print (msg)
return False
return True
def _info_path(self, path):
path, filename = os.path.split(path)
return path, filename
def list_directory(self, repo_id):
if not self._headers:
return False
url = '{}repos/{}/dir/'.format(self._url, repo_id)
params = {'p': '/', 't': 'd', 'recursive': '1'}
try:
resp = requests.get(url, params=params, headers=self._headers)
return resp.json()
except Exception as e:
return False
def create_dir(self, repo_id, name):
url = '{}repos/{}/dir/'.format(self._url, repo_id)
data = {'operation': 'mkdir'}
params = {'p': name}
resp = requests.post(url, data=data, headers=self._headers, params=params)
if resp.status_code != requests.codes.created:
msg = 'Create Dir Error: {}'.format(resp.status_code)
print (msg)
return False
return True
def _validate_directory(self, repo_id, target_file):
if target_file == '/':
return True
names = target_file.split('/')[:-1]
directories = self.list_directory(repo_id)
if isinstance(directories, bool):
return False
exists = False
parent_dir = '/'
name_dir = ''
for name in names:
name_dir += '/' + name
for directory in directories:
if name == directory['name'] and parent_dir == directory['parent_dir']:
exists = True
break
if exists:
exists = False
else:
self.create_dir(repo_id, name_dir)
if parent_dir == '/':
parent_dir += name
else:
parent_dir += '/' + name
return True
def update_file(self, path_file, repo_id, target_file, password, create=True):
if not self._headers:
return False
if not self._validate_directory(repo_id, target_file):
return False
if password:
if not self._decrypt(repo_id, password):
return False
update_link = self._get_update_link(repo_id)
if isinstance(path_file, tuple):
obj_file = path_file[0]
filename = path_file[1]
else:
obj_file = self._open(path_file)
_, filename = self._info_path(path_file)
files = {
'file': (filename, obj_file),
'filename': (None, filename),
'target_file': (None, '{}{}'.format(target_file, filename))
}
resp = requests.post(update_link, files=files, headers=self._headers)
if resp.status_code != requests.codes.ok:
msg = 'Update Code: {}\n{}'.format(resp.status_code, resp.text)
print (msg)
if resp.status_code == self.FILE_DOES_NOT_EXIST and create:
relative_path = '/'
if target_file != '/':
relative_path += target_file
return self.upload_file(path_file, repo_id, relative_path, password)
else:
return False
return True
def ping(self):
url = self._url + 'ping/'
resp = requests.get(url)
return resp.json()
def auth_ping(self):
url = self._url + 'auth/ping/'
resp = requests.get(url, headers=self._headers)
return resp.json()
class PrintTicket(object):
LINE = '------------------------------------------------\n'
TITLES = 'CANT. U ARTICULO P.U. TOTAL\n'
LEYENDA = 'GRACIAS POR SU COMPRA\n\nGuarde este ticket para cualquier ' \
'aclaración.\nComprobante simplificado de operación con\npúblico en ' \
'general de acuerdo al Art. 37\nFracc II inc. v del Reglamento del\n' \
'Código Fiscal de la Federación.\n\n'
def __init__(self, info):
self.p = self._init_printer(info)
def _init_printer(self, info):
try:
if info['ip']:
p = printer.Network(info['ip'])
else:
p = printer.Usb(*info['usb'])
p.codepage = 'cp850'
return p
except Exception as e:
print (e)
return
def _set(self, *data):
self.p.set(*data)
return
def _t(self, text):
self.p.text(text)
return
def _l(self):
self._t(self.LINE)
return
def printer(self, data):
if self.p is None:
return False
self._emisor(data['emisor'])
self._receptor(data['receptor'])
self._ticket(data['ticket'])
self._products(data['products'])
self._footer(data['ticket'])
self.p.cut()
return True
def _emisor(self, data):
self._set('center', 'B', 'B', 2, 2)
self._t(data['name'])
self._set('center', 'B', 'B', 2)
self._t(data['rfc'])
self._set('center', 'A')
self._t(data['regimen'])
self._t(data['address'])
return
def _receptor(self, data):
self._set('center', 'B', 'B', 2)
self._t(data['name'])
return
def _ticket(self, data):
self._set('left', 'B', 'B', 2)
self._t(data['title'])
self._set('left', 'A')
self._t(data['date'])
return
def _products(self, data):
self._l()
self._t(self.TITLES)
self._l()
for p in data:
self._t(p)
self._l()
self._t('Total artículos: {}\n'.format(len(data)))
self._l()
return
def _footer(self, data):
self._set('right', 'B', 'B', 2)
self._t(data['total'])
self._set('center', 'A')
self._t(data['letters'])
self._t(self.LEYENDA)
self._set('center', 'A', 'B')
self._t('empresalibre.net')
return

View File

@ -2,6 +2,35 @@
import falcon import falcon
from middleware import get_template from middleware import get_template
from urllib.parse import unquote
class AppEmpresas(object):
template = 'empresas.html'
def __init__(self, db):
self._db = db
@falcon.after(get_template)
def on_get(self, req, resp):
values = req.params
resp.status = falcon.HTTP_200
def on_post(self, req, resp):
values = req.params
opt = values.pop('opt', '1')
if opt == '1':
req.context['result'] = self._db.empresa_agregar(values)
elif opt == '2':
req.context['result'] = self._db.respaldar_dbs()
resp.status = falcon.HTTP_200
def on_delete(self, req, resp):
values = req.params
if self._db.empresa_borrar(values):
resp.status = falcon.HTTP_200
else:
resp.status = falcon.HTTP_204
class AppLogin(object): class AppLogin(object):
@ -16,11 +45,14 @@ class AppLogin(object):
def on_post(self, req, resp): def on_post(self, req, resp):
session = req.env['beaker.session'] session = req.env['beaker.session']
session.invalidate()
values = req.params values = req.params
values['rfc'] = values['rfc'].upper() values['rfc'] = values['rfc'].upper()
result = self._db.authenticate(values) values['ip'] = req.remote_addr
result, user = self._db.authenticate(values)
if result['login']: if result['login']:
session.save() session.save()
session['userobj'] = user
session['user'] = result['user'] session['user'] = result['user']
session['rfc'] = values['rfc'] session['rfc'] = values['rfc']
req.context['result'] = result req.context['result'] = result
@ -62,24 +94,101 @@ class AppMain(object):
class AppValues(object): class AppValues(object):
TABLES = ('allusuarios', 'usuario', 'usuarioupdate', 'editusuario',
'addusuario')
def __init__(self, db): def __init__(self, db):
self._db = db self._db = db
def _valid_user(self, table, user):
if table in self.TABLES:
if user.es_admin or user.es_superusuario:
return True
else:
return False
return True
def on_get(self, req, resp, table): def on_get(self, req, resp, table):
values = req.params values = req.params
req.context['result'] = self._db.get_values(table, values) session = req.env['beaker.session']
if table == 'product':
original = values['name']
try:
values['name'] = unquote(req.query_string.split('=')[1])
except Exception as e:
values['name'] = original
if req.path in ('/values/titlelogin', '/values/empresas'):
req.context['result'] = self._db.get_values(table, values, session)
resp.status = falcon.HTTP_200
return
if not 'userobj' in session and req.path != '/values/empresas':
session.invalidate()
raise falcon.HTTPTemporaryRedirect('/')
if table == 'admin':
req.context['result'] = session['userobj'].es_superusuario \
or session['userobj'].es_admin
else:
if not self._valid_user(table, session['userobj']):
resp.status = falcon.HTTP_403
return
req.context['result'] = self._db.get_values(table, values, session)
resp.status = falcon.HTTP_200 resp.status = falcon.HTTP_200
def on_delete(self, req, resp, table):
values = req.params
session = req.env['beaker.session']
if table == 'usuario' and (session['userobj'].id == int(values['id'])):
resp.status = falcon.HTTP_204
return
if not self._valid_user(table, session['userobj']):
resp.status = falcon.HTTP_403
return
if self._db.delete(table, values['id']):
resp.status = falcon.HTTP_200
else:
resp.status = falcon.HTTP_204
def on_post(self, req, resp, table): def on_post(self, req, resp, table):
file_object = req.get_param('upload') file_object = req.get_param('upload')
if file_object is None: if file_object is None:
session = req.env['beaker.session'] session = req.env['beaker.session']
values = req.params values = req.params
if not self._valid_user(table, session['userobj']):
resp.status = falcon.HTTP_403
return
if table == 'correo': if table == 'correo':
req.context['result'] = self._db.validate_email(values) req.context['result'] = self._db.validate_email(values)
elif table == 'sendmail': elif table == 'sendmail':
req.context['result'] = self._db.send_email(values, session) req.context['result'] = self._db.send_email(values, session)
elif table == 'enviarprefac':
req.context['result'] = self._db.enviar_prefac(values)
elif table == 'addmoneda':
req.context['result'] = self._db.add_moneda(values)
elif table == 'addunidad':
req.context['result'] = self._db.add_unidad(values)
elif table == 'addimpuesto':
req.context['result'] = self._db.add_impuesto(values)
elif table == 'addusuario':
req.context['result'] = self._db.add_usuario(values)
elif table == 'editusuario':
req.context['result'] = self._db.edit_usuario(values)
elif table == 'bdfl':
req.context['result'] = self._db.importar_bdfl()
elif table == 'invoicenotes':
req.context['result'] = self._db.save_invoice_notes(values)
elif table == 'nivedu':
req.context['result'] = self._db.add_nivel_educativo(values)
else: else:
req.context['result'] = self._db.validate_cert(values, session) req.context['result'] = self._db.validate_cert(values, session)
else: else:
@ -87,6 +196,22 @@ class AppValues(object):
resp.status = falcon.HTTP_200 resp.status = falcon.HTTP_200
class AppFiles(object):
def __init__(self, db):
self._db = db
def on_get(self, req, resp, table):
values = req.params
resp.status = falcon.HTTP_200
def on_post(self, req, resp, table):
session = req.env['beaker.session']
file_object = req.get_param('upload')
req.context['result'] = self._db.upload_file(session, table, file_object)
resp.status = falcon.HTTP_200
class AppConfig(object): class AppConfig(object):
def __init__(self, db): def __init__(self, db):
@ -102,6 +227,12 @@ class AppConfig(object):
req.context['result'] = self._db.add_config(values) req.context['result'] = self._db.add_config(values)
resp.status = falcon.HTTP_200 resp.status = falcon.HTTP_200
def on_delete(self, req, resp):
values = req.params
if self._db.delete('config', values['id']):
resp.status = falcon.HTTP_200
else:
resp.status = falcon.HTTP_204
class AppPartners(object): class AppPartners(object):
@ -127,6 +258,29 @@ class AppPartners(object):
resp.status = falcon.HTTP_204 resp.status = falcon.HTTP_204
class AppStudents(object):
def __init__(self, db):
self._db = db
def on_get(self, req, resp):
values = req.params
req.context['result'] = self._db.get_students(values)
resp.status = falcon.HTTP_200
def on_post(self, req, resp):
values = req.params
req.context['result'] = self._db.students(values)
resp.status = falcon.HTTP_200
def on_delete(self, req, resp):
values = req.params
if self._db.delete('students', values['id']):
resp.status = falcon.HTTP_200
else:
resp.status = falcon.HTTP_204
class AppProducts(object): class AppProducts(object):
def __init__(self, db): def __init__(self, db):
@ -134,12 +288,17 @@ class AppProducts(object):
def on_get(self, req, resp): def on_get(self, req, resp):
values = req.params 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 resp.status = falcon.HTTP_200
def on_post(self, req, resp): def on_post(self, req, resp):
values = req.params values = req.params
req.context['result'] = self._db.product(values) req.context['result'] = self._db.products(values)
resp.status = falcon.HTTP_200 resp.status = falcon.HTTP_200
def on_delete(self, req, resp): def on_delete(self, req, resp):
@ -157,22 +316,72 @@ class AppInvoices(object):
def on_get(self, req, resp): def on_get(self, req, resp):
values = req.params values = req.params
req.context['result'] = self._db.get_invoices(values) session = req.env['beaker.session']
req.context['result'] = self._db.get_invoices(values, session['userobj'])
resp.status = falcon.HTTP_200 resp.status = falcon.HTTP_200
def on_post(self, req, resp): def on_post(self, req, resp):
values = req.params values = req.params
req.context['result'] = self._db.invoice(values) session = req.env['beaker.session']
req.context['result'] = self._db.invoice(values, session['userobj'])
resp.status = falcon.HTTP_200
def on_put(self, req, resp):
values = req.params
session = req.env['beaker.session']
req.context['result'] = self._db.invoice_put(values, session['userobj'])
resp.status = falcon.HTTP_200 resp.status = falcon.HTTP_200
def on_delete(self, req, resp): def on_delete(self, req, resp):
values = req.params values = req.params
if self._db.delete('invoice', values['id']): session = req.env['beaker.session']
if self._db.delete('invoice', values['id'], session['userobj']):
resp.status = falcon.HTTP_200 resp.status = falcon.HTTP_200
else: else:
resp.status = falcon.HTTP_204 resp.status = falcon.HTTP_204
class AppPreInvoices(object):
def __init__(self, db):
self._db = db
def on_get(self, req, resp):
values = req.params
req.context['result'] = self._db.get_preinvoices(values)
resp.status = falcon.HTTP_200
def on_post(self, req, resp):
values = req.params
req.context['result'] = self._db.preinvoice(values)
resp.status = falcon.HTTP_200
def on_delete(self, req, resp):
values = req.params
if self._db.delete('preinvoice', values['id']):
resp.status = falcon.HTTP_200
else:
resp.status = falcon.HTTP_204
class AppTickets(object):
def __init__(self, db):
self._db = db
def on_get(self, req, resp):
values = req.params
session = req.env['beaker.session']
req.context['result'] = self._db.get_tickets(values, session['userobj'])
resp.status = falcon.HTTP_200
def on_post(self, req, resp):
values = req.params
session = req.env['beaker.session']
req.context['result'] = self._db.tickets(values, session['userobj'])
resp.status = falcon.HTTP_200
class AppEmisor(object): class AppEmisor(object):
def __init__(self, db): def __init__(self, db):
@ -197,6 +406,55 @@ class AppEmisor(object):
resp.status = falcon.HTTP_204 resp.status = falcon.HTTP_204
class AppCuentasBanco(object):
def __init__(self, db):
self._db = db
def on_get(self, req, resp):
values = req.params
session = req.env['beaker.session']
req.context['result'] = self._db.get_cuentasbanco(values)
resp.status = falcon.HTTP_200
def on_post(self, req, resp):
values = req.params
req.context['result'] = self._db.cuentasbanco(values)
resp.status = falcon.HTTP_200
def on_delete(self, req, resp):
values = req.params
if self._db.delete('cuentasbanco', values['id']):
resp.status = falcon.HTTP_200
else:
resp.status = falcon.HTTP_204
class AppMovimientosBanco(object):
def __init__(self, db):
self._db = db
def on_get(self, req, resp):
values = req.params
session = req.env['beaker.session']
req.context['result'] = self._db.get_movimientosbanco(values)
resp.status = falcon.HTTP_200
def on_post(self, req, resp):
values = req.params
# ~ req.context['result'] = self._db.add_movbanco(values)
req.context['result'] = self._db.bankmovement(values)
resp.status = falcon.HTTP_200
def on_delete(self, req, resp):
values = req.params
if self._db.delete('movbanco', values['id']):
resp.status = falcon.HTTP_200
else:
resp.status = falcon.HTTP_204
class AppFolios(object): class AppFolios(object):
def __init__(self, db): def __init__(self, db):
@ -220,17 +478,350 @@ class AppFolios(object):
resp.status = falcon.HTTP_204 resp.status = falcon.HTTP_204
class AppEmployees(object):
def __init__(self, db):
self._db = db
def on_get(self, req, resp):
values = req.params
req.context['result'] = self._db.get_employees(values)
resp.status = falcon.HTTP_200
def on_post(self, req, resp):
values = req.params
req.context['result'] = self._db.employees(values)
resp.status = falcon.HTTP_200
def on_delete(self, req, resp):
values = req.params
if self._db.delete('employee', values['id']):
resp.status = falcon.HTTP_200
else:
resp.status = falcon.HTTP_204
class AppNomina(object):
def __init__(self, db):
self._db = db
def on_get(self, req, resp):
values = req.params
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):
values = req.params
session = req.env['beaker.session']
req.context['result'] = self._db.nomina(values, session['userobj'])
resp.status = falcon.HTTP_200
def on_delete(self, req, resp):
values = req.params
if self._db.delete('nomina', values['id']):
resp.status = falcon.HTTP_200
else:
resp.status = falcon.HTTP_204
class AppDocumentos(object): class AppDocumentos(object):
def __init__(self, db): def __init__(self, db):
self._db = db self._db = db
#~ self._not_json = True
def on_get(self, req, resp, type_doc, id_doc): def on_get(self, req, resp, type_doc, id_doc):
# ~ print('TD', type_doc)
session = req.env['beaker.session'] session = req.env['beaker.session']
req.context['result'], file_name, content_type = \ req.context['result'], file_name, content_type = \
self._db.get_doc(type_doc, id_doc, session['rfc']) self._db.get_doc(type_doc, id_doc, session['rfc'])
resp.append_header('Content-Disposition', if not type_doc in ('pdf', 'pre', 'tpdf', 'pdfpago', 'html', 'nompdf'):
'attachment; filename={}'.format(file_name)) resp.append_header('Content-Disposition',
'attachment; filename={}'.format(file_name))
if type_doc in ('pdf', 'nompdf', 'pdfpago'):
resp.append_header('Content-Disposition',
'inline; filename={}'.format(file_name))
resp.content_type = content_type resp.content_type = content_type
resp.status = falcon.HTTP_200 resp.status = falcon.HTTP_200
# ~ Revisado
class AppInvoicePay(object):
def __init__(self, db):
self._db = db
def on_get(self, req, resp):
values = req.params
req.context['result'] = self._db.get_invoicepay(values)
resp.status = falcon.HTTP_200
class AppCfdiPay(object):
def __init__(self, db):
self._db = db
def on_get(self, req, resp):
values = req.params
req.context['result'] = self._db.get_cfdipay(values)
resp.status = falcon.HTTP_200
def on_post(self, req, resp):
values = req.params
req.context['result'] = self._db.cfdipay(values)
resp.status = falcon.HTTP_200
class AppSATBancos(object):
def __init__(self, db):
self._db = db
def on_get(self, req, resp):
values = req.params
req.context['result'] = self._db.get_sat_bancos(values)
resp.status = falcon.HTTP_200
def on_post(self, req, resp):
values = req.params
req.context['result'] = self._db.sat_bancos(values)
resp.status = falcon.HTTP_200
class AppSATFormaPago(object):
def __init__(self, db):
self._db = db
def on_get(self, req, resp):
values = req.params
req.context['result'] = self._db.get_sat_forma_pago(values)
resp.status = falcon.HTTP_200
def on_post(self, req, resp):
values = req.params
req.context['result'] = self._db.sat_forma_pago(values)
resp.status = falcon.HTTP_200
class AppSATLeyendaFiscales(object):
def __init__(self, db):
self._db = db
def on_get(self, req, resp):
values = req.params
req.context['result'] = self._db.sat_leyendas_fiscales_get(values)
resp.status = falcon.HTTP_200
def on_post(self, req, resp):
values = req.params
req.context['result'] = self._db.sat_leyendas_fiscales_post(values)
resp.status = falcon.HTTP_200
def on_delete(self, req, resp):
values = req.params
if self._db.sat_leyendas_fiscales_delete(values['id']):
resp.status = falcon.HTTP_200
else:
resp.status = falcon.HTTP_204
class AppSociosCuentasBanco(object):
def __init__(self, db):
self._db = db
def on_get(self, req, resp):
values = req.params
req.context['result'] = self._db.get_partners_accounts_bank(values)
resp.status = falcon.HTTP_200
def on_post(self, req, resp):
values = req.params
req.context['result'] = self._db.partners_accounts_bank(values)
resp.status = falcon.HTTP_200
class AppCert(object):
def __init__(self, db):
self._db = db
def on_get(self, req, resp):
values = req.params
req.context['result'] = self._db.cert_get(values)
resp.status = falcon.HTTP_200
def on_post(self, req, resp):
values = req.params
req.context['result'] = self._db.cert_post(values)
resp.status = falcon.HTTP_200
class AppSucursales(object):
def __init__(self, db):
self._db = db
def on_get(self, req, resp):
values = req.params
req.context['result'] = self._db.sucursales_get(values)
resp.status = falcon.HTTP_200
def on_post(self, req, resp):
values = req.params
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
class AppSATUnidadesPeso(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.sat_unidades_peso_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.sat_unidades_peso_post(values, user)
resp.status = falcon.HTTP_200
class AppSATRegimenes(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.sat_regimenes_get(values, user)
resp.status = falcon.HTTP_200
class AppSociosRegimenes(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.socios_regimenes_get(values, user)
resp.status = falcon.HTTP_200

View File

@ -1,622 +0,0 @@
#!/usr/bin/env python
#~ import re
#~ from xml.etree import ElementTree as ET
#~ from requests import Request, Session, exceptions
import datetime
import hashlib
import os
import requests
import time
from lxml import etree
from xml.dom.minidom import parseString
from xml.sax.saxutils import escape, unescape
from uuid import UUID
from logbook import Logger
from zeep import Client
from zeep.plugins import HistoryPlugin
from zeep.cache import SqliteCache
from zeep.transports import Transport
from zeep.exceptions import Fault, TransportError
if __name__ == '__main__':
from configpac import DEBUG, TIMEOUT, AUTH, URL
else:
from .configpac import DEBUG, TIMEOUT, AUTH, URL
log = Logger('PAC')
#~ node = client.create_message(client.service, SERVICE, **args)
#~ print(etree.tostring(node, pretty_print=True).decode())
class Ecodex(object):
def __init__(self):
self.codes = URL['codes']
self.error = ''
self.message = ''
self._transport = Transport(cache=SqliteCache(), timeout=TIMEOUT)
self._plugins = None
self._history = None
if DEBUG:
self._history = HistoryPlugin()
self._plugins = [self._history]
def _get_token(self, rfc):
client = Client(URL['seguridad'],
transport=self._transport, plugins=self._plugins)
try:
result = client.service.ObtenerToken(rfc, self._get_epoch())
except Fault as e:
self.error = str(e)
log.error(self.error)
return ''
s = '{}|{}'.format(AUTH['ID'], result.Token)
return hashlib.sha1(s.encode()).hexdigest()
def _get_token_rest(self, rfc):
data = {
'rfc': rfc,
'grant_type': 'authorization_token',
}
headers = {'Content-type': 'application/x-www-form-urlencoded'}
result = requests.post(URL['token'], data=data, headers=headers)
data = result.json()
s = '{}|{}'.format(AUTH['ID'], data['service_token'])
return hashlib.sha1(s.encode()).hexdigest(), data['access_token']
def _validate_xml(self, xml):
NS_CFDI = {'cfdi': 'http://www.sat.gob.mx/cfd/3'}
if os.path.isfile(xml):
tree = etree.parse(xml).getroot()
else:
tree = etree.fromstring(xml.encode())
fecha = tree.get('Fecha')
rfc = tree.xpath('string(//cfdi:Emisor/@Rfc)', namespaces=NS_CFDI)
data = {
'ComprobanteXML': etree.tostring(tree).decode(),
'RFC': rfc,
'Token': self._get_token(rfc),
'TransaccionID': self._get_epoch(fecha),
}
return data
def _get_by_hash(self, sh, rfc):
token, access_token = self._get_token_rest(rfc)
url = URL['hash'].format(sh)
headers = {
'Authorization': 'Bearer {}'.format(access_token),
'X-Auth-Token': token,
}
result = requests.get(url, headers=headers)
if result.status_code == 200:
print (result.json())
return
def timbra_xml(self, xml):
data = self._validate_xml(xml)
client = Client(URL['timbra'],
transport=self._transport, plugins=self._plugins)
try:
result = client.service.TimbraXML(**data)
except Fault as e:
error = str(e)
if self.codes['HASH'] in error:
sh = error.split(' ')[3]
return self._get_by_hash(sh[:40], data['RFC'])
self.error = error
return ''
tree = parseString(result.ComprobanteXML.DatosXML)
xml = tree.toprettyxml(encoding='utf-8').decode('utf-8')
return xml
def _get_epoch(self, date=None):
if isinstance(date, str):
f = '%Y-%m-%dT%H:%M:%S'
e = int(time.mktime(time.strptime(date, f)))
else:
date = datetime.datetime.now()
e = int(time.mktime(date.timetuple()))
return e
def estatus_cuenta(self, rfc):
#~ Codigos:
#~ 100 = Cuenta encontrada
#~ 101 = RFC no dado de alta en el sistema ECODEX
token = self._get_token(rfc)
if not token:
return {}
data = {
'RFC': rfc,
'Token': token,
'TransaccionID': self._get_epoch()
}
client = Client(URL['clients'],
transport=self._transport, plugins=self._plugins)
try:
result = client.service.EstatusCuenta(**data)
except Fault as e:
log.error(str(e))
return
#~ print (result)
return result.Estatus
class Finkok(object):
def __init__(self, auth={}):
self.codes = URL['codes']
self.error = ''
self.message = ''
self._transport = Transport(cache=SqliteCache(), timeout=TIMEOUT)
self._plugins = None
self._history = None
self.uuid = ''
self.fecha = None
if DEBUG:
self._history = HistoryPlugin()
self._plugins = [self._history]
self._auth = AUTH
else:
self._auth = auth
def _debug(self):
if not DEBUG:
return
print('SEND', self._history.last_sent)
print('RESULT', self._history.last_received)
return
def _check_result(self, method, result):
#~ print ('CODE', result.CodEstatus)
#~ print ('INCIDENCIAS', result.Incidencias)
self.message = ''
MSG = {
'OK': 'Comprobante timbrado satisfactoriamente',
'307': 'Comprobante timbrado previamente',
}
status = result.CodEstatus
if status is None and result.Incidencias:
for i in result.Incidencias['Incidencia']:
self.error += 'Error: {}\n{}'.format(
i['CodigoError'], i['MensajeIncidencia'])
return ''
if method == 'timbra' and status in (MSG['OK'], MSG['307']):
#~ print ('UUID', result.UUID)
#~ print ('FECHA', result.Fecha)
if status == MSG['307']:
self.message = MSG['307']
tree = parseString(result.xml)
response = tree.toprettyxml(encoding='utf-8').decode('utf-8')
self.uuid = result.UUID
self.fecha = result.Fecha
return response
def _load_file(self, path):
try:
with open(path, 'rb') as f:
data = f.read()
except Exception as e:
self.error = str(e)
return
return data
def _validate_xml(self, file_xml):
if os.path.isfile(file_xml):
try:
with open(file_xml, 'rb') as f:
xml = f.read()
except Exception as e:
self.error = str(e)
return False, ''
else:
xml = file_xml.encode('utf-8')
return True, xml
def _validate_uuid(self, uuid):
try:
UUID(uuid)
return True
except ValueError:
self.error = 'UUID no válido: {}'.format(uuid)
return False
def timbra_xml(self, file_xml):
self.error = ''
if not DEBUG and not self._auth:
self.error = 'Sin datos para timbrar'
return
method = 'timbra'
ok, xml = self._validate_xml(file_xml)
if not ok:
return ''
client = Client(
URL[method], transport=self._transport, plugins=self._plugins)
args = {
'username': self._auth['USER'],
'password': self._auth['PASS'],
'xml': xml,
}
if URL['quick_stamp']:
try:
result = client.service.quick_stamp(**args)
except Fault as e:
self.error = str(e)
return
else:
try:
result = client.service.stamp(**args)
except Fault as e:
self.error = str(e)
return
return self._check_result(method, result)
def _get_xml(self, uuid):
if not self._validate_uuid(uuid):
return ''
method = 'util'
client = Client(
URL[method], transport=self._transport, plugins=self._plugins)
args = {
'username': self._auth['USER'],
'password': self._auth['PASS'],
'uuid': uuid,
'taxpayer_id': self.rfc,
'invoice_type': 'I',
}
try:
result = client.service.get_xml(**args)
except Fault as e:
self.error = str(e)
return ''
except TransportError as e:
self.error = str(e)
return ''
if result.error:
self.error = result.error
return ''
tree = parseString(result.xml)
xml = tree.toprettyxml(encoding='utf-8').decode('utf-8')
return xml
def recupera_xml(self, file_xml='', uuid=''):
self.error = ''
if uuid:
return self._get_xml(uuid)
method = 'timbra'
ok, xml = self._validate_xml(file_xml)
if not ok:
return ''
client = Client(
URL[method], transport=self._transport, plugins=self._plugins)
try:
result = client.service.stamped(
xml, self._auth['user'], self._auth['pass'])
except Fault as e:
self.error = str(e)
return ''
return self._check_result(method, result)
def estatus_xml(self, uuid):
method = 'timbra'
if not self._validate_uuid(uuid):
return ''
client = Client(
URL[method], transport=self._transport, plugins=self._plugins)
try:
result = client.service.query_pending(
self._auth['USER'], self._auth['PASS'], uuid)
#~ print (result.date)
#~ tree = parseString(unescape(result.xml))
#~ response = tree.toprettyxml(encoding='utf-8').decode('utf-8')
return result.status
except Fault as e:
self.error = str(e)
return ''
def cancel_xml(self, rfc, uuids, path_cer, path_key):
for u in uuids:
if not self._validate_uuid(u):
return ''
cer = self._load_file(path_cer)
key = self._load_file(path_key)
method = 'cancel'
client = Client(
URL[method], transport=self._transport, plugins=self._plugins)
uuid_type = client.get_type('ns1:UUIDS')
sa = client.get_type('ns0:stringArray')
args = {
'UUIDS': uuid_type(uuids=sa(string=uuids)),
'username': self._auth['USER'],
'password': self._auth['PASS'],
'taxpayer_id': rfc,
'cer': cer,
'key': key,
'store_pending': True,
}
try:
result = client.service.cancel(**args)
except Fault as e:
self.error = str(e)
return ''
if result.CodEstatus and self.codes['205'] in result.CodEstatus:
self.error = result.CodEstatus
return ''
return result
def cancel_signature(self, file_xml):
method = 'cancel'
if os.path.isfile(file_xml):
root = etree.parse(file_xml).getroot()
else:
root = etree.fromstring(file_xml)
xml = etree.tostring(root)
client = Client(
URL[method], transport=self._transport, plugins=self._plugins)
args = {
'username': self._auth['USER'],
'password': self._auth['PASS'],
'xml': xml,
'store_pending': True,
}
result = client.service.cancel_signature(**args)
return result
def get_acuse(self, rfc, uuids, type_acuse='C'):
for u in uuids:
if not self._validate_uuid(u):
return ''
method = 'cancel'
client = Client(
URL[method], transport=self._transport, plugins=self._plugins)
args = {
'username': self._auth['USER'],
'password': self._auth['PASS'],
'taxpayer_id': rfc,
'uuid': '',
'type': type_acuse,
}
try:
result = []
for u in uuids:
args['uuid'] = u
r = client.service.get_receipt(**args)
result.append(r)
except Fault as e:
self.error = str(e)
return ''
return result
def estatus_cancel(self, uuids):
for u in uuids:
if not self._validate_uuid(u):
return ''
method = 'cancel'
client = Client(
URL[method], transport=self._transport, plugins=self._plugins)
args = {
'username': self._auth['USER'],
'password': self._auth['PASS'],
'uuid': '',
}
try:
result = []
for u in uuids:
args['uuid'] = u
r = client.service.query_pending_cancellation(**args)
result.append(r)
except Fault as e:
self.error = str(e)
return ''
return result
def add_token(self, rfc, email):
"""
Se requiere cuenta de reseller para usar este método
"""
method = 'util'
client = Client(
URL[method], transport=self._transport, plugins=self._plugins)
args = {
'username': AUTH['USER'],
'password': AUTH['PASS'],
'name': rfc,
'token_username': email,
'taxpayer_id': rfc,
'status': True,
}
result = client.service.add_token(**args)
return result
def get_date(self):
method = 'util'
client = Client(
URL[method], transport=self._transport, plugins=self._plugins)
try:
result = client.service.datetime(AUTH['USER'], AUTH['PASS'])
except Fault as e:
self.error = str(e)
return ''
if result.error:
self.error = result.error
return
return result.datetime
def add_client(self, rfc, type_user=False):
"""
Se requiere cuenta de reseller para usar este método
type_user: False == 'P' == Prepago or True == 'O' == On demand
"""
tu = {False: 'P', True: 'O'}
method = 'client'
client = Client(
URL[method], transport=self._transport, plugins=self._plugins)
args = {
'reseller_username': AUTH['USER'],
'reseller_password': AUTH['PASS'],
'taxpayer_id': rfc,
'type_user': tu[type_user],
'added': datetime.datetime.now().isoformat()[:19],
}
try:
result = client.service.add(**args)
except Fault as e:
self.error = str(e)
return ''
return result
def edit_client(self, rfc, status=True):
"""
Se requiere cuenta de reseller para usar este método
status = 'A' or 'S'
"""
sv = {False: 'S', True: 'A'}
method = 'client'
client = Client(
URL[method], transport=self._transport, plugins=self._plugins)
args = {
'reseller_username': AUTH['USER'],
'reseller_password': AUTH['PASS'],
'taxpayer_id': rfc,
'status': sv[status],
}
try:
result = client.service.edit(**args)
except Fault as e:
self.error = str(e)
return ''
return result
def get_client(self, rfc):
"""
Se requiere cuenta de reseller para usar este método
"""
method = 'client'
client = Client(
URL[method], transport=self._transport, plugins=self._plugins)
args = {
'reseller_username': AUTH['USER'],
'reseller_password': AUTH['PASS'],
'taxpayer_id': rfc,
}
try:
result = client.service.get(**args)
except Fault as e:
self.error = str(e)
return ''
except TransportError as e:
self.error = str(e)
return ''
return result
def assign_client(self, rfc, credit):
"""
Se requiere cuenta de reseller para usar este método
"""
method = 'client'
client = Client(
URL[method], transport=self._transport, plugins=self._plugins)
args = {
'username': AUTH['USER'],
'password': AUTH['PASS'],
'taxpayer_id': rfc,
'credit': credit,
}
try:
result = client.service.assign(**args)
except Fault as e:
self.error = str(e)
return ''
return result
def _get_data_sat(path):
BF = 'string(//*[local-name()="{}"]/@{})'
NS_CFDI = {'cfdi': 'http://www.sat.gob.mx/cfd/3'}
try:
if os.path.isfile(path):
tree = etree.parse(path).getroot()
else:
tree = etree.fromstring(path)
data = {}
emisor = escape(
tree.xpath('string(//cfdi:Emisor/@rfc)', namespaces=NS_CFDI) or
tree.xpath('string(//cfdi:Emisor/@Rfc)', namespaces=NS_CFDI)
)
receptor = escape(
tree.xpath('string(//cfdi:Receptor/@rfc)', namespaces=NS_CFDI) or
tree.xpath('string(//cfdi:Receptor/@Rfc)', namespaces=NS_CFDI)
)
data['total'] = tree.get('total') or tree.get('Total')
data['emisor'] = emisor
data['receptor'] = receptor
data['uuid'] = tree.xpath(BF.format('TimbreFiscalDigital', 'UUID'))
except Exception as e:
print (e)
return {}
return '?re={emisor}&rr={receptor}&tt={total}&id={uuid}'.format(**data)
def get_status_sat(xml):
data = _get_data_sat(xml)
if not data:
return
URL = 'https://consultaqr.facturaelectronica.sat.gob.mx/ConsultaCFDIService.svc?wsdl'
client = Client(URL, transport=Transport(cache=SqliteCache()))
try:
result = client.service.Consulta(expresionImpresa=data)
except Exception as e:
return 'Error: {}'.format(str(e))
return result.Estado
def main():
return
if __name__ == '__main__':
main()

View File

@ -0,0 +1,4 @@
#!/usr/bin/env python
from .comerciodigital import PACComercioDigital
from .finkok import PACFinkok

View File

@ -0,0 +1,350 @@
#!/usr/bin/env python3
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
from cryptography import x509
from cryptography.x509.oid import NameOID
from cryptography.x509.oid import ExtensionOID
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import padding
from conf import TOKEN
class SATCertificate(object):
def __init__(self, cer=b'', key=b'', password=''):
self._error = ''
self._init_values()
self._get_data_cer(cer)
self._get_data_key(key, password)
# ~ if not password:
# ~ self._test()
# ~ def _test(self):
# ~ key = self._get_key('')
# ~ self._p = ''
# ~ self._key_der = key.private_bytes(
# ~ encoding=serialization.Encoding.DER,
# ~ format=serialization.PrivateFormat.PKCS8,
# ~ encryption_algorithm=serialization.BestAvailableEncryption(self._p.encode())
# ~ )
# ~ return
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
self._are_couple = False
self._is_valid_time = False
self._key = b''
self._cer = b''
self._cer_pem = ''
self._cer_txt = ''
self._key_enc = b''
self._key_der = b''
self._p12 = b''
self._cer_modulus = 0
self._key_modulus = 0
return
def __str__(self):
msg = '\tRFC: {}\n'.format(self.rfc)
msg += '\tNo de Serie: {}\n'.format(self.serial_number)
msg += '\tVálido desde: {}\n'.format(self.not_before)
msg += '\tVálido hasta: {}\n'.format(self.not_after)
msg += '\tEs vigente: {}\n'.format(self.is_valid_time)
msg += '\tSon pareja: {}\n'.format(self.are_couple)
msg += '\tEs FIEL: {}\n'.format(self.is_fiel)
return msg
def __bool__(self):
return self.is_valid
def _get_hash(self):
digest = hashes.Hash(hashes.SHA512(), default_backend())
digest.update(self._rfc.encode())
digest.update(self._serial_number.encode())
digest.update(TOKEN.encode())
return digest.finalize()
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_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()
self._is_valid_time = (now > self.not_before) and (now < self.not_after)
if not self._is_valid_time:
msg = 'El certificado no es vigente'
self._error = msg
self._is_fiel = obj.extensions.get_extension_for_oid(
ExtensionOID.KEY_USAGE).value.key_agreement
self._cer_pem = obj.public_bytes(serialization.Encoding.PEM).decode()
self._cer_txt = ''.join(self._cer_pem.split('\n')[1:-2])
self._cer_modulus = obj.public_key().public_numbers().n
return
def _get_data_key(self, key, password):
self._key = key
self._keyp = password
self._key_enc = key
if not key or not password:
return
try:
obj = serialization.load_der_private_key(
key, password.encode(), default_backend())
except ValueError:
msg = 'La contraseña es incorrecta'
self._error = msg
return
p = self._get_hash()
self._key_enc = obj.private_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PrivateFormat.PKCS8,
encryption_algorithm=serialization.BestAvailableEncryption(p)
)
self._key_modulus = obj.public_key().public_numbers().n
self._are_couple = self._cer_modulus == self._key_modulus
if not self._are_couple:
msg = 'El CER y el KEY no son pareja'
self._error = msg
return
def _get_key(self, password):
if not password:
password = self._get_hash()
private_key = serialization.load_pem_private_key(
self._key_enc, password=password, backend=default_backend())
return private_key
def _get_key_pem(self):
obj = self._get_key('')
key_pem = obj.private_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PrivateFormat.PKCS8,
encryption_algorithm=serialization.NoEncryption()
)
return key_pem
# Not work
def _get_p12(self):
obj = serialization.pkcs12.serialize_key_and_certificates('test',
self.key_pem, self.cer_pem, None,
encryption_algorithm=serialization.NoEncryption()
)
return obj
def sign(self, data, password=''):
private_key = self._get_key(password)
firma = private_key.sign(data, padding.PKCS1v15(), hashes.SHA256())
return base64.b64encode(firma).decode()
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)
ctx.key = key
ctx.sign(node)
node = xmlsec.tree.find_node(tree, 'X509Certificate')
node.text = self.cer_txt
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', '')
node = xmlsec.tree.find_node(tree, 'Modulus')
node.text = node.text.replace('\n', '')
# ~ xml_signed = ET.tostring(tree,
# ~ xml_declaration=True, encoding='UTF-8').decode()
xml_signed = ET.tostring(tree, encoding='UTF-8').decode().replace('\n', '')
return xml_signed
@property
def rfc(self):
return self._rfc
@property
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
@property
def not_after(self):
return self._not_after
@property
def is_fiel(self):
return self._is_fiel
@property
def are_couple(self):
return self._are_couple
@property
def is_valid(self):
return not bool(self.error)
@property
def is_valid_time(self):
return self._is_valid_time
@property
def cer(self):
return self._cer
@property
def cer_pem(self):
return self._cer_pem.encode()
@property
def cer_txt(self):
return self._cer_txt
@property
def key_pem(self):
return self._get_key_pem()
@property
def key_enc(self):
return self._key_enc
@property
def p12(self):
return self._get_p12()
@property
def error(self):
return self._error
@classmethod
def save_cert(cls, obj):
import hashlib
cer = x509.load_pem_x509_certificate(obj.cer_pem.encode(), default_backend())
cer_der = cer.public_bytes(serialization.Encoding.DER)
token = hashlib.md5(obj.rfc.encode()).hexdigest()
path = Path(f'/tmp/{obj.rfc}.key')
path.write_text(obj.key_enc)
args = f'openssl rsa -inform PEM -outform PEM -in "{str(path)}" -passin pass:{token}'
pem = subprocess.check_output(args, shell=True).decode()
key = serialization.load_pem_private_key(
pem.encode(), password=None, backend=default_backend())
digest = hashes.Hash(hashes.SHA512(), default_backend())
digest.update(obj.rfc.encode())
digest.update(obj.serie.encode())
digest.update(TOKEN.encode())
p = digest.finalize()
key_enc = key.private_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PrivateFormat.PKCS8,
encryption_algorithm=serialization.BestAvailableEncryption(p)
)
cert = SATCertificate(cer_der, key_enc)
obj.key_enc = cert.key_enc
obj.cer = cert.cer
obj.serie = cert.serial_number
obj.desde = cert.not_before
obj.hasta = cert.not_after
obj.save()
return
def main(args):
# ~ contra = getpass.getpass('Introduce la contraseña del archivo KEY: ')
contra = '12345678a'
if not contra.strip():
msg = 'La contraseña es requerida'
print(msg)
return
path_cer = Path(args.cer)
path_key = Path(args.key)
if not path_cer.is_file():
msg = 'El archivo CER es necesario'
print(msg)
return
if not path_key.is_file():
msg = 'El archivo KEY es necesario'
print(msg)
return
cer = path_cer.read_bytes()
key = path_key.read_bytes()
cert = SATCertificate(cer, key, contra)
if cert.error:
print(cert.error)
else:
print(cert)
return
def _process_command_line_arguments():
parser = argparse.ArgumentParser(description='CFDI Certificados')
help = 'Archivo CER'
parser.add_argument('-c', '--cer', help=help, default='')
help = 'Archivo KEY'
parser.add_argument('-k', '--key', help=help, default='')
args = parser.parse_args()
return args
if __name__ == '__main__':
args = _process_command_line_arguments()
main(args)

View File

@ -0,0 +1,3 @@
#!/usr/bin/env python3
from .comercio import PACComercioDigital

View File

@ -0,0 +1,431 @@
#!/usr/bin/env python
# ~
# ~ PAC
# ~ Copyright (C) 2018-2019 Mauricio Baeza Servin - public [AT] elmau [DOT] 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
# ~ the Free Software Foundation, either version 3 of the License, or
# ~ (at your option) any later version.
# ~
# ~ This program is distributed in the hope that it will be useful,
# ~ but WITHOUT ANY WARRANTY; without even the implied warranty of
# ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# ~ GNU General Public License for more details.
# ~
# ~ 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 logging
import lxml.etree as ET
import requests
from requests.exceptions import ConnectionError
from .conf import DEBUG
# ~ , AUTH
LOG_FORMAT = '%(asctime)s - %(levelname)s - %(message)s'
LOG_DATE = '%d/%m/%Y %H:%M:%S'
logging.addLevelName(logging.ERROR, '\033[1;41mERROR\033[1;0m')
logging.addLevelName(logging.DEBUG, '\x1b[33mDEBUG\033[1;0m')
logging.addLevelName(logging.INFO, '\x1b[32mINFO\033[1;0m')
logging.basicConfig(level=logging.DEBUG, format=LOG_FORMAT, datefmt=LOG_DATE)
log = logging.getLogger(__name__)
logging.getLogger('requests').setLevel(logging.ERROR)
TIMEOUT = 10
NAMESPACES = {
'3.3': 'http://www.sat.gob.mx/cfd/3',
'4.0': 'http://www.sat.gob.mx/cfd/4',
'tdf': 'http://www.sat.gob.mx/TimbreFiscalDigital',
}
def pretty_print_POST(req):
"""
At this point it is completely built and ready
to be fired; it is "prepared".
However pay attention at the formatting used in
this function because it is programmed to be pretty
printed and may differ from the actual request.
"""
print('{}\n{}\r\n{}\r\n\r\n{}'.format(
'-----------START-----------',
req.method + ' ' + req.url,
'\r\n'.join('{}: {}'.format(k, v) for k, v in req.headers.items()),
req.body,
))
class PACComercioDigital(object):
ws = 'https://{}.comercio-digital.mx/{}'
api = 'https://app2.comercio-digital.mx/{}'
URL = {
'timbra': ws.format('ws', 'timbre4/timbrarV5'),
'cancel': ws.format('cancela', 'cancela4/cancelarUuid'),
'cancelxml': ws.format('cancela', 'cancela4/cancelarXml'),
'status': ws.format('cancela', 'arws/consultaEstatus'),
'client': api.format('x3/altaEmpresa'),
'saldo': api.format('x3/saldo'),
'timbres': api.format('x3/altaTimbres'),
}
CODES = {
'000': '000 Exitoso',
'004': '004 RFC {} ya esta dado de alta con Estatus=A',
'704': '704 Usuario Invalido',
'702': '702 Error rfc/empresa invalido',
}
NS_CFDI = {
'cfdi': 'http://www.sat.gob.mx/cfd/4',
'tdf': 'http://www.sat.gob.mx/TimbreFiscalDigital',
}
if DEBUG:
ws = 'https://pruebas.comercio-digital.mx/{}'
ws6 = 'https://pruebas6.comercio-digital.mx/arws/{}'
URL = {
'timbra': ws.format('timbre4/timbrarV5'),
'cancel': ws.format('cancela4/cancelarUuid'),
'cancelxml': ws.format('cancela4/cancelarXml'),
'status': ws6.format('consultaEstatus'),
'client': api.format('x3/altaEmpresa'),
'saldo': api.format('x3/saldo'),
'timbres': api.format('x3/altaTimbres'),
}
def __init__(self):
self.error = ''
def _error(self, msg):
self.error = str(msg)
log.error(msg)
return
def _post(self, url, data, headers={}):
result = None
headers['host'] = url.split('/')[2]
headers['Content-type'] = 'text/plain'
headers['Connection'] = 'Keep-Alive'
headers['Expect'] = '100-continue'
if DEBUG:
req = requests.Request('POST', url, headers=headers, data=data)
prepared = req.prepare()
pretty_print_POST(prepared)
try:
result = requests.post(url, data=data, headers=headers, timeout=TIMEOUT)
except ConnectionError as e:
self._error(e)
return result
def _validate_cfdi(self, xml):
"""
Comercio Digital solo soporta la declaración con doble comilla
"""
# ~ tree = ET.fromstring(xml.encode())
# ~ xml = ET.tostring(tree,
# ~ pretty_print=True, doctype='<?xml version="1.0" encoding="utf-8"?>')
return xml.encode('utf-8')
def stamp(self, cfdi, auth):
# ~ if DEBUG or not auth:
# ~ auth = AUTH
url = self.URL['timbra']
headers = {
'usrws': auth['user'],
'pwdws': auth['pass'],
'tipo': 'XML',
}
cfdi = self._validate_cfdi(cfdi)
result = self._post(url, cfdi, headers)
if result is None:
return ''
if result.status_code != 200:
return ''
if 'errmsg' in result.headers:
self._error(result.headers['errmsg'])
return ''
xml = result.content
tree = ET.fromstring(xml)
cfdi_uuid = tree.xpath(
'string(//cfdi:Complemento/tdf:TimbreFiscalDigital/@UUID)',
namespaces=self.NS_CFDI)
date_stamped = tree.xpath(
'string(//cfdi:Complemento/tdf:TimbreFiscalDigital/@FechaTimbrado)',
namespaces=self.NS_CFDI)
data = {
'xml': xml.decode(),
'uuid': cfdi_uuid,
'date': date_stamped,
}
return data
def _get_data_cancel(self, cfdi, info, auth):
info['tipo'] = 'cfdi'
info['key'] = base64.b64encode(info['key_enc']).decode()
info['cer'] = base64.b64encode(info['cer_ori']).decode()
tree = ET.fromstring(cfdi.encode())
version = tree.attrib['Version']
namespaces = {
'cfdi': NAMESPACES[version],
'tdf': NAMESPACES['tdf'],
}
tipo = tree.xpath(
'string(//cfdi:Comprobante/@TipoDeComprobante)',
namespaces=namespaces)
total = tree.xpath(
'string(//cfdi:Comprobante/@Total)',
namespaces=namespaces)
rfc_emisor = tree.xpath(
'string(//cfdi:Comprobante/cfdi:Emisor/@Rfc)',
namespaces=namespaces)
rfc_receptor = tree.xpath(
'string(//cfdi:Comprobante/cfdi:Receptor/@Rfc)',
namespaces=namespaces)
uid = tree.xpath(
'string(//cfdi:Complemento/tdf:TimbreFiscalDigital/@UUID)',
namespaces=namespaces)
data = (
f"USER={auth['user']}",
f"PWDW={auth['pass']}",
f"RFCE={rfc_emisor}",
f"UUID={uid}",
f"PWDK={info['pass']}",
f"KEYF={info['key']}",
f"CERT={info['cer']}",
f"TIPO1={info['tipo']}",
f"ACUS=SI",
f"RFCR={rfc_receptor}",
f"TIPOC={tipo}",
f"TOTAL={total}",
f"UUIDREL={info['args']['uuid']}",
f"MOTIVO={info['args']['reason']}",
)
return '\n'.join(data)
def cancel(self, cfdi, info, auth):
# ~ if DEBUG or not auth:
# ~ auth = AUTH
url = self.URL['cancel']
data = self._get_data_cancel(cfdi, info, auth)
result = self._post(url, data)
if result is None:
return ''
if result.status_code != 200:
return ''
if result.headers['codigo'] != '000':
self._error(result.headers['errmsg'])
return ''
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 = {
'cfdi': 'http://www.sat.gob.mx/cfd/3',
'tdf': 'http://www.sat.gob.mx/TimbreFiscalDigital',
}
tree = ET.fromstring(cfdi.encode())
tipocfdi = tree.xpath(
'string(//cfdi:Comprobante/@TipoDeComprobante)',
namespaces=NS_CFDI)
total = tree.xpath(
'string(//cfdi:Comprobante/@Total)',
namespaces=NS_CFDI)
rfc_receptor = tree.xpath(
'string(//cfdi:Comprobante/cfdi:Receptor/@Rfc)',
namespaces=NS_CFDI)
headers = {
'usrws': auth['user'],
'pwdws': auth['pass'],
'rfcr': rfc_receptor,
'total': total,
'tipocfdi': tipocfdi,
}
headers.update(info)
return headers
def cancel_xml(self, xml, auth, cfdi='', info={'tipo': 'cfdi'}):
# ~ if DEBUG or not auth:
# ~ auth = AUTH
url = self.URL['cancelxml']
headers = self._get_headers_cancel_xml(cfdi, info, auth)
result = self._post(url, xml, headers)
if result is None:
return ''
if result.status_code != 200:
return ''
if result.headers['codigo'] != '000':
self._error(result.headers['errmsg'])
return ''
tree = ET.fromstring(result.text)
date_cancel = tree.xpath('string(//Acuse/@Fecha)')[:19]
data = {
'acuse': result.text,
'date': date_cancel,
}
return data
def status(self, data, auth):
# ~ if not auth:
# ~ auth = AUTH
url = self.URL['status']
data = (
f"USER={auth['user']}",
f"PWDW={auth['pass']}",
f"RFCR={data['rfc_receptor']}",
f"RFCE={data['rfc_emisor']}",
f"TOTAL={data['total']}",
f"UUID={data['uuid']}",
)
data = '\n'.join(data)
result = self._post(url, data)
if result is None:
return ''
if result.status_code != 200:
self._error(result.status_code)
return self.error
return result.text
def _get_data_client(self, auth, values):
data = [f"usr_ws={auth['user']}", f"pwd_ws={auth['pass']}"]
fields = (
'rfc_contribuyente',
'nombre_contribuyente',
'calle',
'noExterior',
'noInterior',
'colonia',
'localidad',
'municipio',
'estado',
'pais',
'cp',
'contacto',
'telefono',
'email',
'rep_nom',
'rep_rfc',
'email_fact',
'pwd_asignado',
)
data += [f"{k}={values[k]}" for k in fields]
return '\n'.join(data)
def client_add(self, data, auth):
# ~ auth = AUTH
url = self.URL['client']
data = self._get_data_client(auth, data)
result = self._post(url, data)
if result is None:
return False
if result.status_code != 200:
self._error(f'Code: {result.status_code}')
return False
if result.text != self.CODES['000']:
self._error(result.text)
return False
return True
def client_balance(self, data, rfc=''):
url = self.URL['saldo']
host = url.split('/')[2]
headers = {
'Content-type': 'text/plain',
'Host': host,
'Connection' : 'Keep-Alive',
}
data = {'usr': data['user'], 'pwd': data['pass']}
try:
result = requests.get(url, params=data, headers=headers, timeout=TIMEOUT)
except ConnectionError as e:
self._error(e)
return ''
if result.status_code != 200:
return ''
if result.text == self.CODES['704']:
self._error(result.text)
return ''
if result.text == self.CODES['702']:
self._error(result.text)
return ''
return result.text
def client_add_timbres(self, data, auth):
# ~ if not auth:
# ~ auth = AUTH
url = self.URL['timbres']
data = '\n'.join((
f"usr_ws={auth['user']}",
f"pwd_ws={auth['pass']}",
f"rfc_recibir={data['rfc']}",
f"num_timbres={data['timbres']}"
))
result = self._post(url, data)
if result is None:
return False
if result.status_code != 200:
self._error(f'Code: {result.status_code}')
return False
if result.text != self.CODES['000']:
self._error(result.text)
return False
return True

View File

@ -0,0 +1,38 @@
#!/usr/bin/env python
# ~
# ~ PAC
# ~ Copyright (C) 2018-2019 Mauricio Baeza Servin - public [AT] elmau [DOT] 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
# ~ the Free Software Foundation, either version 3 of the License, or
# ~ (at your option) any later version.
# ~
# ~ This program is distributed in the hope that it will be useful,
# ~ but WITHOUT ANY WARRANTY; without even the implied warranty of
# ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# ~ GNU General Public License for more details.
# ~
# ~ You should have received a copy of the GNU General Public License
# ~ along with this program. If not, see <http://www.gnu.org/licenses/>.
# ~ Siempre consulta la documentación de PAC
# ~ AUTH = Las credenciales de timbrado proporcionadas por el PAC
# ~ NO cambies las credenciales de prueba
DEBUG = False
AUTH = {
'user': '',
'pass': '',
}
if DEBUG:
AUTH = {
'user': 'AAA010101AAA',
'pass': 'PWD',
}

View File

@ -0,0 +1,3 @@
#!/usr/bin/env python3
from .finkok import PACFinkok

View File

@ -0,0 +1,46 @@
#!/usr/bin/env python
# ~
# ~ PAC
# ~ Copyright (C) 2018-2019 Mauricio Baeza Servin - public [AT] elmau [DOT] 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
# ~ the Free Software Foundation, either version 3 of the License, or
# ~ (at your option) any later version.
# ~
# ~ This program is distributed in the hope that it will be useful,
# ~ but WITHOUT ANY WARRANTY; without even the implied warranty of
# ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# ~ GNU General Public License for more details.
# ~
# ~ You should have received a copy of the GNU General Public License
# ~ along with this program. If not, see <http://www.gnu.org/licenses/>.
# ~ Siempre consulta la documentación de PAC
# ~ AUTH = Las credenciales de timbrado proporcionadas por el PAC
# ~ NO cambies las credenciales de prueba
DEBUG = False
AUTH = {
'user': '',
'pass': '',
'RESELLER': {
'user': '',
'pass': ''
}
}
if DEBUG:
AUTH = {
'user': 'pruebas-finkok@correolibre.net',
'pass': '5c9a88da105bff9a8c430cb713f6d35269f51674bdc5963c1501b7316366',
'RESELLER': {
'user': '',
'pass': ''
}
}

View File

@ -0,0 +1,580 @@
#!/usr/bin/env python
# ~
# ~ PAC
# ~ Copyright (C) 2018-2019 Mauricio Baeza Servin - public [AT] elmau [DOT] 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
# ~ the Free Software Foundation, either version 3 of the License, or
# ~ (at your option) any later version.
# ~
# ~ This program is distributed in the hope that it will be useful,
# ~ but WITHOUT ANY WARRANTY; without even the implied warranty of
# ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# ~ GNU General Public License for more details.
# ~
# ~ 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 datetime
import logging
import os
import re
from io import BytesIO
from xml.sax.saxutils import unescape
import lxml.etree as ET
from zeep import Client
from zeep.plugins import Plugin
from zeep.cache import SqliteCache
from zeep.transports import Transport
from zeep.exceptions import Fault, TransportError
from requests.exceptions import ConnectionError
from .conf import DEBUG, AUTH
LOG_FORMAT = '%(asctime)s - %(levelname)s - %(message)s'
LOG_DATE = '%d/%m/%Y %H:%M:%S'
logging.addLevelName(logging.ERROR, '\033[1;41mERROR\033[1;0m')
logging.addLevelName(logging.DEBUG, '\x1b[33mDEBUG\033[1;0m')
logging.addLevelName(logging.INFO, '\x1b[32mINFO\033[1;0m')
logging.basicConfig(level=logging.DEBUG, format=LOG_FORMAT, datefmt=LOG_DATE)
log = logging.getLogger(__name__)
logging.getLogger('requests').setLevel(logging.ERROR)
logging.getLogger('zeep').setLevel(logging.ERROR)
TIMEOUT = 10
DEBUG_SOAP = False
class DebugPlugin(Plugin):
def _to_string(self, envelope, name):
if DEBUG_SOAP:
data = ET.tostring(envelope, pretty_print=True, encoding='utf-8').decode()
path = f'/tmp/soap_{name}.xml'
with open(path, 'w') as f:
f.write(data)
# ~ print(data)
return
def egress(self, envelope, http_headers, operation, binding_options):
self._to_string(envelope, 'request')
return envelope, http_headers
def ingress(self, envelope, http_headers, operation):
self._to_string(envelope, 'response')
return envelope, http_headers
class PACFinkok(object):
WS = 'https://facturacion.finkok.com/servicios/soap/{}.wsdl'
NS_TYPE = 'ns1'
if DEBUG:
WS = 'https://demo-facturacion.finkok.com/servicios/soap/{}.wsdl'
NS_TYPE = 'ns0'
URL = {
'quick_stamp': False,
'timbra': WS.format('stamp'),
'cancel': WS.format('cancel'),
'client': WS.format('registration'),
'util': WS.format('utilities'),
}
CODE = {
'200': 'Comprobante timbrado satisfactoriamente',
'205': 'No Encontrado',
'307': 'Comprobante timbrado previamente',
'702': 'No se encontro el RFC del emisor',
'IP': 'Invalid Passphrase',
'IPMSG': 'Frase de paso inválida',
'NE': 'No Encontrado',
}
def __init__(self):
self._error = ''
self._transport = Transport(cache=SqliteCache(), timeout=TIMEOUT)
self._plugins = [DebugPlugin()]
@property
def error(self):
return self._error
def _validate_result(self, result):
if hasattr(result, 'Incidencias') and not result.Incidencias is None:
fault = result.Incidencias.Incidencia[0]
cod_error = fault.CodigoError.encode('utf-8').decode()
msg_error = fault.MensajeIncidencia.encode('utf-8').decode()
error = 'Error: {}\n{}'.format(cod_error, msg_error)
if cod_error == '307':
return result
self._error = self.CODE.get(cod_error, error)
return {}
if hasattr(result, 'CodEstatus'):
ce = result.CodEstatus
if ce is None:
return result
if ce == self.CODE['IP']:
self._error = self.CODE['IPMSG']
return {}
if self.CODE['NE'] in ce:
self._error = 'UUID ' + self.CODE['NE']
return {}
if ce == 'UUID Not Found':
self._error = 'UUID ' + self.CODE['NE']
return {}
if self.CODE['200'] != ce:
self._error = ce
return {}
return result
return result
def _get_result(self, client, method, args):
self._error = ''
try:
result = getattr(client.service, method)(**args)
except Fault as e:
self._error = str(e)
return {}
except TransportError as e:
if '413' in str(e):
self._error = '413<BR><BR><b>Documento muy grande para timbrar</b>'
else:
self._error = str(e)
return {}
except ConnectionError as e:
msg = '502 - Error de conexión'
self._error = msg
return {}
return self._validate_result(result)
def _to_string(self, data):
root = ET.parse(BytesIO(data.encode('utf-8'))).getroot()
xml = ET.tostring(root,
pretty_print=True, xml_declaration=True, encoding='utf-8')
return xml.decode('utf-8')
def stamp(self, cfdi, auth={}):
if DEBUG or not auth:
auth = AUTH
method = 'timbra'
client = Client(self.URL[method],
transport=self._transport, plugins=self._plugins)
args = {
'username': auth['user'],
'password': auth['pass'],
'xml': cfdi.encode('utf-8'),
}
result = self._get_result(client, 'stamp', args)
if self.error:
log.error(self.error)
return ''
data = {
'xml': self._to_string(result.xml),
'uuid': result.UUID,
'date': result.Fecha,
}
return data
def _get_data_cancel(self, cfdi):
VERSIONS = {
'3.3': 'http://www.sat.gob.mx/cfd/3',
'4.0': 'http://www.sat.gob.mx/cfd/4',
}
NS_CFDI = {
'tdf': 'http://www.sat.gob.mx/TimbreFiscalDigital',
}
tree = ET.fromstring(cfdi.encode())
version = tree.attrib['Version']
NS_CFDI['cfdi'] = VERSIONS[version]
rfc_emisor = tree.xpath(
'string(//cfdi:Comprobante/cfdi:Emisor/@Rfc)',
namespaces=NS_CFDI)
cfdi_uuid = tree.xpath(
'string(//cfdi:Complemento/tdf:TimbreFiscalDigital/@UUID)',
namespaces=NS_CFDI)
return rfc_emisor, cfdi_uuid
def cancel(self, cfdi, info, auth={}):
if DEBUG or not auth:
auth = AUTH
rfc_emisor, cfdi_uuid = self._get_data_cancel(cfdi)
method = 'cancel'
client = Client(self.URL[method],
transport=self._transport, plugins=self._plugins)
uuid_type = client.get_type(f'{self.NS_TYPE}:UUIDS')
ns1_uuid = client.get_type(f'{self.NS_TYPE}:UUID')
# ~ sa = client.get_type('ns0:stringArray')
data_uuid = {
'UUID': cfdi_uuid,
'FolioSustitucion': info['args']['uuid'],
'Motivo': info['args']['reason'],
}
# ~ 'UUIDS': uuid_type(uuids=sa(string=cfdi_uuid)),
args = {
'UUIDS': uuid_type(ns1_uuid(**data_uuid)),
'username': auth['user'],
'password': auth['pass'],
'taxpayer_id': rfc_emisor,
'cer': info['cer'],
'key': info['key'],
'store_pending': False,
}
result = self._get_result(client, 'cancel', args)
if self.error:
log.error(self.error)
return ''
folio = result['Folios']['Folio'][0]
status = folio['EstatusUUID']
if status != '201':
log.debug(f'Cancel status: {status} - {cfdi_uuid}')
data = {
'acuse': result['Acuse'],
'date': result['Fecha'],
}
return data
def cancel_xml(self, xml, auth={}, cfdi=''):
if DEBUG or not auth:
auth = AUTH
method = 'cancel'
client = Client(self.URL[method],
transport=self._transport, plugins=self._plugins)
client.set_ns_prefix('can', 'http://facturacion.finkok.com/cancel')
args = {
'xml': xml.encode(),
'username': auth['user'],
'password': auth['pass'],
'store_pending': False,
}
result = self._get_result(client, 'cancel_signature', args)
if self.error:
log.error(self.error)
return ''
folio = result['Folios']['Folio'][0]
status = folio['EstatusUUID']
if status == '708':
self._error = 'Error 708 del SAT, intenta más tarde.'
log.error(self.error)
return ''
if status != '201':
log.debug(f'Cancel status: {status} -')
data = {
'acuse': result['Acuse'],
'date': result['Fecha'],
}
return data
def client_add(self, rfc, type_user=False):
"""Agrega un nuevo cliente para timbrado.
Se requiere cuenta de reseller para usar este método
Args:
rfc (str): El RFC del nuevo cliente
Kwargs:
type_user (bool):
False == 'P' == Prepago
True == 'O' == On demand
Returns:
True or False
origin PAC
'message':
'Account Created successfully'
'Account Already exists'
'success': True or False
"""
auth = AUTH['RESELLER']
tu = {True: 'O', False: 'P'}
method = 'client'
client = Client(
self.URL[method], transport=self._transport, plugins=self._plugins)
args = {
'reseller_username': auth['user'],
'reseller_password': auth['pass'],
'taxpayer_id': rfc,
'type_user': tu[type_user],
'added': datetime.datetime.now().isoformat()[:19],
}
result = self._get_result(client, 'add', args)
if self.error:
return False
if not result.success:
self.error = result.message
return False
# ~ PAC success debería ser False
msg = 'Account Already exists'
if result.message == msg:
self.error = msg
return True
return result.success
def client_get_token(self, rfc, email):
"""Genera un nuevo token al cliente para timbrado.
Se requiere cuenta de reseller para usar este método
Args:
rfc (str): El RFC del cliente, ya debe existir
email (str): El correo del cliente, funciona como USER al timbrar
Returns:
token (str): Es la contraseña para timbrar
origin PAC
dict
'username': 'username',
'status': True or False
'name': 'name',
'success': True or False
'token': 'Token de timbrado',
'message': None
"""
auth = AUTH['RESELLER']
method = 'util'
client = Client(
self.URL[method], transport=self._transport, plugins=self._plugins)
args = {
'username': auth['user'],
'password': auth['pass'],
'name': rfc,
'token_username': email,
'taxpayer_id': rfc,
'status': True,
}
result = self._get_result(client, 'add_token', args)
if self.error:
log.error(self.error)
return ''
if not result.success:
self.error = result.message
log.error(self.error)
return ''
return result.token
def client_add_timbres(self, rfc, credit):
"""Agregar credito a un emisor
Se requiere cuenta de reseller
Args:
rfc (str): El RFC del emisor, debe existir
credit (int): Cantidad de folios a agregar
Returns:
dict
'success': True or False,
'credit': nuevo credito despues de agregar or None
'message':
'Success, added {credit} of credit to {RFC}.'
'RFC no encontrado'
"""
auth = AUTH['RESELLER']
method = 'client'
client = Client(
self.URL[method], transport=self._transport, plugins=self._plugins)
args = {
'username': auth['user'],
'password': auth['pass'],
'taxpayer_id': rfc,
'credit': credit,
}
result = self._get_result(client, 'assign', args)
if self.error:
log.error(error)
return ''
if not result.success:
self.error = result.message
return 0
return result.credit
def client_balance(self, auth={}, rfc=''):
"""Regresa los timbres restantes del cliente
Se pueden usar las credenciales de relleser o las credenciales del emisor
Args:
auth (dict): Credenciales del emisor
rfc (str): El RFC del emisor
Returns:
int Cantidad de timbres restantes
"""
if not auth:
auth = AUTH['RESELLER']
method = 'client'
client = Client(self.URL[method],
transport=self._transport, plugins=self._plugins)
args = {
'reseller_username': auth['user'],
'reseller_password': auth['pass'],
'taxpayer_id': rfc,
}
result = self._get_result(client, 'get', args)
if self.error:
log.error(self.error)
return ''
success = bool(result.users)
if not success:
self._error = result.message or 'RFC no existe'
log.error(self.error)
return 0
return result.users.ResellerUser[0].credit
def client_set_status(self, rfc, status):
"""Edita el estatus (Activo o Suspendido) de un cliente
Se requiere cuenta de reseller para usar este método
Args:
rfc (str): El RFC del cliente
Kwargs:
status (bool):
True == 'A' == Activo
False == 'S' == Suspendido
Returns:
dict
'message':
'Account Created successfully'
'Account Already exists'
'success': True or False
"""
auth = AUTH['RESELLER']
ts = {True: 'A', False: 'S'}
method = 'client'
client = Client(self.URL[method],
transport=self._transport, plugins=self._plugins)
args = {
'reseller_username': auth['user'],
'reseller_password': auth['pass'],
'taxpayer_id': rfc,
'status': ts[status],
}
result = self._get_result(client, 'edit', args)
if self.error:
return False
if not result.success:
self.error = result.message
return False
return True
def client_switch(self, rfc, type_user):
"""Edita el tipo de timbrado (OnDemand o Prepago) de un cliente
Se requiere cuenta de reseller para usar este método
Args:
rfc (str): El RFC del cliente
Kwargs:
status (bool):
True == 'O' == OnDemand
False == 'P' == Prepago
Returns:
dict
'message':
'Account Created successfully'
'Account Already exists'
'success': True or False
"""
auth = AUTH['RESELLER']
tu = {True: 'O', False: 'P'}
method = 'client'
client = Client(self.URL[method],
transport=self._transport, plugins=self._plugins)
args = {
'username': auth['user'],
'password': auth['pass'],
'taxpayer_id': rfc,
'type_user': tu[type_user],
}
result = self._get_result(client, 'switch', args)
if self.error:
return False
if not result.success:
self.error = result.message
return False
return True
def client_report_folios(self, rfc, date_from, date_to, invoice_type='I'):
"""Obtiene un reporte del total de facturas timbradas
"""
auth = AUTH['RESELLER']
args = {
'username': auth['user'],
'password': auth['pass'],
'taxpayer_id': rfc,
'date_from': date_from,
'date_to': date_to,
'invoice_type': invoice_type,
}
method = 'util'
client = Client(self.URL[method],
transport=self._transport, plugins=self._plugins)
result = self._get_result(client, 'report_total', args)
if result.result is None:
# ~ PAC - Debería regresar RFC inexistente o sin registros
self.error = 'RFC no existe o no tiene registros'
return 0
total = result.result.ReportTotal[0].total
return total

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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,33 @@
server {
listen 80;
server_name cfdi.empresalibre.net;
location / {
include uwsgi_params;
uwsgi_pass 127.0.0.1:3033;
access_log /var/log/nginx/empresa-libre.access.log;
error_log /var/log/nginx/empresa-libre.error.log;
}
#error_page 404 /404.html;
# redirect server error pages to the static page /50x.html
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
location /static {
alias /opt/empresa-libre/static;
}
if ($http_user_agent ~* (python|curl) ) {
return 404;
}
# Necesario para Let's Encrypt
location ^~ /.well-known/acme-challenge/ {
default_type "text/plain";
root /opt/www;
}
}

View File

@ -0,0 +1,9 @@
[Unit]
Description=uWSGI instance to serve Empresa Libre
[Service]
ExecStart=/usr/bin/uwsgi /opt/empresa-libre/app/main.ini
KillSignal=SIGQUIT
[Install]
WantedBy=multi-user.target

View File

@ -1,13 +0,0 @@
[uwsgi]
socket = 127.0.0.1:3033
uid = nginx
gid = nginx
chdir = /srv/app/empresa-libre/app
wsgi-file = main.py
callable = app
master = true
processes = 4
threads = 4
thunder-lock = true
#~ stats = 127.0.0.1:9191
logger = file:/srv/log/empresalibre-uwsgi.log

View File

@ -0,0 +1,19 @@
[uwsgi]
#~ Debe de corresponder con tu configuracion proxi en tu servidor web
socket = 127.0.0.1:3033
uid = nginx
gid = nginx
#~ Establece una ruta accesible para el servidor web
chdir = /srv/app/empresa-libre/app
wsgi-file = main.py
callable = app
master = true
#~ Puedes configurar de acuerdo a los recursos de tu servidor
processes = 4
threads = 4
thunder-lock = true
#~ stats = 127.0.0.1:9191
#~ Establece una ruta accesible para el servidor web
logger = file:/srv/log/empresalibre-uwsgi.log
log-maxsize = 1000000
http-timeout = 180

View File

@ -8,16 +8,29 @@ from middleware import (
AuthMiddleware, AuthMiddleware,
JSONTranslator, JSONTranslator,
ConnectionMiddleware, ConnectionMiddleware,
static,
handle_404 handle_404
) )
from models.db import StorageEngine from models.db import StorageEngine
from controllers.main import ( from controllers.main import (AppEmpresas,
AppLogin, AppLogout, AppAdmin, AppEmisor, AppConfig, AppLogin, AppLogout, AppAdmin, AppEmisor, AppConfig,
AppMain, AppValues, AppPartners, AppProducts, AppInvoices, AppFolios, AppMain, AppValues, AppPartners, AppProducts, AppInvoices, AppFolios,
AppDocumentos AppDocumentos, AppFiles, AppPreInvoices, AppCuentasBanco,
AppMovimientosBanco, AppTickets, AppStudents, AppEmployees, AppNomina,
AppInvoicePay, AppCfdiPay, AppSATBancos, AppSociosCuentasBanco,
AppSATFormaPago, AppSATLeyendaFiscales, AppCert, AppSucursales,
AppPartnerProducts,
AppInventoryEntries,
AppTicketsDetails,
AppUsers,
AppWareHouse,
AppWareHouseProduct,
AppSATUnidadesPeso,
AppSATRegimenes,
AppSociosRegimenes,
) )
from settings import DEBUG
from settings import DEBUG, MV, NO_HTTPS, PATH_SESSIONS
db = StorageEngine() db = StorageEngine()
@ -31,6 +44,7 @@ api = falcon.API(middleware=[
api.req_options.auto_parse_form_urlencoded = True api.req_options.auto_parse_form_urlencoded = True
api.add_sink(handle_404, '') api.add_sink(handle_404, '')
api.add_route('/empresas', AppEmpresas(db))
api.add_route('/', AppLogin(db)) api.add_route('/', AppLogin(db))
api.add_route('/logout', AppLogout(db)) api.add_route('/logout', AppLogout(db))
api.add_route('/admin', AppAdmin(db)) api.add_route('/admin', AppAdmin(db))
@ -38,23 +52,49 @@ api.add_route('/emisor', AppEmisor(db))
api.add_route('/folios', AppFolios(db)) api.add_route('/folios', AppFolios(db))
api.add_route('/main', AppMain(db)) api.add_route('/main', AppMain(db))
api.add_route('/values/{table}', AppValues(db)) api.add_route('/values/{table}', AppValues(db))
api.add_route('/files/{table}', AppFiles(db))
api.add_route('/config', AppConfig(db)) api.add_route('/config', AppConfig(db))
api.add_route('/doc/{type_doc}/{id_doc}', AppDocumentos(db)) api.add_route('/doc/{type_doc}/{id_doc}', AppDocumentos(db))
api.add_route('/partners', AppPartners(db)) api.add_route('/partners', AppPartners(db))
api.add_route('/products', AppProducts(db)) api.add_route('/products', AppProducts(db))
api.add_route('/invoices', AppInvoices(db)) api.add_route('/invoices', AppInvoices(db))
api.add_route('/preinvoices', AppPreInvoices(db))
api.add_route('/tickets', AppTickets(db))
api.add_route('/cuentasbanco', AppCuentasBanco(db))
if DEBUG: api.add_route('/movbanco', AppMovimientosBanco(db))
api.add_sink(static, '/static') api.add_route('/students', AppStudents(db))
api.add_route('/employees', AppEmployees(db))
api.add_route('/nomina', AppNomina(db))
api.add_route('/invoicepay', AppInvoicePay(db))
api.add_route('/cfdipay', AppCfdiPay(db))
api.add_route('/satbancos', AppSATBancos(db))
api.add_route('/satformapago', AppSATFormaPago(db))
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))
api.add_route('/satunidadespeso', AppSATUnidadesPeso(db))
api.add_route('/satregimenes', AppSATRegimenes(db))
api.add_route('/sociosregimenes', AppSociosRegimenes(db))
session_options = { session_options = {
'session.type': 'file', 'session.type': 'file',
'session.cookie_expires': True, 'session.cookie_expires': True,
'session.data_dir': '/tmp/cache/data', 'session.httponly': True,
'session.lock_dir': '/tmp/cache/lock', 'session.secure': True,
'session.data_dir': PATH_SESSIONS['data'],
'session.lock_dir': PATH_SESSIONS['lock'],
} }
# ~ Si no usas (NO deberías) certificados en tu servidor, ponla siempre en False
if DEBUG or MV or NO_HTTPS:
session_options['session.secure'] = False
app = SessionMiddleware(api, session_options) app = SessionMiddleware(api, session_options)

View File

@ -1,9 +1,11 @@
[uwsgi] [uwsgi]
http = 127.0.0.1:8000 http = 127.0.0.1:8000
#~ http = 37.228.132.181:9000
wsgi-file = main.py wsgi-file = main.py
callable = app callable = app
master = true master = true
processes = 4 processes = 4
threads = 4 threads = 4
py-autoreload = 1 py-autoreload = 1
thunder-lock = true
static-map = /static=../static
http-timeout = 300

13
source/app/main_linux.ini Normal file
View File

@ -0,0 +1,13 @@
[uwsgi]
http = :8000
uid = empresa
gid = empresa
chdir = /home/empresa/.opt/empresa-libre/source/app
wsgi-file = main.py
callable = app
master = true
processes = 4
threads = 4
thunder-lock = true
static-map = /static=../static
logger = file:../../../empresa-libre-uwsgi.log

View File

@ -3,7 +3,7 @@
import falcon import falcon
from controllers import util from controllers import util
from models import main from models import main
from settings import PATH_STATIC from settings import MV
def handle_404(req, resp): def handle_404(req, resp):
@ -20,22 +20,44 @@ def get_template(req, resp, resource):
resp.body = util.get_template(resource.template, data) resp.body = util.get_template(resource.template, data)
def static(req, res): # ~ def static(req, res):
path = PATH_STATIC + req.path # ~ path = PATH_STATIC + req.path
if util.is_file(path): # ~ if util.is_file(path):
res.content_type = util.get_mimetype(path) # ~ res.content_type = util.get_mimetype(path)
res.stream, res.stream_len = util.get_stream(path) # ~ res.stream, res.stream_len = util.get_stream(path)
res.status = falcon.HTTP_200 # ~ res.status = falcon.HTTP_200
else: # ~ else:
res.status = falcon.HTTP_404 # ~ res.status = falcon.HTTP_404
class AuthMiddleware(object): class AuthMiddleware(object):
def process_response(self, req, resp, resource):
pass
def process_resource(self, req, resp, resource, params): def process_resource(self, req, resp, resource, params):
session = req.env['beaker.session']
user = session.get('userobj', None)
id_session = req.cookies.get('beaker.session.id', '') id_session = req.cookies.get('beaker.session.id', '')
if not id_session and req.path != '/':
if req.path == '/values/titlelogin':
pass
elif req.path == '/empresas' or req.path == '/values/empresas':
if MV:
pass
else:
raise falcon.HTTPTemporaryRedirect('/')
elif id_session and req.path == '/admin':
if user is None:
raise falcon.HTTPTemporaryRedirect('/')
elif not user.es_admin and not user.es_superusuario:
raise falcon.HTTPTemporaryRedirect('/main')
elif not id_session and req.path != '/':
raise falcon.HTTPTemporaryRedirect('/') raise falcon.HTTPTemporaryRedirect('/')
elif id_session and user is None:
session.delete()
if req.path == '/main':
raise falcon.HTTPTemporaryRedirect('/')
class JSONTranslator(object): class JSONTranslator(object):
@ -46,9 +68,15 @@ class JSONTranslator(object):
def process_response(self, req, resp, resource): def process_response(self, req, resp, resource):
if 'result' not in req.context: if 'result' not in req.context:
return return
if '/doc/' in req.path: if '/doc/' in req.path:
resp.body = req.context['result'] resp.body = req.context['result']
return return
if 'blob' in req.context:
resp.body = req.context['blob']
return
resp.body = util.dumps(req.context['result']) resp.body = util.dumps(req.context['result'])
@ -61,7 +89,10 @@ class ConnectionMiddleware(object):
rfc = session.get('rfc', '') rfc = session.get('rfc', '')
if id_session and rfc: if id_session and rfc:
opt = util.get_con(rfc) opt = util.get_con(rfc)
main.conectar(opt) if opt:
main.conectar(opt)
else:
raise falcon.HTTPTemporaryRedirect('/')
def process_response(self, req, resp, resource): def process_response(self, req, resp, resource):
main.desconectar() main.desconectar()

View File

@ -1,5 +1,21 @@
#!/usr/bin/env python #!/usr/bin/env python
# ~ Empresa Libre
# ~ Copyright (C) 2016-2018 Mauricio Baeza Servin (web@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
# ~ the Free Software Foundation, either version 3 of the License, or
# ~ (at your option) any later version.
# ~
# ~ This program is distributed in the hope that it will be useful,
# ~ but WITHOUT ANY WARRANTY; without even the implied warranty of
# ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# ~ GNU General Public License for more details.
# ~
# ~ You should have received a copy of the GNU General Public License
# ~ along with this program. If not, see <http://www.gnu.org/licenses/>.
from . import main from . import main
@ -11,17 +27,89 @@ class StorageEngine(object):
def authenticate(self, args): def authenticate(self, args):
return main.authenticate(args) return main.authenticate(args)
def get_values(self, table, values=None): def get_employees(self, values):
return main.Empleados.get_by(values)
def get_nomina(self, values):
return main.CfdiNomina.get_by(values)
def empresa_agregar(self, values):
# ~ return main.empresa_agregar(values['alta_rfc'], False)
return main._new_client(values['alta_rfc'], False)
def empresa_borrar(self, values):
# ~ return main.empresa_borrar(values['rfc'])
return main._delete_client(values['rfc'], False, False)
def respaldar_dbs(self):
return main.respaldar_dbs()
def get_values(self, table, values=None, session=None):
if table in ('allusuarios', 'usuarioupdate', 'main'):
return getattr(self, '_get_{}'.format(table))(values, session)
return getattr(self, '_get_{}'.format(table))(values) return getattr(self, '_get_{}'.format(table))(values)
def _get_timbres(self, values):
return main.Emisor.get_timbres()
def _get_schoolgroups(self, values):
return main.Grupos.get_by(values)
def _get_nivedusat(self, values):
return main.SATNivelesEducativos.get_by()
def _get_niveduall(self, values):
return main.NivelesEducativos.get_all()
def _get_titlelogin(self, values):
return main.get_title_app(2)
def _get_canopenpre(self, values):
return main.PreFacturasDetalle.can_open(values['id'])
def _get_importinvoice(self, values):
return main.import_invoice()
def _get_importceods(self, values):
return main.import_ceods()
def _get_main(self, values, session):
return main.config_main(session['userobj'])
def _get_configtimbrar(self, values):
return main.config_timbrar()
def _get_invoicenotes(self, values):
return main.Facturas.get_notes(values['id'])
def save_invoice_notes(self, values):
return main.Facturas.save_notes(values)
def _get_configticket(self, values):
return main.config_ticket()
def _get_saldocuenta(self, values):
return main.CuentasBanco.get_saldo(values['id'])
def _get_validartimbrar(self, values):
return main.validar_timbrar()
def _get_preproductos(self, values):
return main.PreFacturasDetalle.facturar(values['id'])
def upload_file(self, session, table, file_obj):
if not 'rfc' in session:
return {'status': 'error'}
return main.upload_file(session['rfc'], table, file_obj)
def get_config(self, values): def get_config(self, values):
return main.Configuracion.get_(values) return main.Configuracion.get_(values)
def add_config(self, values): def add_config(self, values):
return main.Configuracion.add(values) return main.Configuracion.add(values)
def add_cert(self, file_object): def add_cert(self, file_obj):
return main.Certificado.add(file_object) return main.Certificado.add(file_obj)
def validate_cert(self, values, session): def validate_cert(self, values, session):
return main.Certificado.validate(values, session) return main.Certificado.validate(values, session)
@ -32,6 +120,26 @@ class StorageEngine(object):
def send_email(self, values, session): def send_email(self, values, session):
return main.Facturas.send(values['id'], session['rfc']) return main.Facturas.send(values['id'], session['rfc'])
def enviar_prefac(self, values):
return main.PreFacturas.enviar(values['id'])
def _get_verifysat(self, values):
return main.Facturas.get_verify_sat(values['id'])
def _get_filteryears(self, values):
years1 = main.Facturas.filter_years()
years2 = main.PreFacturas.filter_years()
return [years1, years2]
def _get_filteryearsticket(self, values):
return main.Tickets.filter_years()
def _get_filteryearsnomina(self, values):
return main.CfdiNomina.filter_years()
def _get_cuentayears(self, values):
return main.CuentasBanco.get_years()
def _get_cert(self, values): def _get_cert(self, values):
return main.Certificado.get_data() return main.Certificado.get_data()
@ -41,6 +149,12 @@ class StorageEngine(object):
def _get_formapago(self, values): def _get_formapago(self, values):
return main.SATFormaPago.get_activos(values) return main.SATFormaPago.get_activos(values)
def _get_tiporelacion(self, values):
return main.SATTipoRelacion.get_activos(values)
def _get_condicionespago(self, values):
return main.CondicionesPago.get_()
def _get_categorias(self, values): def _get_categorias(self, values):
return main.Categorias.get_all() return main.Categorias.get_all()
@ -50,70 +164,246 @@ class StorageEngine(object):
def _get_unidades(self, values): def _get_unidades(self, values):
return main.SATUnidades.get_activos() return main.SATUnidades.get_activos()
def _get_unitbykey(self, values):
return main.SATUnidades.get_activos_by_key()
def add_moneda(self, values):
return main.SATMonedas.add(values)
def add_unidad(self, values):
return main.SATUnidades.add(values)
def add_impuesto(self, values):
return main.SATImpuestos.add(values)
def add_usuario(self, values):
return main.Usuarios.add(values)
def edit_usuario(self, values):
return main.Usuarios.edit(values)
def _get_taxes(self, values): def _get_taxes(self, values):
return main.SATImpuestos.get_activos() return main.SATImpuestos.get_activos()
def _get_alltaxes(self, values):
return main.SATImpuestos.get_()
def _get_allcurrencies(self, values):
return main.SATMonedas.get_()
def _get_allbancos(self, values):
return main.SATBancos.get_()
def _get_allunidades(self, values):
return main.SATUnidades.get_()
def _get_allformasdepago(self, values):
return main.SATFormaPago.get_()
def _get_allusoscfdi(self, values):
return main.SATUsoCfdi.get_all()
def _get_allregimenes(self, values):
filters = {'opt': 'all'}
return main.SATRegimenes.get_data(filters, None)
def _get_allusuarios(self, values, session):
return main.Usuarios.get_(session['userobj'])
def _get_usuarioupdate(self, values, session):
return main.Usuarios.actualizar(values, session['userobj'])
def _get_taxupdate(self, values):
return main.SATImpuestos.actualizar(values)
def _get_currencyupdate(self, values):
return main.SATMonedas.actualizar(values)
def _get_bancoupdate(self, values):
return main.SATBancos.actualizar(values)
def _get_emisorbancoupdate(self, values):
return main.CuentasBanco.activate(values)
def _get_unidadupdate(self, values):
return main.SATUnidades.actualizar(values)
def _get_formasdepagoupdate(self, values):
return main.SATFormaPago.actualizar(values)
def _get_usocfdiupdate(self, values):
return main.SATUsoCfdi.actualizar(values)
def _get_regimenesupdate(self, values):
return main.SATRegimenes.actualizar(values)
def _get_emisorcuentasbanco(self, values):
return main.CuentasBanco.emisor()
def _get_satkey(self, values): def _get_satkey(self, values):
return main.get_sat_key(values['key']) return main.get_sat_key(values['key'])
def _get_satmonedas(self, values):
return main.get_sat_monedas(values['key'])
def _get_satunidades(self, values):
return main.get_sat_unidades(values['key'])
def _get_satunidadespeso(self, values):
return main.get_sat_unidadespeso(values['key'])
def _get_satproductos(self, values):
return main.get_sat_productos(values['key'])
def _get_series(self, values): def _get_series(self, values):
return main.Folios.get_all() return main.Folios.get_all()
def _get_monedas(self, values): def _get_monedas(self, values):
return main.SATMonedas.get_activos() return main.SATMonedas.get_activos()
def _get_monedasid(self, values):
return main.SATMonedas.get_activos_by_id()
def _get_bancosid(self, values):
return main.SATBancos.get_activos_by_id()
def _get_regimenes(self, values): def _get_regimenes(self, values):
return main.Emisor.get_regimenes() return main.Emisor.get_regimenes()
def _get_usocfdi(self, values): def _get_usocfdi(self, values):
return main.SATUsoCfdi.get_activos(values) return main.SATUsoCfdi.get_activos()
def delete(self, table, id): def _get_ebancomov(self, values):
return main.MovimientosBanco.con(values['id'])
def delete(self, table, id, user=None):
if table == 'partner': if table == 'partner':
return main.Socios.remove(id) return main.Socios.remove(id)
if table == 'product': if table == 'product':
return main.Productos.remove(id) return main.Productos.remove(id)
if table == 'invoice': if table == 'invoice':
return main.Facturas.remove(id) return main.Facturas.remove(id, user)
if table == 'folios': if table == 'folios':
return main.Folios.remove(id) return main.Folios.remove(id)
if table == 'preinvoice':
return main.PreFacturas.remove(id)
if table == 'satimpuesto':
return main.SATImpuestos.remove(id)
if table == 'satunit':
return main.SATUnidades.remove(id)
if table == 'cuentasbanco':
return main.CuentasBanco.remove(id)
if table == 'movbanco':
return main.MovimientosBanco.remove(id)
if table == 'usuario':
return main.Usuarios.remove(id)
if table == 'config':
return main.Configuracion.remove(id)
if table == 'nivedu':
return main.NivelesEducativos.remove(id)
if table == 'students':
return main.Alumnos.remove(id)
if table == 'employee':
return main.Empleados.remove(id)
if table == 'nomina':
return main.CfdiNomina.remove(id)
return False return False
def _get_client(self, values): def _get_client(self, values):
return main.Socios.get_by_client(values) return main.Socios.get_by_client(values)
def _get_student(self, values):
return main.Alumnos.get_by_name(values)
def _get_product(self, values): def _get_product(self, values):
return main.Productos.get_by(values) return main.Productos.get_by(values)
def _get_productokey(self, values):
return main.Productos.get_by_key(values)
def get_partners(self, values): def get_partners(self, values):
return main.Socios.get_(values) return main.Socios.get_(values)
def partner(self, values): def partner(self, values):
if 'opt' in values:
return main.Socios.opt(values)
id = int(values.pop('id', '0')) id = int(values.pop('id', '0'))
if id: if id:
return main.Socios.actualizar(values, id) return main.Socios.actualizar(values, id)
return main.Socios.add(values) return main.Socios.add(values)
def get_products(self, values): def get_products(self, values):
return main.Productos.get_(values) return main.Productos.get_(values)
def product(self, values): def products(self, values):
id = int(values.pop('id', '0')) id = int(values.pop('id', '0'))
if id: if id:
return main.Productos.actualizar(values, id) return main.Productos.actualizar(values, id)
opt = values.get('opt', '')
if opt:
return main.Productos.opt(values)
return main.Productos.add(values) return main.Productos.add(values)
def invoice(self, values): def invoice(self, values, user):
if 'opt' in values:
return main.Facturas.opt(values, user)
id = int(values.pop('id', '0')) id = int(values.pop('id', '0'))
if id: if id:
return main.Facturas.actualizar(values, id) return main.Facturas.actualizar(values, id)
return main.Facturas.add(values)
def get_invoices(self, values): return main.Facturas.add(values, user)
return main.Facturas.get_(values)
def _get_timbrar(self, values): def invoice_put(self, values, user):
return main.Facturas.timbrar(int(values['id'])) return main.Facturas.put(values, user)
def preinvoice(self, values):
id = int(values.pop('id', '0'))
#~ if id:
#~ return main.PreFacturas.actualizar(values, id)
return main.PreFacturas.add(values)
def get_students(self, values):
return main.Alumnos.get_by(values)
def students(self, values):
opt = values.pop('opt')
if opt == 'add':
return main.Alumnos.add(values['values'])
if opt == 'edit':
return main.Alumnos.actualizar(values['values'])
def tickets(self, values, user):
opt = values.pop('opt')
if opt == 'add':
return main.Tickets.add(values, user)
if opt == 'cancel':
return main.Tickets.cancel(values, user)
if opt == 'invoice':
return main.Tickets.invoice(values, user)
if opt == 'print':
return main.Tickets.printer(values)
def get_tickets(self, values, user):
return main.Tickets.get_by(values, user)
def get_invoices(self, filters, user):
if filters.get('by', ''):
return main.Facturas.get_by(filters, user)
return main.Facturas.get_(filters)
def get_preinvoices(self, values):
return main.PreFacturas.get_(values)
# ~ def _get_timbrar(self, values):
# ~ return main.Facturas.timbrar(values)
def _get_anticipoegreso(self, values):
return main.Facturas.anticipo_egreso(int(values['id']))
def get_emisor(self, rfc): def get_emisor(self, rfc):
return main.Emisor.get_(rfc) return main.Emisor.get_(rfc)
@ -121,21 +411,138 @@ class StorageEngine(object):
def emisor(self, values): def emisor(self, values):
return main.Emisor.add(values) return main.Emisor.add(values)
def cuentasbanco(self, values):
return main.CuentasBanco.add(values)
# ~ def add_movbanco(self, values):
# ~ return main.MovimientosBanco.add(values)
def get_cuentasbanco(self, values):
return main.CuentasBanco.get_(values)
def get_folios(self): def get_folios(self):
return main.Folios.get_() return main.Folios.get_()
def add_folios(self, values): def add_folios(self, values):
return main.Folios.add(values) return main.Folios.add(values)
def get_doc(self, type_doc, id, rfc): def add_nivel_educativo(self, values):
if type_doc == 'xml': return main.NivelesEducativos.add(values)
data, file_name = main.Facturas.get_xml(id)
content_type = 'application/xml' def get_doc(self, type_doc, id, rfc):
if type_doc == 'pdf': return main.get_doc(type_doc, id, rfc)
data, file_name = main.Facturas.get_pdf(id, rfc)
content_type = 'application/pdf' def get_movimientosbanco(self, values):
if type_doc == 'zip': return main.MovimientosBanco.get_(values)
data, file_name = main.Facturas.get_zip(id, rfc)
content_type = 'application/octet-stream' def importar_bdfl(self):
return data, file_name, content_type return main.importar_bdfl()
# ~ Revisado
def get_invoicepay(self, values):
return main.FacturasPagos.get_values(values)
def get_cfdipay(self, values):
return main.CfdiPagos.get_values(values)
def get_sat_bancos(self, values):
return main.SATBancos.get_values(values)
def get_sat_forma_pago(self, values):
return main.SATFormaPago.get_values(values)
def get_partners_accounts_bank(self, values):
return main.SociosCuentasBanco.get_values(values)
def cfdipay(self, values):
return main.CfdiPagos.post(values)
def bankmovement(self, values):
return main.MovimientosBanco.post(values)
def sat_bancos(self, values):
return main.SATBancos.post(values)
def sat_forma_pago(self, values):
return main.SATFormaPago.post(values)
def partners_accounts_bank(self, values):
return main.SociosCuentasBanco.post(values)
def nomina(self, values, user):
return main.CfdiNomina.post(values, user)
def sat_leyendas_fiscales_get(self, values):
return main.SATLeyendasFiscales.get_values(values)
def sat_leyendas_fiscales_post(self, values):
return main.SATLeyendasFiscales.post(values)
def sat_leyendas_fiscales_delete(self, values):
return main.SATLeyendasFiscales.remove(values)
# ~ v2
def cert_get(self, filters):
return main.Certificado.get_data(filters)
def cert_post(self, filters):
return main.Certificado.post(filters)
def sucursales_get(self, filters):
return main.Sucursales.get_data(filters)
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)
def sat_unidades_peso_get(self, filters, user):
return main.SATUnidadesPeso.get_data(filters, user)
def sat_unidades_peso_post(self, args, user):
return main.SATUnidadesPeso.post(args, user)
def sat_regimenes_get(self, filters, user):
return main.SATRegimenes.get_data(filters, user)
def socios_regimenes_get(self, filters, user):
return main.SociosRegimenes.get_data(filters, user)
# Companies only in MV
def _get_empresas(self, values):
return main.companies_get()

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,20 @@
#!/usr/bin/env python #!/usr/bin/env python3
# ~ Empresa Libre
# ~ Copyright (C) 2016-2018 Mauricio Baeza Servin (publico@cuates.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
# ~ the Free Software Foundation, either version 3 of the License, or
# ~ (at your option) any later version.
# ~
# ~ This program is distributed in the hope that it will be useful,
# ~ but WITHOUT ANY WARRANTY; without even the implied warranty of
# ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# ~ GNU General Public License for more details.
# ~
# ~ 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 logbook import logbook
import os import os
@ -7,15 +23,40 @@ from mako.lookup import TemplateLookup
from logbook import Logger, StreamHandler, RotatingFileHandler from logbook import Logger, StreamHandler, RotatingFileHandler
logbook.set_datetime_format('local') logbook.set_datetime_format('local')
from conf import DEBUG from conf import DEBUG, MV, LOG_PATH
try:
from conf import DEFAULT_PASSWORD
except ImportError:
DEFAULT_PASSWORD = 'salgueiro4.0'
TITLE_APP = 'Empresa Libre'
try:
from conf import NO_HTTPS
except ImportError:
NO_HTTPS = True
DEBUG = DEBUG DEBUG = DEBUG
VERSION = '0.1.0' VERSION = '2.3.2'
EMAIL_SUPPORT = ('soporte@empresalibre.net',)
EMAIL_SUPPORT = ('soporte@empresalibre.mx',)
TITLE_APP = '{} v{}'.format(TITLE_APP, VERSION)
BASE_DIR = os.path.abspath(os.path.dirname(__file__)) BASE_DIR = os.path.abspath(os.path.dirname(__file__))
PATH_STATIC = os.path.abspath(os.path.join(BASE_DIR, '..')) COMPANIES = os.path.abspath(os.path.join(BASE_DIR, '..', 'db', 'rfc.db'))
path_static = os.path.abspath(os.path.join(BASE_DIR, '..', 'static'))
path_docs = os.path.abspath(os.path.join(BASE_DIR, '..', 'docs'))
path_css = os.path.join(path_static, 'css')
path_img = os.path.join(path_static, 'img')
path_user_template = os.path.join(path_docs, 'templates')
path_user_logos = os.path.join(path_docs, 'logos')
# ~ PATH_STATIC = os.path.abspath(os.path.join(BASE_DIR, '..'))
PATH_TEMPLATES = os.path.abspath(os.path.join(BASE_DIR, '..', 'templates')) PATH_TEMPLATES = os.path.abspath(os.path.join(BASE_DIR, '..', 'templates'))
PATH_MEDIA = os.path.abspath(os.path.join(BASE_DIR, '..', 'docs')) PATH_MEDIA = os.path.abspath(os.path.join(BASE_DIR, '..', 'docs'))
@ -23,17 +64,26 @@ PATH_CP = os.path.abspath(os.path.join(BASE_DIR, '..', 'db', 'cp.db'))
COMPANIES = os.path.abspath(os.path.join(BASE_DIR, '..', 'db', 'rfc.db')) COMPANIES = os.path.abspath(os.path.join(BASE_DIR, '..', 'db', 'rfc.db'))
DB_SAT = os.path.abspath(os.path.join(BASE_DIR, '..', 'db', 'sat.db')) DB_SAT = os.path.abspath(os.path.join(BASE_DIR, '..', 'db', 'sat.db'))
PATH_SESSIONS = {
'data': os.path.abspath(os.path.join(BASE_DIR, '..', 'cache', 'data')),
'lock': os.path.abspath(os.path.join(BASE_DIR, '..', 'cache', 'lock')),
}
IV = 'valores_iniciales.json' IV = 'valores_iniciales.json'
INIT_VALUES = os.path.abspath(os.path.join(BASE_DIR, '..', 'db', IV)) INIT_VALUES = os.path.abspath(os.path.join(BASE_DIR, '..', 'db', IV))
# ~ CT = 'cancel_template.xml'
# ~ TEMPLATE_CANCEL = os.path.abspath(os.path.join(PATH_TEMPLATES, CT))
PATH_XSLT = os.path.abspath(os.path.join(BASE_DIR, '..', 'xslt')) PATH_XSLT = os.path.abspath(os.path.join(BASE_DIR, '..', 'xslt'))
PATH_BIN = os.path.abspath(os.path.join(BASE_DIR, '..', 'bin')) PATH_BIN = os.path.abspath(os.path.join(BASE_DIR, '..', 'bin'))
template_lookup = TemplateLookup(directories=[PATH_TEMPLATES], PATH_TEMPLATES_USER = os.path.abspath(os.path.join(
BASE_DIR, '..', 'docs', 'templates'))
directories=[PATH_TEMPLATES, PATH_TEMPLATES_USER]
template_lookup = TemplateLookup(directories=directories,
input_encoding='utf-8', input_encoding='utf-8',
output_encoding='utf-8') output_encoding='utf-8')
LOG_PATH = 'empresalibre.log'
LOG_NAME = 'API' LOG_NAME = 'API'
LOG_LEVEL = 'INFO' LOG_LEVEL = 'INFO'
@ -50,7 +100,6 @@ if DEBUG:
level=LOG_LEVEL, level=LOG_LEVEL,
format_string=format_string).push_application() format_string=format_string).push_application()
else: else:
LOG_PATH = '/srv/log/empresalibre.log'
RotatingFileHandler( RotatingFileHandler(
LOG_PATH, LOG_PATH,
backup_count=10, backup_count=10,
@ -69,22 +118,202 @@ log = Logger(LOG_NAME)
PATH_XSLTPROC = 'xsltproc' PATH_XSLTPROC = 'xsltproc'
PATH_OPENSSL = 'openssl' PATH_OPENSSL = 'openssl'
PATH_XMLSEC = 'xmlsec1'
if 'win' in sys.platform: if 'win' in sys.platform:
PATH_XSLTPROC = os.path.join(PATH_BIN, 'xsltproc.exe') PATH_XSLTPROC = os.path.join(PATH_BIN, 'xsltproc.exe')
PATH_OPENSSL = os.path.join(PATH_BIN, 'openssl.exe') PATH_OPENSSL = os.path.join(PATH_BIN, 'openssl.exe')
PATH_XMLSEC = os.path.join(PATH_BIN, 'xmlsec.exe')
PRE_DEFAULT = {
'CFDI': {'VERSION': '4.0', 'PRE': '{http://www.sat.gob.mx/cfd/4}'},
'NOMINA': {'VERSION': '1.2', 'PRE': '{http://www.sat.gob.mx/nomina12}'},
'PAGOS': {'VERSION': '2.0', 'PRE': '{http://www.sat.gob.mx/Pagos20}'},
'TIBRE': {'VERSION': '1.1', 'PRE': '{http://www.sat.gob.mx/TimbreFiscalDigital}'},
}
pre2 ='{http://www.sat.gob.mx/cfd/2}'
pre3 ='{http://www.sat.gob.mx/cfd/3}'
PRE_HISTORY = {
'CFDI': {'2.0': pre2, '2.2': pre2,
'3.0': pre3, '3.2': pre3, '3.3': pre3},
'NOMINA': {'1.1': '{http://www.sat.gob.mx/nomina}'},
'PAGOS': {'1.0': '{http://www.sat.gob.mx/Pagos}'},
}
PRE = { PRE = {
'2.0': '{http://www.sat.gob.mx/cfd/2}', '2.0': '{http://www.sat.gob.mx/cfd/2}',
'2.2': '{http://www.sat.gob.mx/cfd/2}', '2.2': '{http://www.sat.gob.mx/cfd/2}',
'3.0': '{http://www.sat.gob.mx/cfd/3}', '3.0': '{http://www.sat.gob.mx/cfd/3}',
'3.2': '{http://www.sat.gob.mx/cfd/3}', '3.2': '{http://www.sat.gob.mx/cfd/3}',
'3.3': '{http://www.sat.gob.mx/cfd/3}', '3.3': '{http://www.sat.gob.mx/cfd/3}',
'4.0': '{http://www.sat.gob.mx/cfd/4}',
'TIMBRE': '{http://www.sat.gob.mx/TimbreFiscalDigital}', 'TIMBRE': '{http://www.sat.gob.mx/TimbreFiscalDigital}',
'DONATARIA': '{http://www.sat.gob.mx/donat}',
'INE': '{http://www.sat.gob.mx/ine}',
'LOCALES': '{http://www.sat.gob.mx/implocal}',
'NOMINA': { 'NOMINA': {
'1.1': '{http://www.sat.gob.mx/nomina}', '1.1': '{http://www.sat.gob.mx/nomina}',
'1.2': '{http://www.sat.gob.mx/nomina12}', '1.2': '{http://www.sat.gob.mx/nomina12}',
} },
'PAGOS': {
'1.0': '{http://www.sat.gob.mx/Pagos}',
}
} }
CURRENT_CFDI = '3.3' # To delete
# ~ CURRENT_CFDI = '4.0'
# ~ CURRENT_CFDI_NOMINA = '1.2'
DECIMALES = 2
DECIMALES_TAX = 4
DECIMALES_PRECIOS = 4
IMPUESTOS = {
'ISR': '001',
'IVA': '002',
'IEPS': '003',
'EXENTO': '000',
'ISH': '000',
'INSPECCION DE OBRA': '000',
'ICIC': '000',
'CEDULAR': '000',
'CMIC': '000',
'SUPERVISION': '000',
}
DEFAULT_SAT_PRODUCTO = '01010101'
DEFAULT_SERIE_TICKET = 'T'
DEFAULT_CFDIPAY = {
'SERIE': 'FP',
'TYPE': 'P',
'WAYPAY': 'PPD',
'CURRENCY': 'XXX',
'TC': '1',
'USED': 'CP01',
'KEYSAT': '84111506',
'UNITKEY': 'ACT',
'DESCRIPTION': 'Pago',
'TYPE_RELATION': '04',
}
DIR_FACTURAS = 'facturas'
USAR_TOKEN = False
CANCEL_SIGNATURE = False
PUBLIC = 'Público en general'
DEFAULT_SAT_NOMINA = {
'SERIE': 'N',
'FORMA_PAGO': '99',
'USO_CFDI': 'CN01',
'CLAVE': '84111505',
'UNIDAD': 'ACT',
'DESCRIPCION': 'Pago de nómina',
}
API = 'https://api.empresalibre.net{}'
CURRENCY_MN = 'MXN'
# ~ v2
CANCEL_VERSION = ('3.3', '4.0')
CFDI_VERSIONS = CANCEL_VERSION
IS_MV = MV
DB_COMPANIES = os.path.abspath(os.path.join(BASE_DIR, '..', 'db', 'rfc.db'))
path_bk = os.path.join(path_docs, 'tmp')
path_local = 'facturas'
path_sat = os.path.abspath(os.path.join(BASE_DIR, '..', 'db', 'valores_iniciales.json'))
EXT = {
'CSS': 'css',
'HTML': 'html',
'ODS': 'ods',
'PNG': 'png',
'JSON': 'json',
}
MXN = 'MXN'
CARTA_PORTE = {
'MONEDA': 'XXX',
}
PATHS = {
'STATIC': path_static,
'CSS': path_css,
'IMG': path_img,
'DOCS': path_docs,
'USER': path_user_template,
'LOGOS': path_user_logos,
'BK': path_bk,
'LOCAL': path_local,
'SAT': path_sat,
'xslt': PATH_XSLT,
}
VALUES_PDF = {
'CANCEL': {True: 'inline', False: 'none'},
'TYPE': {'I': 'Ingreso', 'E': 'Egreso', 'T': 'Traslado'},
'TAX': {'001': 'ISR', '002': 'IVA', '003': 'IEPS'},
'METHOD': {
'PUE': 'Pago en una sola exhibición',
'PPD': 'Pago en parcialidades o diferido',
},
}
RESICO = '626'
RFCS = {
'PUBLIC': 'XAXX010101000',
'FOREIGN': 'XEXX010101000',
'CVD110412TF6': 'finkok',
'SCD110105654': 'comercio',
'AAA010101AAA': 'comercio',
'SPR190613I52': 'comercio',
}
URL = {
'SEAFILE': 'https://seafile.cuates.net',
}
DEFAULT_GLOBAL = {
'cantidad': 1.00,
'unidad': 'ACT',
'descripcion': 'Venta',
'clave_sat': '01010101',
}
# ~ TEMPLATE_CANCEL = """<CancelaCFD xmlns="http://cancelacfd.sat.gob.mx">
# ~ <Cancelacion xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" RfcEmisor="{rfc}" Fecha="{fecha}">
# ~ <Folios>
# ~ <Folio UUID="{uuid}" Motivo="{motivo}"{folio}/>
# ~ </Folios>
# ~ <Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
# ~ <SignedInfo>
# ~ <CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315" />
# ~ <SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1" />
# ~ <Reference URI="">
# ~ <Transforms>
# ~ <Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" />
# ~ </Transforms>
# ~ <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
# ~ <DigestValue/>
# ~ </Reference>
# ~ </SignedInfo>
# ~ <SignatureValue/>
# ~ <KeyInfo>
# ~ <X509Data>
# ~ <X509IssuerSerial>
# ~ <X509IssuerName/>
# ~ <X509SerialNumber/>
# ~ </X509IssuerSerial>
# ~ <X509Certificate/>
# ~ </X509Data>
# ~ <KeyValue>
# ~ <RSAKeyValue>
# ~ <Modulus/>
# ~ <Exponent/>
# ~ </RSAKeyValue>
# ~ </KeyValue>
# ~ </KeyInfo>
# ~ </Signature>
# ~ </Cancelacion>
# ~ </CancelaCFD>"""
TEMPLATE_CANCEL = """<CancelaCFD xmlns="http://cancelacfd.sat.gob.mx"><Cancelacion xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" Fecha="{fecha}" RfcEmisor="{rfc}" xmlns="http://cancelacfd.sat.gob.mx"><Folios><Folio UUID="{uuid}" Motivo="{motivo}"{folio}/></Folios><Signature xmlns="http://www.w3.org/2000/09/xmldsig#"><SignedInfo><CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315" /><SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1" /><Reference URI=""><Transforms><Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" /></Transforms><DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" /><DigestValue/></Reference></SignedInfo><SignatureValue/><KeyInfo><X509Data><X509IssuerSerial><X509IssuerName/><X509SerialNumber/></X509IssuerSerial><X509Certificate/></X509Data><KeyValue><RSAKeyValue><Modulus/><Exponent/></RSAKeyValue></KeyValue></KeyInfo></Signature></Cancelacion></CancelaCFD>"""

BIN
source/bin/libeay32.dll Normal file

Binary file not shown.

BIN
source/bin/openssl.exe Normal file

Binary file not shown.

BIN
source/bin/ssleay32.dll Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

473
source/db/unidad_peso.csv Normal file
View File

@ -0,0 +1,473 @@
id key name
1 Tu Contenedor externo
2 X1A Tambor de acero
3 X1B Tambor de aluminio
4 X1D Tambor contrachapado
5 X1F Contenedor flexible
6 X1G Tambor de fibra
7 X1w Tambor de madera
8 X2C Barril de madera
9 X3A Bidón de acero
10 X3H Bidón de plástico
11 X43 Bolsa de gran tamaño
12 X44 Bolsa de plástico
13 X4A Caja de acero
14 X4B Caja de aluminio
15 X4C Caja de madera natural
16 X4D Caja de contrachapado
17 X4F Caja de madera reconstituida
18 X4G Caja de cartón
19 X4H Caja de plástico
20 X5H Bolsa de plástico tejido
21 X5L Bolsa textil
22 X5M Bolsa de papel
23 X6H Recipiente de plástico, Contenedor compuesto.
24 X6P Recipiente de vidrio, Contenedor compuesto.
25 X7A Estuche para carro
26 X7B Estuche de madera
27 X8A Pallet de madera
28 X8B Cajón de madera
29 X8C Madera flejada
30 XAA Contenedor intermedio para gráneles de plástico rígido
31 XAB Contenedor de fibra
32 XAC Contenedor de papel
33 XAD Contenedor de madera
34 XAE Aerosol
35 XAF Pallet modular con collares, 80cms * 60cms
36 XAG Pallet o empaquetado
37 XAH Pallet, 100cms X 110cms
38 XAI Contenedor tipo concha
39 XAJ Cono
40 XAL Esfera
41 XAM Ampolleta no protegida
42 XAP Ampolleta protegida
43 XAT Atomizador
44 XAV Cápsula
45 XB4 Cinturón
46 XBA Barril
47 XBB Bobina
48 XBC Cajón para botellas / Estante para botellas
49 XBD Tablero
50 XBE Flejado
51 XBF Globo no protegido
52 XBG Bolso
53 XBH Manojo
54 XBI Compartimiento
55 XBJ Cubeta
56 XBK Cesta
57 XBL Paca comprimida
58 XBM Cuenco
59 XBN Paca no comprimida
60 XBO Botella no-protegida y cilíndrica
61 XBP Globo protegido
62 XBQ Botella cilíndrica protegida
63 XBR Barra
64 XBS Botella, no-protegida en forma de bulbo
65 XBT Rollo de tela
66 XBU Butt
67 XBV Botella de bulbo protegido
68 XBW Caja para líquidos
69 XBX Caja
70 XBY Tablero, con fleje/ agrupados/ armados
71 XBZ Barras, con fleje/ agrupados/ armados
72 XCA Lata rectangular
73 XCB Cajón para cerveza
74 XCC Mantequera
75 XCD Lata con mango y boquilla
76 XCE Cesto tejido
77 XCF Cofre
78 XCG Contenedor tipo Jaula
79 XCH Cajonera
80 XCI Frasco
81 XCJ Ataúd
82 XCK Barrica
83 XCL Espiral
84 XCM Paquete de tarjetas
85 XCN Contenedor, no especificado como equipo de transporte
86 XCO Garrafón no protegido
87 XCP Garrafón protegido
88 XCQ Cartucho
89 XCR Cajón
90 XCS Estuche
91 XCT Cartón
92 XCU Vaso
93 XCV Cubierta
94 XCW Jaula estilo rodillo
95 XCX Lata cilíndrica
96 XCY Cilindro
97 XCZ Lona
98 XDA Cajón multicapa de plástico
99 XDB Cajón de varias capas de madera
100 XDC Cajón multicapa de cartón
101 XDG Jaula, Según la clasificación de la compañía (Commonwealth Handling Equipment Pool (CHEP))
102 XDH Caja, Según la clasificación de la compañía (CHEP), Eurobox
103 XDI Tambor de hierro
104 XDJ damajuana o garrafa, no protegido
105 XDK Cajón a granel, cartón
106 XDL Cajas de plástico
107 XDM Cajones a granel de madera
108 XDN Dispensador
109 XDP damajuana o garrafa, protegido
110 XDR Tambor
111 XDS Bandeja de una capa sin cubierta y de plástico
112 XDT Bandeja de una capa sin cubierta y de madera
113 XDU Bandeja de una capa sin cubierta y poliestireno
114 XDV Bandeja de una capa sin cubierta y de cartón
115 XDW Bandeja de dos capas sin tapa y con bandeja de plástico
116 XDX Bandeja de dos capas sin cubierta y de madera
117 XDY Bandeja de dos capas sin cubierta y de cartón
118 XEC Bolsa de plástico
119 XED Estuche, con pallet de base
120 XEE Estuche, con pallet base de madera
121 XEF Estuche, con pallet base de cartón
122 XEG Estuche, con pallet base de plástico
123 XEH Estuche, con pallet base de metal
124 XEI Estuche isotérmico
125 XEN Sobre
126 XFB Bolsa flexible
127 XFC Cajón para fruta
128 XFD Cajón enmarcado
129 XFE Tanque flexible
130 XFI Firkin
131 XFL Matraz
132 XFO Cajón para zapatos
133 XFP Caja auxiliar para película fotográfica
134 XFR Marco
135 XFT Contenedor para alimentos
136 XFW Carro de cama plana
137 XFX Bolsa flexible tipo contenedor
138 XGB Botella para gas
139 XGI Viga
140 XGL Contenedor tipo galón
141 XGR Recipiente de vidrio
142 XGU Bandeja contenedor para apilar horizontalmente objetos planos
143 XGY Costal de Yute
144 XGZ Vigas con correas o agrupadas
145 XHA Cesta con mango y de plástico
146 XHB Cesta con mango y de madera
147 XHC Cesta con asa y de cartón
148 XHG Hogshead
149 XHN Gancho
150 XHR Cesto
151 XIA Paquete con pantalla y de madera
152 XIB Paquete con pantalla y de cartón
153 XIC Paquete con pantalla y de plástico
154 XID Paquete con pantalla y de metal
155 XIE Paquete de mostrador.
156 XIF Envase para alimentos
157 XIG Paquete envuelto en papel
158 XIH Tambor de plástico
159 XIK Paquete de cartón con los agujeros para botellas
160 XIL Bandeja rígida con tapa y apilable (CEN TS 14482: 2002)
161 XIN Lingote
162 XIZ Lingotes con correas/ agrupados
163 XJB Bolsa jumbo
164 XJC Bidón rectangular
165 XJG Jarra
166 XJR Tarro
167 XJT Bolsa de yute
168 XJY Bidón, cilíndrico
169 XKG Barrilete
170 XKI Kit (Conjunto de piezas)
171 XLE Valijas
172 XLG Bitácora
173 XLT Lote
174 XLU Caja de arrastre
175 XLV Contenedor pequeño
176 XLZ Registros con fleje/ agrupados/ armados
177 XMA Cajón metálico
178 XMB Múltiplo de bolsas
179 XMC Cajón para leche
180 XME Contenedor de metal
181 XMR Recipiente de metal
182 XMS Saco milti-pared
183 XMT Tapete
184 XMW Contenedor envuelto en plástico
185 XMX Caja pequeña de cerillos
186 XNA No disponible
187 XNE Sin empaque o no empaquetado
188 XNF Sin empaque o no empaquetado, unidad simple
189 XNG Sin empaque o no empaquetado, unidades múltiples
190 XNS Caja nido
191 XNT Red
192 XNU Red de plástico con tubo
193 XNV Red textil con tubo
194 XOA Pallet, Según la clasificación de la compañía (Commonwealth Handling Equipment Pool (CHEP) 40 cm x 60 cm
195 XOB Pallet, Según la clasificación de la compañía (Commonwealth Handling Equipment Pool (CHEP) 80 cm x 120 cm
196 XOC Pallet, Según la clasificación de la compañía (Commonwealth Handling Equipment Pool (CHEP) 100 cm x 120 cm
197 XOD Pallet, AS 4068-1993
198 XOE Pallet, ISO T11
199 XOF Plataforma, peso o dimensión no especificada
200 XOK Bloque
201 XOT Octabin
202 XP2 Charola
203 XPA Cajetilla
204 XPB Pallet, Caja combinada y abierta con caja y pallet.
205 XPC Paquete postal
206 XPD Pallet modular con collares (80cms * 100cms)
207 XPE Pallet modular con collares (80cms * 120cms)
208 XPF Corral
209 XPG Placa
210 XPH Cantaro
211 XPI Pleca
212 XPJ Canastilla
213 XPK Paquete
214 XPL Balde
215 XPN Tablón
216 XPO Bolsa pequeña
217 XPR Contenedor de plástico
218 XPT Maceta
219 XPU Cacerola
220 XPV Tubos, con fleje/ agrupados/ armados
221 XPX Pallet
222 XPY Placas con fleje/ agrupados/ armados
223 XPZ Tablones con fleje/ agrupados/ armados
224 XQA Tambor de acero con cabeza no desmontable
225 XQB Tambor de acero con cabeza extraíble
226 XQC Tambor de aluminio con cabeza no extraíble
227 XQD Tambor de aluminio con cabeza extraíble
228 XQF Tambor, plástico con cabeza no desmontable
229 XQG Tambor, plástico, cabeza extraíble
230 XQH Barril de madera con tapón
231 XQJ Barril de madera con cabeza desprendible
232 XQK Bidón de acero con cabeza no desmontable
233 XQL Bidón de acero con cabeza desmontable
234 XQM Bidón de plástico con cabeza no desmontable
235 XQN Bidón de plástico con cabeza extraíble
236 XQP Caja de madera natural estándar
237 XQQ Caja de madera natural con muros a prueba de filtraciones
238 XQR Caja de plástico expandida
239 XQS Caja de plástico sólida
240 XRD Rod
241 XRG Anillo
242 XRJ Estante, Perchero para ropa
243 XRK Estante
244 XRL Carrete
245 XRO Rollo
246 XRT Red Roja
247 XRZ Varillas con fleje/ agrupados/ armados
248 XSA Saco
249 XSB Losa
250 XSC Cajón poco profundo
251 XSD Huso
252 XSE Baúl
253 XSH Bolsa pequeña hermética
254 XSI Patín
255 XSK Carcasa esqueleto
256 XSL Hoja de deslizamiento
257 XSM Hoja de metal
258 XSO Carrete pequeño
259 XSP Hoja de empaque de plástico
260 XSS Cajón de acero
261 XSU Maleta
262 XSV Sobre de acero
263 XSW Envoltorio
264 XSY Manga
265 XSZ Hojas con fleje/ agrupados/ armados
266 XT1 Tableta
267 XTB Tina
268 XTC Caja para té
269 XTD Tubo plegable
270 XTG Contenedor de tanque genérico
271 XTI Tierce
272 XTK Tanque rectangular
273 XTL Tina con tapa
274 XTN Hojalata
275 XTO Tonel
276 XTR Maletero
277 XTS Estructura
278 XTT Bolsa de mano
279 XTU Tubo
280 XTV Tubo con boquilla
281 XTW Pallet tricapa
282 XTY Tanque cilíndrico
283 XTZ Tubos con fleje/ agrupados/ armados
284 XUC Sin empaque
285 XUN Unidad
286 XVA Tanque
287 XVG Tanque de gas (a 1,031 mbar y 15° C)
288 XVI Frasco pequeño
289 XVK Paquete transportable
290 XVL Contenedor para líquidos a granel
291 XVN Vehículo
292 XVO "Contenedor para sólido de partículas grandes a granel (""nódulos"")"
293 XVP Envasado al vacío
294 XVQ Tanque para Gas licuado (a temperatura / presión anormal)
295 XVR Contenedor para sólidos de partículas granulares a granel (Granos)
296 XVS Contenedor de chatarra a granel
297 XVY "Contenedor para sólido de partículas finas a granel (""polvos"")"
298 XWA Contenedor de granel intermedio
299 XWB Botella de mimbre
300 XWC Contenedor intermedio para gráneles y de acero
301 XWD Contenedor intermedio para gráneles y de aluminio
302 XWF Contenedor intermedio para gráneles y de metal
303 XWG Contenedor intermedio para gráneles y de acero presurizado menor a 10 kpa
304 XWH Contenedor intermedio para gráneles y de aluminio, presurizado menor a 10 kpa
305 XWJ Contenedor intermedio para gráneles y de metal con una presión de 10 kpa
306 XWK Contenedor intermedio para gráneles y de acero para líquido
307 XWL Contenedor intermedio para gráneles y de aluminio para líquido
308 XWM Contenedor intermedio para gráneles y de metal para líquido
309 XWN Contenedor intermedio para gráneles con tejido plástico sin capa con revestimiento
310 XWP Contenedor intermedio para gráneles con tejido plástico y recubierto
311 XWQ Contenedor intermedio para gráneles con tejido plástico con revestimiento
312 XWR Contenedor intermedio para gráneles con tejido plástico, revestido y con forro
313 XWS Contenedor intermedio para gráneles con película de plástico
314 XWT Contenedor intermedio para gráneles textil sin capa / forro
315 XWU Contenedor intermedio para gráneles de madera natural con forro interior
316 XWV Contenedor intermedio para gráneles textil recubierto
317 XWW Contenedor intermedio para gráneles textil con revestimiento
318 XWX Contenedor intermedio para gráneles textil recubierto y con forro
319 XWY Contenedor intermedio para gráneles contrachapado con revestimiento interior
320 XWZ Contenedor intermedio para gráneles de madera reconstituida con revestimiento interior
321 XXA Bolsa de tejido plástico, sin abrigo interior ni forro
322 XXB Bolsa de tejido plástico a prueba de filtraciones
323 XXC Bolsa de tejido plástico resistente al agua
324 XXD Bolsa con película de plástico
325 XXF Bolsa textil sin capa ni forro interior
326 XXG Bolsa textil a prueba de filtraciones
327 XXH Bolsa textil resistente al agua
328 XXJ Bolsa de papel multi-pared
329 XXK Bolsa de papel multi-pared, resistente al agua
330 XYA Empaque compuesto, recipiente de plástico en tambor de acero
331 XYB Empaque compuesto, recipiente de plástico en cajas de acero
332 XYC Empaque compuesto, recipiente de plástico en tambor de aluminio
333 XYD Empaque compuesto, recipiente de plástico en cajón de aluminio
334 XYF Empaque compuesto, recipiente de plástico en caja de madera
335 XYG Empaque compuesto, recipiente de plástico en tambor de madera contrachapada
336 XYH Empaque compuesto, recipiente de plástico en caja de madera contrachapada
337 XYJ Empaque compuesto, recipiente de plástico en tambor de fibra
338 XYK Empaque compuesto, recipiente de plástico en caja de cartón
339 XYL Empaque compuesto, recipiente de plástico en el tambor de plástico
340 XYM Empaque compuesto, recipiente de plástico en caja de plástico sólido
341 XYN Empaque compuesto, receptáculo de vidrio en tambor de acero
342 XYP Empaque compuesto, receptáculo de vidrio en caja de cajas de acero
343 XYQ Empaque compuesto, recipiente de vidrio en tambor de aluminio
344 XYR Empaque compuesto, receptáculo de vidrio en caja de aluminio
345 XYS Empaque compuesto, recipiente de vidrio en caja de madera
346 XYT Empaque compuesto, recipiente de vidrio en tambor de madera contrachapada
347 Xyv Empaque compuesto, recipiente de vidrio en el cesto de mimbre
348 XYW Empaque compuesto, recipiente de vidrio en tambor de fibra
349 XYX Empaque compuesto, recipiente de vidrio en caja de cartón
350 XYY Empaque compuesto, recipiente de vidrio en paquete de plástico expandible
351 XYZ Empaque compuesto, recipiente de vidrio en paquete de plástico sólido
352 XZA Contenedor de granel intermedio, papel, multi-pared
353 XZB Bolsa grande
354 XZC Contenedor intermedio para gráneles de papel, multi-pared y resistente al agua
355 XZD Contenedor intermedio para gráneles de plástico rígido, con equipo estructural para sólidos
356 XZF Contenedor intermedio para gráneles de plástico rígido, autoportante para sólidos
357 XZG Contenedor intermedio para gráneles de plástico rígido, con equipo estructural, presurizado
358 XZH Contenedor intermedio para gráneles de plástico rígido, autoportante y presurizado
359 XZJ Contenedor intermedio para gráneles de plástico rígido, con equipo estructural para líquidos
360 XZK Contenedor intermedio para gráneles de plástico rígido, autoportante, líquidos
361 XZL Contenedor intermedio para gráneles, compuesto y de plástico rígido, sólidos
362 XZM Contenedor intermedio para gráneles, compuesto y de plástico flexible, sólidos
363 XZN Contenedor intermedio para gráneles, compuesto y de plástico rígido, presurizado
364 XZP Contenedor intermedio para gráneles, compuesto y de plástico flexible, presurizado
365 XZQ Contenedor intermedio para gráneles, compuesto y de plástico rígido, líquidos
366 XZR Contenedor intermedio para gráneles, compuesto y de plástico flexible para líquidos
367 XZS Contenedor intermedio para gráneles, compuesto
368 XZT Contenedor intermedio para gráneles con tablero de fibras
369 XZU Contenedor intermedio para gráneles flexible
370 XZV Contenedor intermedio para gráneles de metal, distinto del acero
371 XZW Contenedor intermedio para gráneles, de madera natural
372 XZX Contenedor intermedio para gráneles, de contrachapado
373 XZY Contenedor intermedio para gráneles, de madera reconstituida
374 KGM Kilogramo
375 MC Microgramo
376 DJ Decagramo
377 DG Decigramo
378 GRM Gramo
379 CGM Centigramo
380 TNE Tonelada (tonelada métrica)
381 DTN Decitonelada métrica
382 MGM Miligramo
383 HGM Hectogramo
384 KTN Kilotonelada Métrica
385 2U Megagramo
386 LBR Libra
387 GRN Grano
388 ONZ Onza (avoirdupois)
389 CWI Hundredweight
390 CWA Hundred pound
391 LTN Tonelada (UK) o tonelada larga (estados unidos)
392 STI Estone (UK)
393 STN Tonelada (estados unidos) o tonelada corta (UK y estados unidos)
394 APZ Onza troy u onza farmacéutica
395 F13 Slug
396 K64 Libra (avoirdupois) por grado fahrenheit
397 L69 Tonelada por kelvin
398 L87 Tonelada corta por grado fahrenheit
399 M85 Tonelada, ensayo
400 M86 Libra Alemana
401 J33 Microgramo por kilogramo
402 L32 Nanogramo por kilogramo
403 NA Miligramo por kilogramo
404 M29 Kilogramo por kilogramo
405 M91 Libra por libra
406 Q29 Microgramo por hectogramo
407 MTQ Metro cúbico
408 MAL Megalitro
409 LTR Litro
410 MMQ Milímetro cúbico
411 CMQ Centímetro cúbico
412 DMQ Decímetro cúbico
413 MLT Mililitro
414 HLT Hectolitro
415 CLT Centilitro
416 DMA Decámetro cúbico
417 H19 Hectómetro cúbico
418 H20 Kilómetro cúbico
419 M71 Metro cúbico por pascal (joules)
420 DLT Decilitro
421 4G Microlitro
422 K6 Kilolitro
423 A44 Decalitro
424 G94 Centímetro cúbico por bar
425 G95 Litro por bar
426 G96 Metro cúbico por bar
427 G97 Mililitro por bar
428 5I Pies cúbicos estándar
429 INQ Pulgada cúbica
430 FTQ Pie cúbico
431 YDQ Yarda cúbica
432 GLI Galón (UK)
433 GLL Galón (EUA)
434 PT Pinta (US)
435 PTI Pint (uk)
436 QTI Cuarto (UK)
437 PTL Pinta líquida (estados unidos)
438 QTL Cuarto de líquido (estadis unidos)
439 PTD Pinta seca (estados unidos)
440 OZI Onza líquida (UK)
441 QT Cuarto (EUA)
442 J57 Barril (uk petróleo)
443 K21 Pie cúbico por grado fahrenheit
444 K23 Pie cúbico por psi (libra por pulgada cuadrada)
445 L43 Peck (UK)
446 L61 Pinta (US seco)
447 L62 Cuarto de galón (seco de los EUA)
448 L84 Tonelada (flota UK)
449 L86 Tonelada (flota estados unidos)
450 M11 Yarda cúbica por grado fahrenheit
451 M14 Yarda cúbica por psi (libra por pulgada cuadrada)
452 OZA Onza líquida (estados unidos)
453 BUI Bushel (UK)
454 BUA Bushel (EUA)
455 BLL Barril (EUA)
456 BLD Barril seco (EUA)
457 GLD Galón seco (EUA)
458 QTD Cuarto seco (estados unidos)
459 G26 Estere
460 G21 Taza (unidad de volumen)
461 G24 Cucharada (estados unidos)
462 G25 Cucharilla (estados unidos)
463 G23 Peck
464 M67 Acre-pie
465 M68 Cordón
466 M69 Milla cúbica (reino unido)
467 M70 Unidad tradicional de capacidad de carga
468 Q32 Femtolitro
469 Q33 Picolitro
470 Q34 Nanolitro
471 NM3 Metro cúbico normalizado
472 SM3 Metro cúbico estándar
1 id key name
2 1 Tu Contenedor externo
3 2 X1A Tambor de acero
4 3 X1B Tambor de aluminio
5 4 X1D Tambor contrachapado
6 5 X1F Contenedor flexible
7 6 X1G Tambor de fibra
8 7 X1w Tambor de madera
9 8 X2C Barril de madera
10 9 X3A Bidón de acero
11 10 X3H Bidón de plástico
12 11 X43 Bolsa de gran tamaño
13 12 X44 Bolsa de plástico
14 13 X4A Caja de acero
15 14 X4B Caja de aluminio
16 15 X4C Caja de madera natural
17 16 X4D Caja de contrachapado
18 17 X4F Caja de madera reconstituida
19 18 X4G Caja de cartón
20 19 X4H Caja de plástico
21 20 X5H Bolsa de plástico tejido
22 21 X5L Bolsa textil
23 22 X5M Bolsa de papel
24 23 X6H Recipiente de plástico, Contenedor compuesto.
25 24 X6P Recipiente de vidrio, Contenedor compuesto.
26 25 X7A Estuche para carro
27 26 X7B Estuche de madera
28 27 X8A Pallet de madera
29 28 X8B Cajón de madera
30 29 X8C Madera flejada
31 30 XAA Contenedor intermedio para gráneles de plástico rígido
32 31 XAB Contenedor de fibra
33 32 XAC Contenedor de papel
34 33 XAD Contenedor de madera
35 34 XAE Aerosol
36 35 XAF Pallet modular con collares, 80cms * 60cms
37 36 XAG Pallet o empaquetado
38 37 XAH Pallet, 100cms X 110cms
39 38 XAI Contenedor tipo concha
40 39 XAJ Cono
41 40 XAL Esfera
42 41 XAM Ampolleta no protegida
43 42 XAP Ampolleta protegida
44 43 XAT Atomizador
45 44 XAV Cápsula
46 45 XB4 Cinturón
47 46 XBA Barril
48 47 XBB Bobina
49 48 XBC Cajón para botellas / Estante para botellas
50 49 XBD Tablero
51 50 XBE Flejado
52 51 XBF Globo no protegido
53 52 XBG Bolso
54 53 XBH Manojo
55 54 XBI Compartimiento
56 55 XBJ Cubeta
57 56 XBK Cesta
58 57 XBL Paca comprimida
59 58 XBM Cuenco
60 59 XBN Paca no comprimida
61 60 XBO Botella no-protegida y cilíndrica
62 61 XBP Globo protegido
63 62 XBQ Botella cilíndrica protegida
64 63 XBR Barra
65 64 XBS Botella, no-protegida en forma de bulbo
66 65 XBT Rollo de tela
67 66 XBU Butt
68 67 XBV Botella de bulbo protegido
69 68 XBW Caja para líquidos
70 69 XBX Caja
71 70 XBY Tablero, con fleje/ agrupados/ armados
72 71 XBZ Barras, con fleje/ agrupados/ armados
73 72 XCA Lata rectangular
74 73 XCB Cajón para cerveza
75 74 XCC Mantequera
76 75 XCD Lata con mango y boquilla
77 76 XCE Cesto tejido
78 77 XCF Cofre
79 78 XCG Contenedor tipo Jaula
80 79 XCH Cajonera
81 80 XCI Frasco
82 81 XCJ Ataúd
83 82 XCK Barrica
84 83 XCL Espiral
85 84 XCM Paquete de tarjetas
86 85 XCN Contenedor, no especificado como equipo de transporte
87 86 XCO Garrafón no protegido
88 87 XCP Garrafón protegido
89 88 XCQ Cartucho
90 89 XCR Cajón
91 90 XCS Estuche
92 91 XCT Cartón
93 92 XCU Vaso
94 93 XCV Cubierta
95 94 XCW Jaula estilo rodillo
96 95 XCX Lata cilíndrica
97 96 XCY Cilindro
98 97 XCZ Lona
99 98 XDA Cajón multicapa de plástico
100 99 XDB Cajón de varias capas de madera
101 100 XDC Cajón multicapa de cartón
102 101 XDG Jaula, Según la clasificación de la compañía (Commonwealth Handling Equipment Pool (CHEP))
103 102 XDH Caja, Según la clasificación de la compañía (CHEP), Eurobox
104 103 XDI Tambor de hierro
105 104 XDJ damajuana o garrafa, no protegido
106 105 XDK Cajón a granel, cartón
107 106 XDL Cajas de plástico
108 107 XDM Cajones a granel de madera
109 108 XDN Dispensador
110 109 XDP damajuana o garrafa, protegido
111 110 XDR Tambor
112 111 XDS Bandeja de una capa sin cubierta y de plástico
113 112 XDT Bandeja de una capa sin cubierta y de madera
114 113 XDU Bandeja de una capa sin cubierta y poliestireno
115 114 XDV Bandeja de una capa sin cubierta y de cartón
116 115 XDW Bandeja de dos capas sin tapa y con bandeja de plástico
117 116 XDX Bandeja de dos capas sin cubierta y de madera
118 117 XDY Bandeja de dos capas sin cubierta y de cartón
119 118 XEC Bolsa de plástico
120 119 XED Estuche, con pallet de base
121 120 XEE Estuche, con pallet base de madera
122 121 XEF Estuche, con pallet base de cartón
123 122 XEG Estuche, con pallet base de plástico
124 123 XEH Estuche, con pallet base de metal
125 124 XEI Estuche isotérmico
126 125 XEN Sobre
127 126 XFB Bolsa flexible
128 127 XFC Cajón para fruta
129 128 XFD Cajón enmarcado
130 129 XFE Tanque flexible
131 130 XFI Firkin
132 131 XFL Matraz
133 132 XFO Cajón para zapatos
134 133 XFP Caja auxiliar para película fotográfica
135 134 XFR Marco
136 135 XFT Contenedor para alimentos
137 136 XFW Carro de cama plana
138 137 XFX Bolsa flexible tipo contenedor
139 138 XGB Botella para gas
140 139 XGI Viga
141 140 XGL Contenedor tipo galón
142 141 XGR Recipiente de vidrio
143 142 XGU Bandeja contenedor para apilar horizontalmente objetos planos
144 143 XGY Costal de Yute
145 144 XGZ Vigas con correas o agrupadas
146 145 XHA Cesta con mango y de plástico
147 146 XHB Cesta con mango y de madera
148 147 XHC Cesta con asa y de cartón
149 148 XHG Hogshead
150 149 XHN Gancho
151 150 XHR Cesto
152 151 XIA Paquete con pantalla y de madera
153 152 XIB Paquete con pantalla y de cartón
154 153 XIC Paquete con pantalla y de plástico
155 154 XID Paquete con pantalla y de metal
156 155 XIE Paquete de mostrador.
157 156 XIF Envase para alimentos
158 157 XIG Paquete envuelto en papel
159 158 XIH Tambor de plástico
160 159 XIK Paquete de cartón con los agujeros para botellas
161 160 XIL Bandeja rígida con tapa y apilable (CEN TS 14482: 2002)
162 161 XIN Lingote
163 162 XIZ Lingotes con correas/ agrupados
164 163 XJB Bolsa jumbo
165 164 XJC Bidón rectangular
166 165 XJG Jarra
167 166 XJR Tarro
168 167 XJT Bolsa de yute
169 168 XJY Bidón, cilíndrico
170 169 XKG Barrilete
171 170 XKI Kit (Conjunto de piezas)
172 171 XLE Valijas
173 172 XLG Bitácora
174 173 XLT Lote
175 174 XLU Caja de arrastre
176 175 XLV Contenedor pequeño
177 176 XLZ Registros con fleje/ agrupados/ armados
178 177 XMA Cajón metálico
179 178 XMB Múltiplo de bolsas
180 179 XMC Cajón para leche
181 180 XME Contenedor de metal
182 181 XMR Recipiente de metal
183 182 XMS Saco milti-pared
184 183 XMT Tapete
185 184 XMW Contenedor envuelto en plástico
186 185 XMX Caja pequeña de cerillos
187 186 XNA No disponible
188 187 XNE Sin empaque o no empaquetado
189 188 XNF Sin empaque o no empaquetado, unidad simple
190 189 XNG Sin empaque o no empaquetado, unidades múltiples
191 190 XNS Caja nido
192 191 XNT Red
193 192 XNU Red de plástico con tubo
194 193 XNV Red textil con tubo
195 194 XOA Pallet, Según la clasificación de la compañía (Commonwealth Handling Equipment Pool (CHEP) 40 cm x 60 cm
196 195 XOB Pallet, Según la clasificación de la compañía (Commonwealth Handling Equipment Pool (CHEP) 80 cm x 120 cm
197 196 XOC Pallet, Según la clasificación de la compañía (Commonwealth Handling Equipment Pool (CHEP) 100 cm x 120 cm
198 197 XOD Pallet, AS 4068-1993
199 198 XOE Pallet, ISO T11
200 199 XOF Plataforma, peso o dimensión no especificada
201 200 XOK Bloque
202 201 XOT Octabin
203 202 XP2 Charola
204 203 XPA Cajetilla
205 204 XPB Pallet, Caja combinada y abierta con caja y pallet.
206 205 XPC Paquete postal
207 206 XPD Pallet modular con collares (80cms * 100cms)
208 207 XPE Pallet modular con collares (80cms * 120cms)
209 208 XPF Corral
210 209 XPG Placa
211 210 XPH Cantaro
212 211 XPI Pleca
213 212 XPJ Canastilla
214 213 XPK Paquete
215 214 XPL Balde
216 215 XPN Tablón
217 216 XPO Bolsa pequeña
218 217 XPR Contenedor de plástico
219 218 XPT Maceta
220 219 XPU Cacerola
221 220 XPV Tubos, con fleje/ agrupados/ armados
222 221 XPX Pallet
223 222 XPY Placas con fleje/ agrupados/ armados
224 223 XPZ Tablones con fleje/ agrupados/ armados
225 224 XQA Tambor de acero con cabeza no desmontable
226 225 XQB Tambor de acero con cabeza extraíble
227 226 XQC Tambor de aluminio con cabeza no extraíble
228 227 XQD Tambor de aluminio con cabeza extraíble
229 228 XQF Tambor, plástico con cabeza no desmontable
230 229 XQG Tambor, plástico, cabeza extraíble
231 230 XQH Barril de madera con tapón
232 231 XQJ Barril de madera con cabeza desprendible
233 232 XQK Bidón de acero con cabeza no desmontable
234 233 XQL Bidón de acero con cabeza desmontable
235 234 XQM Bidón de plástico con cabeza no desmontable
236 235 XQN Bidón de plástico con cabeza extraíble
237 236 XQP Caja de madera natural estándar
238 237 XQQ Caja de madera natural con muros a prueba de filtraciones
239 238 XQR Caja de plástico expandida
240 239 XQS Caja de plástico sólida
241 240 XRD Rod
242 241 XRG Anillo
243 242 XRJ Estante, Perchero para ropa
244 243 XRK Estante
245 244 XRL Carrete
246 245 XRO Rollo
247 246 XRT Red Roja
248 247 XRZ Varillas con fleje/ agrupados/ armados
249 248 XSA Saco
250 249 XSB Losa
251 250 XSC Cajón poco profundo
252 251 XSD Huso
253 252 XSE Baúl
254 253 XSH Bolsa pequeña hermética
255 254 XSI Patín
256 255 XSK Carcasa esqueleto
257 256 XSL Hoja de deslizamiento
258 257 XSM Hoja de metal
259 258 XSO Carrete pequeño
260 259 XSP Hoja de empaque de plástico
261 260 XSS Cajón de acero
262 261 XSU Maleta
263 262 XSV Sobre de acero
264 263 XSW Envoltorio
265 264 XSY Manga
266 265 XSZ Hojas con fleje/ agrupados/ armados
267 266 XT1 Tableta
268 267 XTB Tina
269 268 XTC Caja para té
270 269 XTD Tubo plegable
271 270 XTG Contenedor de tanque genérico
272 271 XTI Tierce
273 272 XTK Tanque rectangular
274 273 XTL Tina con tapa
275 274 XTN Hojalata
276 275 XTO Tonel
277 276 XTR Maletero
278 277 XTS Estructura
279 278 XTT Bolsa de mano
280 279 XTU Tubo
281 280 XTV Tubo con boquilla
282 281 XTW Pallet tricapa
283 282 XTY Tanque cilíndrico
284 283 XTZ Tubos con fleje/ agrupados/ armados
285 284 XUC Sin empaque
286 285 XUN Unidad
287 286 XVA Tanque
288 287 XVG Tanque de gas (a 1,031 mbar y 15° C)
289 288 XVI Frasco pequeño
290 289 XVK Paquete transportable
291 290 XVL Contenedor para líquidos a granel
292 291 XVN Vehículo
293 292 XVO Contenedor para sólido de partículas grandes a granel ("nódulos")
294 293 XVP Envasado al vacío
295 294 XVQ Tanque para Gas licuado (a temperatura / presión anormal)
296 295 XVR Contenedor para sólidos de partículas granulares a granel (Granos)
297 296 XVS Contenedor de chatarra a granel
298 297 XVY Contenedor para sólido de partículas finas a granel ("polvos")
299 298 XWA Contenedor de granel intermedio
300 299 XWB Botella de mimbre
301 300 XWC Contenedor intermedio para gráneles y de acero
302 301 XWD Contenedor intermedio para gráneles y de aluminio
303 302 XWF Contenedor intermedio para gráneles y de metal
304 303 XWG Contenedor intermedio para gráneles y de acero presurizado menor a 10 kpa
305 304 XWH Contenedor intermedio para gráneles y de aluminio, presurizado menor a 10 kpa
306 305 XWJ Contenedor intermedio para gráneles y de metal con una presión de 10 kpa
307 306 XWK Contenedor intermedio para gráneles y de acero para líquido
308 307 XWL Contenedor intermedio para gráneles y de aluminio para líquido
309 308 XWM Contenedor intermedio para gráneles y de metal para líquido
310 309 XWN Contenedor intermedio para gráneles con tejido plástico sin capa con revestimiento
311 310 XWP Contenedor intermedio para gráneles con tejido plástico y recubierto
312 311 XWQ Contenedor intermedio para gráneles con tejido plástico con revestimiento
313 312 XWR Contenedor intermedio para gráneles con tejido plástico, revestido y con forro
314 313 XWS Contenedor intermedio para gráneles con película de plástico
315 314 XWT Contenedor intermedio para gráneles textil sin capa / forro
316 315 XWU Contenedor intermedio para gráneles de madera natural con forro interior
317 316 XWV Contenedor intermedio para gráneles textil recubierto
318 317 XWW Contenedor intermedio para gráneles textil con revestimiento
319 318 XWX Contenedor intermedio para gráneles textil recubierto y con forro
320 319 XWY Contenedor intermedio para gráneles contrachapado con revestimiento interior
321 320 XWZ Contenedor intermedio para gráneles de madera reconstituida con revestimiento interior
322 321 XXA Bolsa de tejido plástico, sin abrigo interior ni forro
323 322 XXB Bolsa de tejido plástico a prueba de filtraciones
324 323 XXC Bolsa de tejido plástico resistente al agua
325 324 XXD Bolsa con película de plástico
326 325 XXF Bolsa textil sin capa ni forro interior
327 326 XXG Bolsa textil a prueba de filtraciones
328 327 XXH Bolsa textil resistente al agua
329 328 XXJ Bolsa de papel multi-pared
330 329 XXK Bolsa de papel multi-pared, resistente al agua
331 330 XYA Empaque compuesto, recipiente de plástico en tambor de acero
332 331 XYB Empaque compuesto, recipiente de plástico en cajas de acero
333 332 XYC Empaque compuesto, recipiente de plástico en tambor de aluminio
334 333 XYD Empaque compuesto, recipiente de plástico en cajón de aluminio
335 334 XYF Empaque compuesto, recipiente de plástico en caja de madera
336 335 XYG Empaque compuesto, recipiente de plástico en tambor de madera contrachapada
337 336 XYH Empaque compuesto, recipiente de plástico en caja de madera contrachapada
338 337 XYJ Empaque compuesto, recipiente de plástico en tambor de fibra
339 338 XYK Empaque compuesto, recipiente de plástico en caja de cartón
340 339 XYL Empaque compuesto, recipiente de plástico en el tambor de plástico
341 340 XYM Empaque compuesto, recipiente de plástico en caja de plástico sólido
342 341 XYN Empaque compuesto, receptáculo de vidrio en tambor de acero
343 342 XYP Empaque compuesto, receptáculo de vidrio en caja de cajas de acero
344 343 XYQ Empaque compuesto, recipiente de vidrio en tambor de aluminio
345 344 XYR Empaque compuesto, receptáculo de vidrio en caja de aluminio
346 345 XYS Empaque compuesto, recipiente de vidrio en caja de madera
347 346 XYT Empaque compuesto, recipiente de vidrio en tambor de madera contrachapada
348 347 Xyv Empaque compuesto, recipiente de vidrio en el cesto de mimbre
349 348 XYW Empaque compuesto, recipiente de vidrio en tambor de fibra
350 349 XYX Empaque compuesto, recipiente de vidrio en caja de cartón
351 350 XYY Empaque compuesto, recipiente de vidrio en paquete de plástico expandible
352 351 XYZ Empaque compuesto, recipiente de vidrio en paquete de plástico sólido
353 352 XZA Contenedor de granel intermedio, papel, multi-pared
354 353 XZB Bolsa grande
355 354 XZC Contenedor intermedio para gráneles de papel, multi-pared y resistente al agua
356 355 XZD Contenedor intermedio para gráneles de plástico rígido, con equipo estructural para sólidos
357 356 XZF Contenedor intermedio para gráneles de plástico rígido, autoportante para sólidos
358 357 XZG Contenedor intermedio para gráneles de plástico rígido, con equipo estructural, presurizado
359 358 XZH Contenedor intermedio para gráneles de plástico rígido, autoportante y presurizado
360 359 XZJ Contenedor intermedio para gráneles de plástico rígido, con equipo estructural para líquidos
361 360 XZK Contenedor intermedio para gráneles de plástico rígido, autoportante, líquidos
362 361 XZL Contenedor intermedio para gráneles, compuesto y de plástico rígido, sólidos
363 362 XZM Contenedor intermedio para gráneles, compuesto y de plástico flexible, sólidos
364 363 XZN Contenedor intermedio para gráneles, compuesto y de plástico rígido, presurizado
365 364 XZP Contenedor intermedio para gráneles, compuesto y de plástico flexible, presurizado
366 365 XZQ Contenedor intermedio para gráneles, compuesto y de plástico rígido, líquidos
367 366 XZR Contenedor intermedio para gráneles, compuesto y de plástico flexible para líquidos
368 367 XZS Contenedor intermedio para gráneles, compuesto
369 368 XZT Contenedor intermedio para gráneles con tablero de fibras
370 369 XZU Contenedor intermedio para gráneles flexible
371 370 XZV Contenedor intermedio para gráneles de metal, distinto del acero
372 371 XZW Contenedor intermedio para gráneles, de madera natural
373 372 XZX Contenedor intermedio para gráneles, de contrachapado
374 373 XZY Contenedor intermedio para gráneles, de madera reconstituida
375 374 KGM Kilogramo
376 375 MC Microgramo
377 376 DJ Decagramo
378 377 DG Decigramo
379 378 GRM Gramo
380 379 CGM Centigramo
381 380 TNE Tonelada (tonelada métrica)
382 381 DTN Decitonelada métrica
383 382 MGM Miligramo
384 383 HGM Hectogramo
385 384 KTN Kilotonelada Métrica
386 385 2U Megagramo
387 386 LBR Libra
388 387 GRN Grano
389 388 ONZ Onza (avoirdupois)
390 389 CWI Hundredweight
391 390 CWA Hundred pound
392 391 LTN Tonelada (UK) o tonelada larga (estados unidos)
393 392 STI Estone (UK)
394 393 STN Tonelada (estados unidos) o tonelada corta (UK y estados unidos)
395 394 APZ Onza troy u onza farmacéutica
396 395 F13 Slug
397 396 K64 Libra (avoirdupois) por grado fahrenheit
398 397 L69 Tonelada por kelvin
399 398 L87 Tonelada corta por grado fahrenheit
400 399 M85 Tonelada, ensayo
401 400 M86 Libra Alemana
402 401 J33 Microgramo por kilogramo
403 402 L32 Nanogramo por kilogramo
404 403 NA Miligramo por kilogramo
405 404 M29 Kilogramo por kilogramo
406 405 M91 Libra por libra
407 406 Q29 Microgramo por hectogramo
408 407 MTQ Metro cúbico
409 408 MAL Megalitro
410 409 LTR Litro
411 410 MMQ Milímetro cúbico
412 411 CMQ Centímetro cúbico
413 412 DMQ Decímetro cúbico
414 413 MLT Mililitro
415 414 HLT Hectolitro
416 415 CLT Centilitro
417 416 DMA Decámetro cúbico
418 417 H19 Hectómetro cúbico
419 418 H20 Kilómetro cúbico
420 419 M71 Metro cúbico por pascal (joules)
421 420 DLT Decilitro
422 421 4G Microlitro
423 422 K6 Kilolitro
424 423 A44 Decalitro
425 424 G94 Centímetro cúbico por bar
426 425 G95 Litro por bar
427 426 G96 Metro cúbico por bar
428 427 G97 Mililitro por bar
429 428 5I Pies cúbicos estándar
430 429 INQ Pulgada cúbica
431 430 FTQ Pie cúbico
432 431 YDQ Yarda cúbica
433 432 GLI Galón (UK)
434 433 GLL Galón (EUA)
435 434 PT Pinta (US)
436 435 PTI Pint (uk)
437 436 QTI Cuarto (UK)
438 437 PTL Pinta líquida (estados unidos)
439 438 QTL Cuarto de líquido (estadis unidos)
440 439 PTD Pinta seca (estados unidos)
441 440 OZI Onza líquida (UK)
442 441 QT Cuarto (EUA)
443 442 J57 Barril (uk petróleo)
444 443 K21 Pie cúbico por grado fahrenheit
445 444 K23 Pie cúbico por psi (libra por pulgada cuadrada)
446 445 L43 Peck (UK)
447 446 L61 Pinta (US seco)
448 447 L62 Cuarto de galón (seco de los EUA)
449 448 L84 Tonelada (flota UK)
450 449 L86 Tonelada (flota estados unidos)
451 450 M11 Yarda cúbica por grado fahrenheit
452 451 M14 Yarda cúbica por psi (libra por pulgada cuadrada)
453 452 OZA Onza líquida (estados unidos)
454 453 BUI Bushel (UK)
455 454 BUA Bushel (EUA)
456 455 BLL Barril (EUA)
457 456 BLD Barril seco (EUA)
458 457 GLD Galón seco (EUA)
459 458 QTD Cuarto seco (estados unidos)
460 459 G26 Estere
461 460 G21 Taza (unidad de volumen)
462 461 G24 Cucharada (estados unidos)
463 462 G25 Cucharilla (estados unidos)
464 463 G23 Peck
465 464 M67 Acre-pie
466 465 M68 Cordón
467 466 M69 Milla cúbica (reino unido)
468 467 M70 Unidad tradicional de capacidad de carga
469 468 Q32 Femtolitro
470 469 Q33 Picolitro
471 470 Q34 Nanolitro
472 471 NM3 Metro cúbico normalizado
473 472 SM3 Metro cúbico estándar

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
/* /*
@license @license
webix UI v.5.0.0 webix UI v.5.4.3
This software is covered by Webix Commercial License. This software is covered by Webix Commercial License.
Usage without proper license is prohibited. Usage without proper license is prohibited.
(c) XB Software Ltd. (c) XB Software Ltd.
@ -9,12 +9,12 @@ Usage without proper license is prohibited.
.webix_view.webix_form{background-color:#fff} .webix_view.webix_form{background-color:#fff}
body{-webkit-tap-highlight-color:transparent!important} body{-webkit-tap-highlight-color:transparent!important}
.webix_abslayout{position:relative} .webix_abslayout{position:relative}
.webix_abslayout>.webix_view{position:absolute} .webix_abslayout>.webix_view{position:absolute!important}
.webix_layout_space,.webix_layout_wide{background-color:#ebebeb} .webix_layout_space,.webix_layout_wide{background-color:#ebebeb}
.webix_layout_accordion,.webix_layout_clean,.webix_layout_line,.webix_multiview{background:0 0} .webix_layout_accordion,.webix_layout_clean,.webix_layout_line,.webix_multiview{background:0 0}
.webix_overlay{width:100%;height:100%;position:absolute;z-index:10;text-align:center;padding-top:10px} .webix_overlay{width:100%;height:100%;position:absolute;z-index:10;text-align:center;padding-top:10px}
.webix_view>.webix_disabled{width:100%;height:100%;position:absolute;top:0;left:0;background-color:#ddd;opacity:.4;filter:alpha(opacity=40);z-index:1} .webix_view>.webix_disabled{width:100%;height:100%;position:absolute;top:0;left:0;background-color:#ddd;opacity:.4;filter:alpha(opacity=40);z-index:1}
.webix_disabled_view{overflow:hidden!important} .webix_disabled_view{overflow:hidden!important;position:relative}
body.webix_full_screen{margin:0;padding:0;overflow:hidden} body.webix_full_screen{margin:0;padding:0;overflow:hidden}
.webix_noselect,.webix_noselect div{-moz-user-select:none;-webkit-user-select:none;-ms-user-select:none;user-select:none} .webix_noselect,.webix_noselect div{-moz-user-select:none;-webkit-user-select:none;-ms-user-select:none;user-select:none}
.webix_selectable{-moz-user-select:text;-webkit-user-select:text;user-select:text} .webix_selectable{-moz-user-select:text;-webkit-user-select:text;user-select:text}
@ -25,38 +25,38 @@ body.webix_full_screen{margin:0;padding:0;overflow:hidden}
.webix_section>.webix_template{padding:0 8px;background-color:#fff;display:inline} .webix_section>.webix_template{padding:0 8px;background-color:#fff;display:inline}
.webix_layout_space>.webix_section>.webix_template,.webix_layout_wide>.webix_section>.webix_template{padding:0 8px;background-color:#ebebeb} .webix_layout_space>.webix_section>.webix_template,.webix_layout_wide>.webix_section>.webix_template{padding:0 8px;background-color:#ebebeb}
.webix_layout_clean>.webix_section>.webix_template,.webix_layout_line>.webix_section>.webix_template{padding:0 8px;background-color:#fff} .webix_layout_clean>.webix_section>.webix_template,.webix_layout_line>.webix_section>.webix_template{padding:0 8px;background-color:#fff}
.webix_header>div{padding-left:10px;font-family:Helvetica,Verdana;font-size:13px;font-weight:700;color:#1e2022!important;box-shadow:0 1px 1px #fff inset;background:0 1px repeat-x #e5f1ff;background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#eaf3ff),color-stop(50%,#e5f1ff),color-stop(100%,#d6e8ff)) 0 1px repeat-x;background-image:-webkit-linear-gradient(top,#eaf3ff 0,#e5f1ff 50%,#d6e8ff 100%);background-image:-moz-linear-gradient(top,#eaf3ff 0,#e5f1ff 60%,#d6e8ff 100%);background-image:-ms-linear-gradient(top,#eaf3ff 0,#e5f1ff 60%,#d6e8ff 100%);background-image:-o-linear-gradient(top,#eaf3ff 0,#e5f1ff 60%,#d6e8ff 100%);line-height:32px;line-height:34px;padding-top:0;padding-bottom:0} .webix_header>div{padding-left:10px;font-family:Helvetica,Verdana;color:#1e2022;font-size:13px;font-weight:700;color:#1e2022!important;box-shadow:0 1px 1px #fff inset;background:#e5f1ff;background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#eaf3ff),color-stop(50%,#e5f1ff),color-stop(100%,#d6e8ff));background-image:-webkit-linear-gradient(top,#eaf3ff 0,#e5f1ff 50%,#d6e8ff 100%);background-image:-moz-linear-gradient(top,#eaf3ff 0,#e5f1ff 60%,#d6e8ff 100%);background-image:-ms-linear-gradient(top,#eaf3ff 0,#e5f1ff 60%,#d6e8ff 100%);background-image:-o-linear-gradient(top,#eaf3ff 0,#e5f1ff 60%,#d6e8ff 100%);background-position:0 1px;background-repeat:repeat-x;line-height:32px;line-height:34px;padding-top:0;padding-bottom:0}
.webix_header>div .webix_el_label,.webix_header>div .webix_inp_label{color:#1e2022} .webix_header>div .webix_el_label,.webix_header>div .webix_inp_label{color:#1e2022}
.webix_spacer{background:0 0} .webix_spacer{background:0 0}
.webix_view_animate{position:relative;width:100%;height:100%} .webix_view_animate{position:relative;width:100%;height:100%}
x\:ui{display:none} x\:ui{display:none}
.webix_flexlayout{display:flex!important;flex-wrap:wrap;flex-direction:row;overflow:hidden;box-sizing:border-box} .webix_view.webix_flexlayout{display:flex!important;flex-wrap:wrap;flex-direction:row;overflow:hidden;box-sizing:border-box}
.webix_flexlayout>div{display:block!important} .webix_flexlayout>div{display:block!important}
.webix_view_align{background:0 0} .webix_view_align{background:0 0}
.webix_view a.webix_list_item,.webix_view div,.webix_view span{outline:0} .webix_view a.webix_list_item,.webix_view div,.webix_view span{outline:0}
.webix_view a.webix_list_item:focus,.webix_view div:focus,.webix_view span:focus,.webix_view.webix_window:focus{outline:#727981 dotted 1px} .webix_view a.webix_list_item:focus,.webix_view div:focus,.webix_view span:focus,.webix_view.webix_window:focus{outline-color:#727981;outline-style:dotted;outline-width:1px}
.webix_view div.webix_inp_static:focus,button,input,select,textarea{outline:0} .webix_view div.webix_inp_static:focus,button,input,select,textarea{outline:0}
.webix_resize_area{position:absolute;top:0;left:0;width:100%;height:100%} .webix_resize_area{position:absolute;top:0;left:0;width:100%;height:100%}
.webix_resize_handle_x .webix_handle_content,.webix_resize_handle_y .webix_handle_content{width:100%;height:100%;background:rgba(100,100,100,.1);border:1px dotted #9fa5aa} .webix_resize_handle_x .webix_handle_content,.webix_resize_handle_y .webix_handle_content{width:100%;height:100%;background:rgba(100,100,100,.1);border:1px dotted #9fa5aa}
.webix_resize_handle_x{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box;cursor:e-resize;width:1px;height:100%;position:absolute;z-index:2;top:0} .webix_resize_handle_x{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box;cursor:ew-resize;width:1px;height:100%;position:absolute;z-index:2;top:0}
.webix_resize_handle_x .webix_handle_content{border-width:0 1px} .webix_resize_handle_x .webix_handle_content{border-width:0 1px}
.webix_resize_origin_x{cursor:e-resize;width:3px;height:100%;position:absolute;z-index:2;top:0} .webix_resize_origin_x{cursor:ew-resize;width:3px;height:100%;position:absolute;z-index:2;top:0}
.webix_resize_handle_y{cursor:n-resize;height:1px;width:100%;position:absolute;z-index:2;left:0} .webix_resize_handle_y{cursor:ns-resize;height:1px;width:100%;position:absolute;z-index:2;left:0}
.webix_resize_handle_y .webix_handle_content{border-width:1px 0} .webix_resize_handle_y .webix_handle_content{border-width:1px 0}
.webix_resize_origin_y{cursor:n-resize;height:3px;width:100%;position:absolute;z-index:2;left:0} .webix_resize_origin_y{cursor:ns-resize;height:3px;width:100%;position:absolute;z-index:2;left:0}
.webix_resizer.webix_disabled_view{cursor:default} .webix_resizer.webix_disabled_view{cursor:default}
.webix_resizer_vy{cursor:n-resize;margin-top:0!important;border:0!important;padding:0!important;background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABcAAAADAQMAAABCowZuAAAABlBMVEUAAACkvtSA7tmIAAAAAXRSTlMAQObYZgAAAA9JREFUCB1jXMUABoxAGgAJaAFXPIkJqAAAAABJRU5ErkJggg==) center center no-repeat} .webix_resizer_vy{cursor:ns-resize;margin-top:0!important;border:0!important;padding:0!important;background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABcAAAADAQMAAABCowZuAAAABlBMVEUAAACkvtSA7tmIAAAAAXRSTlMAQObYZgAAAA9JREFUCB1jXMUABoxAGgAJaAFXPIkJqAAAAABJRU5ErkJggg==) no-repeat center center}
.webix_resizer_vx{cursor:e-resize;margin-left:0!important;border:0!important;padding:0!important;background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAXAQMAAAD0oWdxAAAABlBMVEUAAACkvtSA7tmIAAAAAXRSTlMAQObYZgAAAA5JREFUeF5jWMDAQDwGAKyuB4FpGQdzAAAAAElFTkSuQmCC) center center no-repeat} .webix_resizer_vx{cursor:ew-resize;margin-left:0!important;border:0!important;padding:0!important;background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAXAQMAAAD0oWdxAAAABlBMVEUAAACkvtSA7tmIAAAAAXRSTlMAQObYZgAAAA5JREFUeF5jWMDAQDwGAKyuB4FpGQdzAAAAAElFTkSuQmCC) no-repeat center center}
.webix_resizer_y{cursor:n-resize;background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABcAAAADAQMAAABCowZuAAAABlBMVEUAAACkvtSA7tmIAAAAAXRSTlMAQObYZgAAAA9JREFUCB1jXMUABoxAGgAJaAFXPIkJqAAAAABJRU5ErkJggg==) center center no-repeat #fff} .webix_resizer_y{cursor:ns-resize;background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABcAAAADAQMAAABCowZuAAAABlBMVEUAAACkvtSA7tmIAAAAAXRSTlMAQObYZgAAAA9JREFUCB1jXMUABoxAGgAJaAFXPIkJqAAAAABJRU5ErkJggg==) no-repeat center center;background-color:#fff}
.webix_resizer_x{cursor:e-resize;background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAXAQMAAAD0oWdxAAAABlBMVEUAAACkvtSA7tmIAAAAAXRSTlMAQObYZgAAAA5JREFUeF5jWMDAQDwGAKyuB4FpGQdzAAAAAElFTkSuQmCC) center center no-repeat #fff} .webix_resizer_x{cursor:ew-resize;background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAXAQMAAAD0oWdxAAAABlBMVEUAAACkvtSA7tmIAAAAAXRSTlMAQObYZgAAAA5JREFUeF5jWMDAQDwGAKyuB4FpGQdzAAAAAElFTkSuQmCC) no-repeat center center;background-color:#fff}
.webix_accordionitem_body{position:relative} .webix_accordionitem_body{position:relative}
.webix_accordionitem_header{border-bottom:1px solid #a4bed4;position:relative;z-index:2;font-family:Helvetica,Verdana;font-size:13px;font-weight:700;color:#1e2022!important;box-shadow:0 1px 1px #fff inset;background:0 1px repeat-x #e5f1ff;background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#eaf3ff),color-stop(50%,#e5f1ff),color-stop(100%,#d6e8ff)) 0 1px repeat-x;background-image:-webkit-linear-gradient(top,#eaf3ff 0,#e5f1ff 50%,#d6e8ff 100%);background-image:-moz-linear-gradient(top,#eaf3ff 0,#e5f1ff 60%,#d6e8ff 100%);background-image:-ms-linear-gradient(top,#eaf3ff 0,#e5f1ff 60%,#d6e8ff 100%);background-image:-o-linear-gradient(top,#eaf3ff 0,#e5f1ff 60%,#d6e8ff 100%);line-height:32px;cursor:pointer;overflow:hidden} .webix_accordionitem_header{border-bottom:1px solid #a4bed4;position:relative;z-index:2;font-family:Helvetica,Verdana;color:#1e2022;font-size:13px;font-weight:700;color:#1e2022!important;box-shadow:0 1px 1px #fff inset;background:#e5f1ff;background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#eaf3ff),color-stop(50%,#e5f1ff),color-stop(100%,#d6e8ff));background-image:-webkit-linear-gradient(top,#eaf3ff 0,#e5f1ff 50%,#d6e8ff 100%);background-image:-moz-linear-gradient(top,#eaf3ff 0,#e5f1ff 60%,#d6e8ff 100%);background-image:-ms-linear-gradient(top,#eaf3ff 0,#e5f1ff 60%,#d6e8ff 100%);background-image:-o-linear-gradient(top,#eaf3ff 0,#e5f1ff 60%,#d6e8ff 100%);background-position:0 1px;background-repeat:repeat-x;line-height:32px;cursor:pointer;overflow:hidden}
.webix_accordionitem_header .webix_el_label,.webix_accordionitem_header .webix_inp_label{color:#1e2022} .webix_accordionitem_header .webix_el_label,.webix_accordionitem_header .webix_inp_label{color:#1e2022}
.webix_ie.horizontal>.webix_accordionitem_header.collapsed{-ms-writing-mode:tb-lr} .webix_ie.horizontal>.webix_accordionitem_header.collapsed{-ms-writing-mode:tb-lr}
.webix_ie.horizontal>.webix_accordionitem_header.collapsed .webix_accordionitem_label{padding-left:0;padding-top:10px} .webix_ie.horizontal>.webix_accordionitem_header.collapsed .webix_accordionitem_label{padding-left:0;padding-top:10px}
.webix_ie.horizontal>.webix_accordionitem_header.collapsed .webix_accordionitem_button{margin:0} .webix_ie.horizontal>.webix_accordionitem_header.collapsed .webix_accordionitem_button{margin:0}
.webix_accordionitem_label{height:100%;padding-left:10px} .webix_accordionitem_label{height:100%;padding-left:10px}
.webix_accordionitem_button{float:right;position:relative;height:11px;width:11px;margin:14px} .webix_accordionitem_button{float:right;position:relative;height:11px;width:11px;margin:14px 14px}
.webix_carousel{position:relative} .webix_carousel{position:relative}
.webix_nav_panel{bottom:15px;height:auto;line-height:8px;overflow:hidden;position:absolute;text-align:center} .webix_nav_panel{bottom:15px;height:auto;line-height:8px;overflow:hidden;position:absolute;text-align:center}
.webix_nav_item{display:inline-block;height:8px;padding:6px 5px 6px 4px;width:8px;cursor:pointer;-moz-user-select:none;-webkit-user-select:none;-ms-user-select:none;user-select:none} .webix_nav_item{display:inline-block;height:8px;padding:6px 5px 6px 4px;width:8px;cursor:pointer;-moz-user-select:none;-webkit-user-select:none;-ms-user-select:none;user-select:none}
@ -89,10 +89,10 @@ x\:ui{display:none}
.webix_dark .webix_nav_button_side.webix_nav_button_next .webix_nav_button_inner{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABUAAAAiCAYAAACwaJKDAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3gcdCSE1IU5u4gAAAW1JREFUSMet1rtOwzAUgOG/5jbBwsQD8BAwIAa2TggegjmRgELbuFeo1GRlQEJMMDN264CUiYdgg0dolzIkFUEk9nHSbLGtT76cc+xaMA53gUegDnwCgfa9Vyp8CngCToFNYB940WF0WRWt57SPqsAK+C7oG+kwuiqLdg3992Xgtelk8jGN4w3gqGDMyTSOZ8eHB+8uM0X7XhMYGMbd6TC6dkIzcH8VsMr+aN9rCeCGEyqEhzZY5TWmcM8C3zihKdy2wIMiWJmWURZWtk1P4a4FvnVCUziwwP0srKQBLYSbALXFYuGU1zqMNBAYhrRUiSI0s/RvKcdZNoChcQt8Tz5TKSg/fQdQFqdJcJvAXha0Z1QCDixgW577drCbBxZXKRkYyOtpkm6lwf+VPwH7VcC/d5Qd7EjA39s0KQQ2UIvvfbZ3zoEHU2Rp3+u4vlDGqwSX6N4qwSX6ltMelAUB1oELYA6cAV/poTxXeZ/+AOFqn1e/Gh7yAAAAAElFTkSuQmCC)} .webix_dark .webix_nav_button_side.webix_nav_button_next .webix_nav_button_inner{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABUAAAAiCAYAAACwaJKDAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3gcdCSE1IU5u4gAAAW1JREFUSMet1rtOwzAUgOG/5jbBwsQD8BAwIAa2TggegjmRgELbuFeo1GRlQEJMMDN264CUiYdgg0dolzIkFUEk9nHSbLGtT76cc+xaMA53gUegDnwCgfa9Vyp8CngCToFNYB940WF0WRWt57SPqsAK+C7oG+kwuiqLdg3992Xgtelk8jGN4w3gqGDMyTSOZ8eHB+8uM0X7XhMYGMbd6TC6dkIzcH8VsMr+aN9rCeCGEyqEhzZY5TWmcM8C3zihKdy2wIMiWJmWURZWtk1P4a4FvnVCUziwwP0srKQBLYSbALXFYuGU1zqMNBAYhrRUiSI0s/RvKcdZNoChcQt8Tz5TKSg/fQdQFqdJcJvAXha0Z1QCDixgW577drCbBxZXKRkYyOtpkm6lwf+VPwH7VcC/d5Qd7EjA39s0KQQ2UIvvfbZ3zoEHU2Rp3+u4vlDGqwSX6N4qwSX6ltMelAUB1oELYA6cAV/poTxXeZ/+AOFqn1e/Gh7yAAAAAElFTkSuQmCC)}
.webix_list_item{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box;padding:2px 10px;line-height:23px;cursor:pointer;border-bottom:1px solid #ebebeb;text-align:left} .webix_list_item{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box;padding:2px 10px;line-height:23px;cursor:pointer;border-bottom:1px solid #ebebeb;text-align:left}
.webix_list_item.webix_invalid{background-color:#fee} .webix_list_item.webix_invalid{background-color:#fee}
.webix_list_item.webix_selected{color:#1e2022;background:#ffdb8f;background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#ffebc2),color-stop(100%,#ffdb8f));background:-webkit-linear-gradient(#ffebc2,#ffdb8f);background:-moz-linear-gradient(#ffebc2,#ffdb8f);background:-ms-linear-gradient(top,#ffebc2,#ffdb8f);background:-o-linear-gradient(top,#ffebc2,#ffdb8f)} .webix_list_item.webix_selected{background-size:100% 100%;background-position:0 0;color:#1e2022;background:#ffdb8f;background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#ffebc2),color-stop(100%,#ffdb8f));background-image:-webkit-linear-gradient(#ffebc2,#ffdb8f);background-image:-moz-linear-gradient(#ffebc2,#ffdb8f);background-image:-ms-linear-gradient(top,#ffebc2,#ffdb8f);background-image:-o-linear-gradient(top,#ffebc2,#ffdb8f)}
.webix_list .webix_list_item.webix_selected{border-bottom:1px solid #ffd47b;box-shadow:0 1px #ffe3a9 inset;padding:2px 10px} .webix_list .webix_list_item.webix_selected{border-bottom:1px solid #ffd47b;box-shadow:0 1px #ffe3a9 inset;padding:2px 10px}
.webix_group{position:relative} .webix_group{position:relative}
.webix_group_back,.webix_group_back.webix_selected{position:relative;color:#1e2022!important;box-shadow:0 1px 1px #fff inset;background:0 1px repeat-x #e5f1ff;background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#eaf3ff),color-stop(50%,#e5f1ff),color-stop(100%,#d6e8ff)) 0 1px repeat-x;background-image:-webkit-linear-gradient(top,#eaf3ff 0,#e5f1ff 50%,#d6e8ff 100%);background-image:-moz-linear-gradient(top,#eaf3ff 0,#e5f1ff 60%,#d6e8ff 100%);background-image:-ms-linear-gradient(top,#eaf3ff 0,#e5f1ff 60%,#d6e8ff 100%);background-image:-o-linear-gradient(top,#eaf3ff 0,#e5f1ff 60%,#d6e8ff 100%)} .webix_group_back,.webix_group_back.webix_selected{position:relative;color:#1e2022!important;box-shadow:0 1px 1px #fff inset;background:#e5f1ff;background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#eaf3ff),color-stop(50%,#e5f1ff),color-stop(100%,#d6e8ff));background-image:-webkit-linear-gradient(top,#eaf3ff 0,#e5f1ff 50%,#d6e8ff 100%);background-image:-moz-linear-gradient(top,#eaf3ff 0,#e5f1ff 60%,#d6e8ff 100%);background-image:-ms-linear-gradient(top,#eaf3ff 0,#e5f1ff 60%,#d6e8ff 100%);background-image:-o-linear-gradient(top,#eaf3ff 0,#e5f1ff 60%,#d6e8ff 100%);background-position:0 1px;background-repeat:repeat-x}
.webix_arrow_icon{position:absolute;top:50%;bottom:50%;margin-top:-12px;right:15px;width:9px;height:22px;line-height:22px;color:#1e2022;font-size:18px;font-family:FontAwesome} .webix_arrow_icon{position:absolute;top:50%;bottom:50%;margin-top:-12px;right:15px;width:9px;height:22px;line-height:22px;color:#1e2022;font-size:18px;font-family:FontAwesome}
.webix_arrow_icon:before{content:'\f105'} .webix_arrow_icon:before{content:'\f105'}
.webix_group_back{padding-left:29px} .webix_group_back{padding-left:29px}
@ -103,18 +103,19 @@ x\:ui{display:none}
.webix_unit_header{padding:0 10px;line-height:19px;text-align:left;background-color:#d6e8ff} .webix_unit_header{padding:0 10px;line-height:19px;text-align:left;background-color:#d6e8ff}
.webix_list-x .webix_list_item{display:inline-block;vertical-align:top;border-right:1px solid #ebebeb;border-bottom:none} .webix_list-x .webix_list_item{display:inline-block;vertical-align:top;border-right:1px solid #ebebeb;border-bottom:none}
.webix_list-x .webix_list_item.webix_selected{border-right:1px solid #ffd47b;border-left:1px solid #ffd47b;padding-left:9px} .webix_list-x .webix_list_item.webix_selected{border-right:1px solid #ffd47b;border-left:1px solid #ffd47b;padding-left:9px}
.webix_list-x .webix_scroll_cont{display:inline} .webix_list-x .webix_scroll_cont{display:inline-block;overflow:visible}
.webix_view.webix_list-x{white-space:nowrap} .webix_view.webix_list-x{white-space:nowrap}
.webix_list_item.webix_disabled{color:#8b949e} .webix_list_item.webix_disabled{color:#8b949e}
.webix_list_item.webix_disabled .webix_submenu_icon{visibility:hidden} .webix_list_item.webix_disabled .webix_submenu_icon{visibility:hidden}
.webix_view.webix_pager{padding:2px 0;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box;background-color:transparent} .webix_view.webix_pager{padding:2px 0;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box;background-color:transparent}
.webix_pager_item,.webix_pager_item_selected{padding:0 6px;text-align:center;height:100%;width:32px;border:1px solid #a4bed4;margin:0 2px;cursor:pointer;background-color:#fff;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box;font-family:Helvetica,Verdana;font-size:13px} .webix_pager_item,.webix_pager_item_selected{padding:0 6px;text-align:center;height:100%;width:auto;min-width:32px;border:1px solid #a4bed4;margin:0 2px;cursor:pointer;background-color:#fff;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box;font-family:Helvetica,Verdana;font-size:13px}
.webix_pager_item_selected{cursor:default;background-color:#ffdb8f;border:1px solid #ffd47b} .webix_pager_item_selected{cursor:default;background-color:#ffdb8f;border:1px solid #ffd47b}
.webix_animation .webix_ss_header{position:relative;z-index:10} .webix_animation .webix_ss_header{position:relative;z-index:10}
.webix_animation .webix_ss_footer,.webix_animation .webix_vscroll_x{position:absolute;bottom:0;z-index:10} .webix_animation .webix_ss_footer,.webix_animation .webix_vscroll_x{position:absolute;bottom:0;z-index:10}
.webix_allow_selection{-moz-user-select:text;-webkit-user-select:text;user-select:text} .webix_allow_selection{-moz-user-select:text;-webkit-user-select:text;user-select:text}
.webix_dataview_item{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box;padding:2px 10px;line-height:23px;cursor:pointer;border-bottom:1px solid #ebebeb;text-align:left;border-right:1px solid #ebebeb} .webix_dataview_item{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box;padding:2px 10px;line-height:23px;cursor:pointer;border-bottom:1px solid #ebebeb;text-align:left;border-right:1px solid #ebebeb}
.webix_dataview_item.webix_selected{color:#1e2022;background:#ffdb8f;background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#ffebc2),color-stop(100%,#ffdb8f));background:-webkit-linear-gradient(#ffebc2,#ffdb8f);background:-moz-linear-gradient(#ffebc2,#ffdb8f);background:-ms-linear-gradient(top,#ffebc2,#ffdb8f);background:-o-linear-gradient(top,#ffebc2,#ffdb8f);border-bottom:1px solid #ffd47b;border-right:1px solid #ffd47b} .webix_dataview_item.webix_selected{background-size:100% 100%;background-position:0 0;color:#1e2022;background:#ffdb8f;background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#ffebc2),color-stop(100%,#ffdb8f));background-image:-webkit-linear-gradient(#ffebc2,#ffdb8f);background-image:-moz-linear-gradient(#ffebc2,#ffdb8f);background-image:-ms-linear-gradient(top,#ffebc2,#ffdb8f);background-image:-o-linear-gradient(top,#ffebc2,#ffdb8f);border-bottom:1px solid #ffd47b;border-right:1px solid #ffd47b}
.webix_dataview_item.webix_invalid{background-color:#fee}
.webix_chart{position:relative;font-family:Helvetica,Verdana;font-size:13px;color:#1e2022;overflow:hidden;z-index:1} .webix_chart{position:relative;font-family:Helvetica,Verdana;font-size:13px;color:#1e2022;overflow:hidden;z-index:1}
.webix_chart canvas{position:absolute} .webix_chart canvas{position:absolute}
.webix_canvas_text{position:absolute;text-align:center;overflow:hidden;white-space:nowrap;font-size:12px;z-index:100} .webix_canvas_text{position:absolute;text-align:center;overflow:hidden;white-space:nowrap;font-size:12px;z-index:100}
@ -155,11 +156,11 @@ x\:ui{display:none}
.webix_dt_editor select{padding-right:0;padding-left:5px} .webix_dt_editor select{padding-right:0;padding-left:5px}
.webix_clipbuffer{width:2000px;height:1px;padding:0;margin:0;border:0;left:0;top:0;white-space:pre-wrap;position:fixed;filter:alpha(opacity=0);opacity:0;pointer-events:none;cursor:default} .webix_clipbuffer{width:2000px;height:1px;padding:0;margin:0;border:0;left:0;top:0;white-space:pre-wrap;position:fixed;filter:alpha(opacity=0);opacity:0;pointer-events:none;cursor:default}
.webix_message_area{position:fixed;right:5px;width:250px;z-index:1000} .webix_message_area{position:fixed;right:5px;width:250px;z-index:1000}
.webix_info{min-width:120px;font-family:Tahoma,Arial;z-index:10000;overflow:hidden;margin:5px 5px 10px;-webkit-transition:all .5s ease;-moz-transition:all .5s ease;-o-transition:all .5s ease;transition:all .5s ease} .webix_info{min-width:120px;font-family:Tahoma,Arial;z-index:10000;overflow:hidden;margin:5px;margin-bottom:10px;-webkit-transition:all .5s ease;-moz-transition:all .5s ease;-o-transition:all .5s ease;transition:all .5s ease}
.webix_no_transition{-webkit-transition:none;-moz-transition:none;transition:none} .webix_no_transition{-webkit-transition:none;-moz-transition:none;transition:none}
.webix_info.hidden{height:0;min-height:0;padding-top:0;padding-bottom:0;border-width:0;margin-top:0;margin-bottom:0;overflow:hidden} .webix_info.hidden{height:0;min-height:0;padding-top:0;padding-bottom:0;border-width:0;margin-top:0;margin-bottom:0;overflow:hidden}
.webix_modal_box{overflow:hidden;display:inline-block;min-width:250px;width:250px;text-align:center;position:fixed;background-color:#fff;box-shadow:0 3px 6px #c8c8c8;font-family:Helvetica,Verdana;z-index:20000;border-radius:0;outline:0} .webix_modal_box{overflow:hidden;display:inline-block;min-width:250px;width:250px;text-align:center;position:fixed;background-color:#fff;box-shadow:0 3px 6px #c8c8c8;font-family:Helvetica,Verdana;z-index:20000;border-radius:0;outline:0}
.webix_popup_title{border-top-left-radius:-1px;border-top-right-radius:-1px;border-width:0} .webix_popup_title{border-top-left-radius:-1px;border-top-right-radius:-1px;border-width:0;background-image:-webkit-linear-gradient(top,#707070 1%,#3d3d3d 70%,#4c4c4c 97%,#393939 97%);background-image:-moz-linear-gradient(top,#707070 1%,#3d3d3d 70%,#4c4c4c 97%,#393939 97%)}
.webix_button,.webix_info,.webix_popup_button{cursor:pointer} .webix_button,.webix_info,.webix_popup_button{cursor:pointer}
.webix_popup_text{overflow:hidden} .webix_popup_text{overflow:hidden}
.webix_popup_controls{border-radius:6px;padding:5px 10px 10px} .webix_popup_controls{border-radius:6px;padding:5px 10px 10px}
@ -171,12 +172,12 @@ div.webix_modal_cover{background-color:#000;cursor:default;filter:alpha(opacity=
.webix_alert_error .webix_popup_title,.webix_confirm_error .webix_popup_title{background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAsCAIAAAArRUU2AAAATklEQVR4nIWLuw2AMBBDjVuQiBT2oWbRDATrnB0KQOJoqPzRe3BrHI6dcBASYREKovtK6/6DsDOX+stN+3H1YX9ciRgnYq5EWYhS2dftBIuLT4JyIrPCAAAAAElFTkSuQmCC)} .webix_alert_error .webix_popup_title,.webix_confirm_error .webix_popup_title{background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAsCAIAAAArRUU2AAAATklEQVR4nIWLuw2AMBBDjVuQiBT2oWbRDATrnB0KQOJoqPzRe3BrHI6dcBASYREKovtK6/6DsDOX+stN+3H1YX9ciRgnYq5EWYhS2dftBIuLT4JyIrPCAAAAAElFTkSuQmCC)}
.webix_alert_error,.webix_confirm_error{border:1px solid red} .webix_alert_error,.webix_confirm_error{border:1px solid red}
.webix_button,.webix_popup_button{border:1px solid #a4bed4} .webix_button,.webix_popup_button{border:1px solid #a4bed4}
.webix_button input,.webix_popup_button div{font-size:13px;color:#1e2022;background:#e6e6e6;background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#fff),color-stop(100%,#e6e6e6));background:-webkit-linear-gradient(#fff,#e6e6e6);background:-moz-linear-gradient(#fff,#e6e6e6);background:-ms-linear-gradient(top,#fff,#e6e6e6);background:-o-linear-gradient(top,#fff,#e6e6e6);height:28px;line-height:28px} .webix_button input,.webix_popup_button div{font-size:13px;color:#1e2022;background:#e6e6e6;background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#fff),color-stop(100%,#e6e6e6));background-image:-webkit-linear-gradient(#fff,#e6e6e6);background-image:-moz-linear-gradient(#fff,#e6e6e6);background-image:-ms-linear-gradient(top,#fff,#e6e6e6);background-image:-o-linear-gradient(top,#fff,#e6e6e6);height:28px;line-height:28px}
.webix_popup_button.confirm div{color:#1e2022!important;box-shadow:0 1px 1px #fff inset;background:0 1px repeat-x #e5f1ff;background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#eaf3ff),color-stop(50%,#e5f1ff),color-stop(100%,#d6e8ff)) 0 1px repeat-x;background-image:-webkit-linear-gradient(top,#eaf3ff 0,#e5f1ff 50%,#d6e8ff 100%);background-image:-moz-linear-gradient(top,#eaf3ff 0,#e5f1ff 60%,#d6e8ff 100%);background-image:-ms-linear-gradient(top,#eaf3ff 0,#e5f1ff 60%,#d6e8ff 100%);background-image:-o-linear-gradient(top,#eaf3ff 0,#e5f1ff 60%,#d6e8ff 100%)} .webix_popup_button.confirm div{color:#1e2022!important;box-shadow:0 1px 1px #fff inset;background:#e5f1ff;background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#eaf3ff),color-stop(50%,#e5f1ff),color-stop(100%,#d6e8ff));background-image:-webkit-linear-gradient(top,#eaf3ff 0,#e5f1ff 50%,#d6e8ff 100%);background-image:-moz-linear-gradient(top,#eaf3ff 0,#e5f1ff 60%,#d6e8ff 100%);background-image:-ms-linear-gradient(top,#eaf3ff 0,#e5f1ff 60%,#d6e8ff 100%);background-image:-o-linear-gradient(top,#eaf3ff 0,#e5f1ff 60%,#d6e8ff 100%);background-position:0 1px;background-repeat:repeat-x}
.webix_popup_title{color:#fff;height:40px;line-height:40px} .webix_popup_title{color:#fff;height:40px;line-height:40px}
.webix_popup_text{margin:0 0 5px;padding-top:25px;font-size:13px;color:#1e2022;min-height:60px} .webix_popup_text{margin:0 0 5px 0;padding-top:25px;font-size:13px;color:#1e2022;min-height:60px}
.webix_error,.webix_info{font-size:13px;color:#333;box-shadow:0 0 10px #888;padding:0;background-color:#FFF;border-radius:3px;border:1px solid #fff} .webix_error,.webix_info{font-size:13px;color:#333;box-shadow:0 0 10px #888;padding:0;background-color:#FFF;border-radius:3px;border:1px solid #fff}
.webix_info div{padding:5px 10px;background-color:#fff;border-radius:3px;border:1px solid #B8B8B8} .webix_info div{padding:5px 10px 5px 10px;background-color:#fff;border-radius:3px;border:1px solid #B8B8B8}
.webix_error{background-color:#d81b1b;border:1px solid #ff3c3c;box-shadow:0 0 10px #000} .webix_error{background-color:#d81b1b;border:1px solid #ff3c3c;box-shadow:0 0 10px #000}
.webix_error div{background-color:#d81b1b;border:1px solid #940000;color:#FFF} .webix_error div{background-color:#d81b1b;border:1px solid #940000;color:#FFF}
.webix_drag_zone{position:absolute;box-shadow:1px 1px 5px #a4bed4;background-color:#fff;font-family:Helvetica,Verdana;font-size:13px;color:#1e2022;pointer-events:none} .webix_drag_zone{position:absolute;box-shadow:1px 1px 5px #a4bed4;background-color:#fff;font-family:Helvetica,Verdana;font-size:13px;color:#1e2022;pointer-events:none}
@ -201,13 +202,13 @@ div.webix_modal_cover{background-color:#000;cursor:default;filter:alpha(opacity=
.webix_block_selection,.webix_cell,.webix_hcell,.webix_ss_footer td,.webix_ss_header td,.webix_table_cell,.webix_view.webix_table_cell{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box} .webix_block_selection,.webix_cell,.webix_hcell,.webix_ss_footer td,.webix_ss_header td,.webix_table_cell,.webix_view.webix_table_cell{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box}
.webix_block_selection{position:absolute} .webix_block_selection{position:absolute}
.webix_ss_sort_asc,.webix_ss_sort_desc{-moz-user-select:none;-webkit-user-select:none;-ms-user-select:none;user-select:none} .webix_ss_sort_asc,.webix_ss_sort_desc{-moz-user-select:none;-webkit-user-select:none;-ms-user-select:none;user-select:none}
.webix_hcell.webix_ss_filter{padding:2px 5px} .webix_hcell.webix_ss_filter{padding:2px 5px 2px 5px}
.webix_ss_filter input,.webix_ss_filter select{width:100%;font-size:inherit;color:#1e2022!important;height:30px;margin-top:-2px;padding:0 3px;vertical-align:top;border:1px solid #a4bed4;line-height:30px;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box;font-family:Helvetica,Verdana} .webix_ss_filter input,.webix_ss_filter select{width:100%;font-size:inherit;color:#1e2022!important;height:30px;margin-top:-2px;padding:0 3px;vertical-align:top;border:1px solid #a4bed4;line-height:30px;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box;font-family:Helvetica,Verdana}
.webix_richfilter{margin-top:1px;line-height:30px} .webix_richfilter{margin-top:1px;line-height:30px}
.webix_ss_filter select{padding:3px} .webix_ss_filter select{padding:3px}
.webix_dd_drag_column,.webix_ss_header,.webix_ss_header TD,.webix_ss_vscroll_header{color:#1e2022!important;box-shadow:0 1px 1px #fff inset;background:0 1px repeat-x #e5f1ff;background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#eaf3ff),color-stop(50%,#e5f1ff),color-stop(100%,#d6e8ff)) 0 1px repeat-x;background-image:-webkit-linear-gradient(top,#eaf3ff 0,#e5f1ff 50%,#d6e8ff 100%);background-image:-moz-linear-gradient(top,#eaf3ff 0,#e5f1ff 60%,#d6e8ff 100%);background-image:-ms-linear-gradient(top,#eaf3ff 0,#e5f1ff 60%,#d6e8ff 100%);background-image:-o-linear-gradient(top,#eaf3ff 0,#e5f1ff 60%,#d6e8ff 100%)} .webix_dd_drag_column,.webix_ss_header,.webix_ss_header TD,.webix_ss_vscroll_header{color:#1e2022!important;box-shadow:0 1px 1px #fff inset;background:#e5f1ff;background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#eaf3ff),color-stop(50%,#e5f1ff),color-stop(100%,#d6e8ff));background-image:-webkit-linear-gradient(top,#eaf3ff 0,#e5f1ff 50%,#d6e8ff 100%);background-image:-moz-linear-gradient(top,#eaf3ff 0,#e5f1ff 60%,#d6e8ff 100%);background-image:-ms-linear-gradient(top,#eaf3ff 0,#e5f1ff 60%,#d6e8ff 100%);background-image:-o-linear-gradient(top,#eaf3ff 0,#e5f1ff 60%,#d6e8ff 100%);background-position:0 1px;background-repeat:repeat-x}
.webix_ss_footer TD,.webix_ss_vscroll_footer{background:#fafafa} .webix_ss_footer TD,.webix_ss_vscroll_footer{background:#fafafa}
.webix_hcell{padding:0 10px;text-align:left} .webix_hcell{padding:0 10px 0 10px;text-align:left}
.webix_ss_header td{border-right:1px solid #a4bed4;border-bottom:1px solid #a4bed4} .webix_ss_header td{border-right:1px solid #a4bed4;border-bottom:1px solid #a4bed4}
.webix_ss_footer td{border-right:1px solid #ebebeb;border-top:1px solid #ebebeb} .webix_ss_footer td{border-right:1px solid #ebebeb;border-top:1px solid #ebebeb}
.webix_size_row td{border:none} .webix_size_row td{border:none}
@ -222,31 +223,32 @@ div.webix_modal_cover{background-color:#000;cursor:default;filter:alpha(opacity=
.webix_block_selection{background:rgba(0,0,0,.1);border:2px solid #ffd47b} .webix_block_selection{background:rgba(0,0,0,.1);border:2px solid #ffd47b}
.webix_dd_drag{white-space:nowrap;overflow:hidden;border-left:1px solid #ebebeb;height:34px} .webix_dd_drag{white-space:nowrap;overflow:hidden;border-left:1px solid #ebebeb;height:34px}
.webix_dd_drag>div{display:inline-block;vertical-align:top;background:#fff;border-top:1px solid #ebebeb;border-right:1px solid #ebebeb;border-bottom:1px solid #ebebeb;padding:5px 10px;height:34px;line-height:26px;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box} .webix_dd_drag>div{display:inline-block;vertical-align:top;background:#fff;border-top:1px solid #ebebeb;border-right:1px solid #ebebeb;border-bottom:1px solid #ebebeb;padding:5px 10px;height:34px;line-height:26px;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box}
.webix_dd_drag_column{padding:5px 10px} .webix_dd_drag_column{padding:5px 10px 5px 10px}
.webix_column>.webix_drag_over.webix_cell{background-color:#e6e6e6;color:#1e2022} .webix_column>.webix_drag_over.webix_cell{background-color:#e6e6e6;color:#1e2022}
.webix_ss_header table,.webix_ss_header td{padding:0;margin:0} .webix_ss_header table,.webix_ss_header td{padding:0;margin:0}
.webix_cell.webix_invalid,.webix_column>.webix_cell.webix_invalid:nth-child(even){background-color:#fee} .webix_cell.webix_invalid,.webix_column>.webix_cell.webix_invalid:nth-child(even){background-color:#fee}
.webix_invalid_cell{position:relative} .webix_invalid_cell{position:relative}
.webix_invalid_cell:after{content:"";position:absolute;top:0;right:0;width:0;height:1px;border-top:8px solid #f44;border-left:8px solid transparent} .webix_invalid_cell:after{content:"";position:absolute;top:0;right:0;width:0;height:1px;border-top:8px solid #f44;border-left:8px solid transparent}
.webix_column>div.webix_cell_select,.webix_column>div.webix_column_select,.webix_column>div.webix_row_select{color:#1e2022;background:#ffdb8f;background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#ffebc2),color-stop(100%,#ffdb8f));background:-webkit-linear-gradient(#ffebc2,#ffdb8f);background:-moz-linear-gradient(#ffebc2,#ffdb8f);background:-ms-linear-gradient(top,#ffebc2,#ffdb8f);background:-o-linear-gradient(top,#ffebc2,#ffdb8f)} .webix_column>div.webix_cell_select,.webix_column>div.webix_column_select,.webix_column>div.webix_row_select{background-size:100% 100%;background-position:0 0;color:#1e2022;background:#ffdb8f;background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#ffebc2),color-stop(100%,#ffdb8f));background-image:-webkit-linear-gradient(#ffebc2,#ffdb8f);background-image:-moz-linear-gradient(#ffebc2,#ffdb8f);background-image:-ms-linear-gradient(top,#ffebc2,#ffdb8f);background-image:-o-linear-gradient(top,#ffebc2,#ffdb8f)}
.webix_column>div.webix_cell_select{border-bottom:1px solid #ffd47b;box-shadow:0 1px #ffe3a9 inset;border-right:1px solid #ffd47b;border-left:1px solid #ffd47b;padding-top:0;padding-left:9px} .webix_column>div.webix_cell_select{border-bottom:1px solid #ffd47b;box-shadow:0 1px #ffe3a9 inset;border-right:1px solid #ffd47b;border-left:1px solid #ffd47b;padding-top:0;padding-left:9px}
.webix_ss_right .webix_column.webix_first>div.webix_cell_select{padding-left:10px} .webix_ss_right .webix_column.webix_first>div.webix_cell_select{padding-left:10px}
.webix_column>div.webix_invalid.webix_row_select:nth-child(even),.webix_column>div.webix_row_select{border-bottom:1px solid #ffd47b;box-shadow:0 1px #ffe3a9 inset;padding-top:0} .webix_column>div.webix_invalid.webix_row_select:nth-child(even),.webix_column>div.webix_row_select{border-bottom:1px solid #ffd47b;box-shadow:0 1px #ffe3a9 inset;padding-top:0}
.webix_column>div.webix_column_select{border-right:1px solid #ffd47b;border-left:1px solid #ffd47b;padding-left:9px} .webix_column>div.webix_column_select{border-right:1px solid #ffd47b;border-left:1px solid #ffd47b;padding-left:9px}
.webix_column>.webix_cell.webix_invalid.webix_cell_select:nth-child(even){color:#1e2022;background:#ffdb8f;background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#ffebc2),color-stop(100%,#ffdb8f));background:-webkit-linear-gradient(#ffebc2,#ffdb8f);background:-moz-linear-gradient(#ffebc2,#ffdb8f);background:-ms-linear-gradient(top,#ffebc2,#ffdb8f);background:-o-linear-gradient(top,#ffebc2,#ffdb8f)} .webix_column>.webix_cell.webix_invalid.webix_cell_select:nth-child(even){background-size:100% 100%;background-position:0 0;color:#1e2022;background:#ffdb8f;background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#ffebc2),color-stop(100%,#ffdb8f));background-image:-webkit-linear-gradient(#ffebc2,#ffdb8f);background-image:-moz-linear-gradient(#ffebc2,#ffdb8f);background-image:-ms-linear-gradient(top,#ffebc2,#ffdb8f);background-image:-o-linear-gradient(top,#ffebc2,#ffdb8f)}
.webix_cell.webix_dtable_colrow,.webix_cell.webix_dtable_subrow,.webix_cell.webix_dtable_subview{position:absolute;left:0;top:0;background-color:#fff;width:100%;padding-right:28px;padding-left:10px;padding-top:1px;border-bottom:1px solid #ebebeb} .webix_cell.webix_dtable_colrow,.webix_cell.webix_dtable_subrow,.webix_dtable_subview{position:absolute;left:0;top:0;background-color:#fff;width:100%;padding-right:28px;padding-left:10px;padding-top:1px;border-bottom:1px solid #ebebeb}
.webix_cell.webix_dtable_colrow.webix_selected{background-size:100% 100%;background-position:0 0;color:#1e2022;background:#ffdb8f;background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#ffebc2),color-stop(100%,#ffdb8f));background-image:-webkit-linear-gradient(#ffebc2,#ffdb8f);background-image:-moz-linear-gradient(#ffebc2,#ffdb8f);background-image:-ms-linear-gradient(top,#ffebc2,#ffdb8f);background-image:-o-linear-gradient(top,#ffebc2,#ffdb8f)}
.webix_dtable_subrow{border-top:1px solid #ebebeb;text-align:right} .webix_dtable_subrow{border-top:1px solid #ebebeb;text-align:right}
.webix_dtable_subview{background:#ebebeb;border-top:1px solid #ebebeb;padding:0 0 0 10px;box-sizing:border-box} .webix_dtable_subview{background:#ebebeb;border-top:1px solid #ebebeb;padding:0;padding-left:10px;box-sizing:border-box}
.number .webix_cell{text-align:right} .number .webix_cell{text-align:right}
.webix_rotate{transform:rotate(-90deg);-webkit-transform:rotate(-90deg);line-height:normal} .webix_rotate{transform:rotate(-90deg);-webkit-transform:rotate(-90deg);line-height:normal}
.webix_measure_rotate{line-height:normal;white-space:normal;padding:10px} .webix_measure_rotate{line-height:normal;white-space:normal;padding:10px}
.webix_span_layer,.webix_span_layer_top{background:0 0;position:absolute;left:0;top:0;height:0;width:0;overflow:visible} .webix_span_layer,.webix_span_layer_top{background:0 0;position:absolute;left:0;top:0;height:0;width:0;overflow:visible}
.webix_span_layer_top{z-index:1;overflow:hidden} .webix_span_layer_top{z-index:1}
.webix_ss_right .webix_span_layer,.webix_ss_right .webix_span_layer_top{border-left:1px solid #a4bed4} .webix_ss_right .webix_span_layer,.webix_ss_right .webix_span_layer_top{border-left:1px solid #a4bed4}
.webix_dtable_span{position:absolute;background:#fff;text-align:left} .webix_dtable_span{position:absolute;background:#fff;text-align:left}
.webix_dtable_span.webix_selected{color:#1e2022;background:#ffdb8f;background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#ffebc2),color-stop(100%,#ffdb8f));background:-webkit-linear-gradient(#ffebc2,#ffdb8f);background:-moz-linear-gradient(#ffebc2,#ffdb8f);background:-ms-linear-gradient(top,#ffebc2,#ffdb8f);background:-o-linear-gradient(top,#ffebc2,#ffdb8f)} .webix_dtable_span.webix_selected{background-size:100% 100%;background-position:0 0;color:#1e2022;background:#ffdb8f;background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#ffebc2),color-stop(100%,#ffdb8f));background-image:-webkit-linear-gradient(#ffebc2,#ffdb8f);background-image:-moz-linear-gradient(#ffebc2,#ffdb8f);background-image:-ms-linear-gradient(top,#ffebc2,#ffdb8f);background-image:-o-linear-gradient(top,#ffebc2,#ffdb8f)}
.webix_hcell span.webix_input_icon{background-color:#a4bed4} .webix_hcell span.webix_input_icon{background-color:#a4bed4}
.webix_area_selection_layer{position:absolute;z-index:1} .webix_area_selection_layer{position:absolute;z-index:2}
.webix_area_selection{position:absolute;background:#1e2022} .webix_area_selection{position:absolute;background:#1e2022}
.webix_area_selection_bottom,.webix_area_selection_top{height:2px} .webix_area_selection_bottom,.webix_area_selection_top{height:2px}
.webix_area_selection_left,.webix_area_selection_right{width:2px} .webix_area_selection_left,.webix_area_selection_right{width:2px}
@ -256,7 +258,7 @@ div.webix_modal_cover{background-color:#000;cursor:default;filter:alpha(opacity=
.webix_win_body{overflow:hidden} .webix_win_body{overflow:hidden}
.webix_win_body,.webix_win_body>.webix_view,.webix_win_body>.webix_view .webix_cal_month,.webix_win_content,.webix_window{border-radius:0} .webix_win_body,.webix_win_body>.webix_view,.webix_win_body>.webix_view .webix_cal_month,.webix_win_content,.webix_window{border-radius:0}
.webix_win_head{border-bottom:1px solid silver} .webix_win_head{border-bottom:1px solid silver}
.webix_win_head>.webix_view>.webix_template{font-family:Helvetica,Verdana;font-size:13px;font-weight:700;color:#1e2022!important;box-shadow:0 1px 1px #fff inset;background:0 1px repeat-x #e5f1ff;background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#eaf3ff),color-stop(50%,#e5f1ff),color-stop(100%,#d6e8ff)) 0 1px repeat-x;background-image:-webkit-linear-gradient(top,#eaf3ff 0,#e5f1ff 50%,#d6e8ff 100%);background-image:-moz-linear-gradient(top,#eaf3ff 0,#e5f1ff 60%,#d6e8ff 100%);background-image:-ms-linear-gradient(top,#eaf3ff 0,#e5f1ff 60%,#d6e8ff 100%);background-image:-o-linear-gradient(top,#eaf3ff 0,#e5f1ff 60%,#d6e8ff 100%);padding:0;text-align:center;line-height:34px} .webix_win_head>.webix_view>.webix_template{font-family:Helvetica,Verdana;color:#1e2022;font-size:13px;font-weight:700;color:#1e2022!important;box-shadow:0 1px 1px #fff inset;background:#e5f1ff;background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#eaf3ff),color-stop(50%,#e5f1ff),color-stop(100%,#d6e8ff));background-image:-webkit-linear-gradient(top,#eaf3ff 0,#e5f1ff 50%,#d6e8ff 100%);background-image:-moz-linear-gradient(top,#eaf3ff 0,#e5f1ff 60%,#d6e8ff 100%);background-image:-ms-linear-gradient(top,#eaf3ff 0,#e5f1ff 60%,#d6e8ff 100%);background-image:-o-linear-gradient(top,#eaf3ff 0,#e5f1ff 60%,#d6e8ff 100%);background-position:0 1px;background-repeat:repeat-x;padding:0;text-align:center;line-height:34px}
.webix_win_head>.webix_view>.webix_template .webix_el_label,.webix_win_head>.webix_view>.webix_template .webix_inp_label{color:#1e2022} .webix_win_head>.webix_view>.webix_template .webix_el_label,.webix_win_head>.webix_view>.webix_template .webix_inp_label{color:#1e2022}
.webix_win_content{height:auto;width:auto;overflow:hidden} .webix_win_content{height:auto;width:auto;overflow:hidden}
.webix_modal{width:100%;height:100%;position:fixed;top:0;left:0;background-color:#000;filter:alpha(opacity=20);opacity:.2} .webix_modal{width:100%;height:100%;position:fixed;top:0;left:0;background-color:#000;filter:alpha(opacity=20);opacity:.2}
@ -268,18 +270,19 @@ div.webix_modal_cover{background-color:#000;cursor:default;filter:alpha(opacity=
.webix_point_bottom{background-position:0 0} .webix_point_bottom{background-position:0 0}
.webix_point_right{background-position:0 -22px} .webix_point_right{background-position:0 -22px}
.webix_point_left{background-position:0 -9px} .webix_point_left{background-position:0 -9px}
.webix_resize_handle{position:absolute;width:15px;height:15px;bottom:0;line-height:15px;right:-1px;text-align:center;background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA8AAAAPCAMAAAAMCGV4AAAABlBMVEUAAAAAAAClZ7nPAAAAAXRSTlMAQObYZgAAABZJREFUeAFjIAUwUshlpJDLSIhLGAAACQ4AFk79JaMAAAAASUVORK5CYII=);cursor:nw-resize} .webix_resize_handle{position:absolute;z-index:2;width:15px;height:15px;bottom:0;line-height:15px;right:-1px;text-align:center;background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA8AAAAPCAMAAAAMCGV4AAAABlBMVEUAAAAAAAClZ7nPAAAAAXRSTlMAQObYZgAAABZJREFUeAFjIAUwUshlpJDLSIhLGAAACQ4AFk79JaMAAAAASUVORK5CYII=);cursor:nw-resize}
.webix_resize_frame{position:absolute;filter:alpha(opacity=10);opacity:.1;background:#d6e8ff;display:block;border:1px solid #70afff;box-shadow:1px 3px 6px #000} .webix_resize_frame{position:absolute;filter:alpha(opacity=10);opacity:.1;background:#d6e8ff;display:block;border:1px solid #70afff;box-shadow:1px 3px 6px #000}
.webix_resize_cursor{cursor:nw-resize} .webix_resize_cursor{cursor:nw-resize}
.webix_tree .webix_scroll_cont>.webix_tree_leaves{padding:9px 10px 9px 9px} .webix_tree .webix_scroll_cont>.webix_tree_leaves{padding:9px 10px 9px 9px}
.webix_tree_item{clear:both;height:22px;line-height:22px;white-space:nowrap} .webix_tree_item{clear:both;height:22px;line-height:22px;white-space:nowrap}
.webix_tree_item.webix_selected{background:0 0} .webix_tree_item.webix_selected{background:0 0}
.webix_tree_item.webix_selected span{padding:4px 10px 4px 4px;margin-left:-4px;color:#1e2022;background:#ffdb8f;background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#ffebc2),color-stop(100%,#ffdb8f));background:-webkit-linear-gradient(#ffebc2,#ffdb8f);background:-moz-linear-gradient(#ffebc2,#ffdb8f);background:-ms-linear-gradient(top,#ffebc2,#ffdb8f);background:-o-linear-gradient(top,#ffebc2,#ffdb8f)} .webix_tree_item.webix_selected span{padding:4px 10px 4px 4px;margin-left:-4px;background-size:100% 100%;background-position:0 0;color:#1e2022;background:#ffdb8f;background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#ffebc2),color-stop(100%,#ffdb8f));background-image:-webkit-linear-gradient(#ffebc2,#ffdb8f);background-image:-moz-linear-gradient(#ffebc2,#ffdb8f);background-image:-ms-linear-gradient(top,#ffebc2,#ffdb8f);background-image:-o-linear-gradient(top,#ffebc2,#ffdb8f)}
.webix_tree_item.webix_invalid{background-color:#fee}
.webix_tree_checkbox{margin:1px 0 0;padding:0;float:left;vertical-align:middle;height:100%;width:13px} .webix_tree_checkbox{margin:1px 0 0;padding:0;float:left;vertical-align:middle;height:100%;width:13px}
.webix_indeterminate{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAYAAABWdVznAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3gcCCSwfRyGesAAAAAxpVFh0Q29tbWVudAAAAAAAvK6ymQAAAExJREFUKM9jLCgo+M9AAmBhYGBgaGtrY+Tk5MSrsLCwkIGBgeE/EwOJgGQNLMgcJSUlBgYGBnQ/Md67d498G+jsB6hbGQfWSYykJg0AUMsPVoNrFX0AAAAASUVORK5CYII=);background-repeat:no-repeat;background-position:left center;height:100%;width:13px;margin-top:0!important} .webix_indeterminate{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAYAAABWdVznAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3gcCCSwfRyGesAAAAAxpVFh0Q29tbWVudAAAAAAAvK6ymQAAAExJREFUKM9jLCgo+M9AAmBhYGBgaGtrY+Tk5MSrsLCwkIGBgeE/EwOJgGQNLMgcJSUlBgYGBnQ/Md67d498G+jsB6hbGQfWSYykJg0AUMsPVoNrFX0AAAAASUVORK5CYII=);background-repeat:no-repeat;background-position:left center;height:100%;width:13px;margin-top:0!important}
.webix_measure_size input,.webix_measure_size select,.webix_measure_size textarea{width:5px} .webix_measure_size input,.webix_measure_size select,.webix_measure_size textarea{width:5px}
.webix_measure_size div{margin-left:-5px;float:none} .webix_measure_size div{margin-left:-5px;float:none}
.webix_measure_size a,.webix_measure_size div{display:inline-block!important} .webix_measure_size a,.webix_measure_size div{display:inline-block!important;position:static!important}
.webix_tree_close,.webix_tree_file,.webix_tree_folder,.webix_tree_folder_open,.webix_tree_none,.webix_tree_open{float:left;width:20px;height:100%;cursor:pointer;margin:0;background-repeat:no-repeat} .webix_tree_close,.webix_tree_file,.webix_tree_folder,.webix_tree_folder_open,.webix_tree_none,.webix_tree_open{float:left;width:20px;height:100%;cursor:pointer;margin:0;background-repeat:no-repeat}
.webix_tree_close,.webix_tree_none,.webix_tree_open{background-position:center center} .webix_tree_close,.webix_tree_none,.webix_tree_open{background-position:center center}
.webix_cell .webix_tree_close,.webix_cell .webix_tree_none,.webix_cell .webix_tree_open{background-color:transparent} .webix_cell .webix_tree_close,.webix_cell .webix_tree_none,.webix_cell .webix_tree_open{background-color:transparent}
@ -291,7 +294,7 @@ div.webix_modal_cover{background-color:#000;cursor:default;filter:alpha(opacity=
.webix_tree_file{background-image:url(data:image/gif;base64,R0lGODlhEgASAMQVAPv7+2RkZPf39/r6+vj4+Pn5+fT09Pb29vX19fHx8fDw8Onp6fLy8u7u7uzs7PPz8+vr6+rq6uXl5ejo6O/v7////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAABUALAAAAAASABIAAAV4YCWOZGkGaKqaVSAMQCwLQnAOj6ErzUTXpQDgQDgcGJTFDzgSEp4HRCKytDUBMKzAoIAsJACrSCjTGhINR5hELg8ECEZifS3HCgQEgj62xwZPAnwtbgMDBQU0g21Yh4iIi3aGkzBihH52lgEEmDIEli0qoigspSYhADs=)} .webix_tree_file{background-image:url(data:image/gif;base64,R0lGODlhEgASAMQVAPv7+2RkZPf39/r6+vj4+Pn5+fT09Pb29vX19fHx8fDw8Onp6fLy8u7u7uzs7PPz8+vr6+rq6uXl5ejo6O/v7////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAABUALAAAAAASABIAAAV4YCWOZGkGaKqaVSAMQCwLQnAOj6ErzUTXpQDgQDgcGJTFDzgSEp4HRCKytDUBMKzAoIAsJACrSCjTGhINR5hELg8ECEZifS3HCgQEgj62xwZPAnwtbgMDBQU0g21Yh4iIi3aGkzBihH52lgEEmDIEli0qoigspSYhADs=)}
.webix_tree_folder{background-image:url(data:image/gif;base64,R0lGODlhEgASAMQfAPvjpP/24fPHVvnUdvzotdSiKv/23vvrw/LCS/C+RfnelvPMaf/88/nbi/rsyerYq/TLYv7y1v7wzeCsLO+8P86dJfvv0MuaIsydKPPOc+SqGKGEPe/AS+zVneWwLf///yH5BAEAAB8ALAAAAAASABIAAAWZ4CdeZGleYvpdA+O+zNUBqHoZOB4EceHUqVtuF+n9VCtdQCI5BC6FA3BkCEQkBALAACVMV9eDVhGJQGnIixigaAwiG6jie2G7BxDLpVJozG13EAICPxUeGRleQQMLgggJUhiHC3pBggKPFJETGZRAF5gJCRSaFxgTC55BCByioz+nEKojGgmtHBwPJAUCELo2J8FfSMTFxsUhADs=)} .webix_tree_folder{background-image:url(data:image/gif;base64,R0lGODlhEgASAMQfAPvjpP/24fPHVvnUdvzotdSiKv/23vvrw/LCS/C+RfnelvPMaf/88/nbi/rsyerYq/TLYv7y1v7wzeCsLO+8P86dJfvv0MuaIsydKPPOc+SqGKGEPe/AS+zVneWwLf///yH5BAEAAB8ALAAAAAASABIAAAWZ4CdeZGleYvpdA+O+zNUBqHoZOB4EceHUqVtuF+n9VCtdQCI5BC6FA3BkCEQkBALAACVMV9eDVhGJQGnIixigaAwiG6jie2G7BxDLpVJozG13EAICPxUeGRleQQMLgggJUhiHC3pBggKPFJETGZRAF5gJCRSaFxgTC55BCByioz+nEKojGgmtHBwPJAUCELo2J8FfSMTFxsUhADs=)}
.webix_tree_folder_open{background-image:url(data:image/gif;base64,R0lGODlhEgASAMQfAPXMWPK9N/fipOzLbPrVaP/89frqwv3tuvvprfLES+7PdP7xyP7z0vPAQfvtyMuaIv/32/735OSqFv3de+7ReP7lmf7gh/nv0v/67unFXfb29vK/PenFSaGEPfLnyv///yH5BAEAAB8ALAAAAAASABIAAAWS4Cc+ZGk+YvqZRVtgGiag6sNBThlFWnTQqQcE4sqRFj/VashsYR6HZI0JWSAjDwQCOGIgERULg/EQzJRQsGVCYHTK51qYTQDkFPj4iA4AJHIUgQoGQA8EdX4NhHgKAxeFfQkJGwGLeI6Fkg2UlQ8Dn5hBCZuchKChIxKkGw05Ga8DHlwkqg2tJ1xBtDlKvTUkviEAOw==)} .webix_tree_folder_open{background-image:url(data:image/gif;base64,R0lGODlhEgASAMQfAPXMWPK9N/fipOzLbPrVaP/89frqwv3tuvvprfLES+7PdP7xyP7z0vPAQfvtyMuaIv/32/735OSqFv3de+7ReP7lmf7gh/nv0v/67unFXfb29vK/PenFSaGEPfLnyv///yH5BAEAAB8ALAAAAAASABIAAAWS4Cc+ZGk+YvqZRVtgGiag6sNBThlFWnTQqQcE4sqRFj/VashsYR6HZI0JWSAjDwQCOGIgERULg/EQzJRQsGVCYHTK51qYTQDkFPj4iA4AJHIUgQoGQA8EdX4NhHgKAxeFfQkJGwGLeI6Fkg2UlQ8Dn5hBCZuchKChIxKkGw05Ga8DHlwkqg2tJ1xBtDlKvTUkviEAOw==)}
.webix_tree_img{background:0 0;float:left;width:18px;height:22px} .webix_tree_img{background:0 0;float:left;width:18px;height:22px;background-position:0 0}
.webix_tree_plus2{background-image:url(data:image/gif;base64,R0lGODlhEgASAMQfAFSWrmi52FCPplORqVmet8HBwfn//02KoE+NpEyIn2a21E2Jn2u/3mzB4Vqguk6LomGtyWGuy27E5VqivG/H51OUrE+OpVyhu1eXr2CqxV6lwFaas06LoXHJ6lKTqgAAACH5BAEAAB8ALAAAAAASABIAAAVZ4CeOYkGeaJmuKpuaLgrHX7IcD2IJrs0hAk8FAGAlDoiBYUNwTIyPAcagyUAirAXCwOUGAoVwmHSwVAiGSIDRYB0EgIthLaG44RN1g9JxwR0QdDSDhIWGKyEAOw==);background-repeat:no-repeat} .webix_tree_plus2{background-image:url(data:image/gif;base64,R0lGODlhEgASAMQfAFSWrmi52FCPplORqVmet8HBwfn//02KoE+NpEyIn2a21E2Jn2u/3mzB4Vqguk6LomGtyWGuy27E5VqivG/H51OUrE+OpVyhu1eXr2CqxV6lwFaas06LoXHJ6lKTqgAAACH5BAEAAB8ALAAAAAASABIAAAVZ4CeOYkGeaJmuKpuaLgrHX7IcD2IJrs0hAk8FAGAlDoiBYUNwTIyPAcagyUAirAXCwOUGAoVwmHSwVAiGSIDRYB0EgIthLaG44RN1g9JxwR0QdDSDhIWGKyEAOw==);background-repeat:no-repeat}
.webix_tree_plus3{background-image:url(data:image/gif;base64,R0lGODlhEgASAMQfAFSWrmi52FCPplORqVmet8HBwfn//02KoE+NpEyIn2a21E2Jn2u/3mzB4Vqguk6LomGtyWGuy27E5VqivG/H51OUrE+OpVyhu1eXr2CqxV6lwFaas06LoXHJ6lKTqgAAACH5BAEAAB8ALAAAAAASABIAAAVb4CeOYkGeaJmuKpuaLgrHX7IcD2IJrs0hAk8FAGAlDoiBYUNwTIyPAcagyUAirAXCwOUGAoVwmHSwVAiGSIDRYB0EgIthLaG44RN1g9JxwR0QdDQtg4SFM4csIQA7)} .webix_tree_plus3{background-image:url(data:image/gif;base64,R0lGODlhEgASAMQfAFSWrmi52FCPplORqVmet8HBwfn//02KoE+NpEyIn2a21E2Jn2u/3mzB4Vqguk6LomGtyWGuy27E5VqivG/H51OUrE+OpVyhu1eXr2CqxV6lwFaas06LoXHJ6lKTqgAAACH5BAEAAB8ALAAAAAASABIAAAVb4CeOYkGeaJmuKpuaLgrHX7IcD2IJrs0hAk8FAGAlDoiBYUNwTIyPAcagyUAirAXCwOUGAoVwmHSwVAiGSIDRYB0EgIthLaG44RN1g9JxwR0QdDQtg4SFM4csIQA7)}
.webix_tree_plus4{background-image:url(data:image/gif;base64,R0lGODlhEgASAMQfAFSWrmi52FCPplORqVmet/n//8HBwU2KoE+NpEyIn2a21E2Jn2u/3mzB4VqgumGtyWGuy27E5VqivE6Lom/H51OUrE+OpVyhu1eXr2CqxV6lwFaas06LoVKTqnHJ6gAAACH5BAEAAB8ALAAAAAASABIAAAVY4CeOZGmeaKqubEsmyzEhlqDCHCJ0FQCkiQNiUNgQHBLgZIApaDIPSGqBKFitgYBhuyUdLBVCARJgNFIHAeBSKEcoaLWE3KB4VGrHw+0yuEZ+fx+Bf4QmIQA7)} .webix_tree_plus4{background-image:url(data:image/gif;base64,R0lGODlhEgASAMQfAFSWrmi52FCPplORqVmet/n//8HBwU2KoE+NpEyIn2a21E2Jn2u/3mzB4VqgumGtyWGuy27E5VqivE6Lom/H51OUrE+OpVyhu1eXr2CqxV6lwFaas06LoVKTqnHJ6gAAACH5BAEAAB8ALAAAAAASABIAAAVY4CeOZGmeaKqubEsmyzEhlqDCHCJ0FQCkiQNiUNgQHBLgZIApaDIPSGqBKFitgYBhuyUdLBVCARJgNFIHAeBSKEcoaLWE3KB4VGrHw+0yuEZ+fx+Bf4QmIQA7)}
@ -308,9 +311,9 @@ div.webix_modal_cover{background-color:#000;cursor:default;filter:alpha(opacity=
.webix_cal_next_button,.webix_cal_prev_button{cursor:pointer;position:absolute;top:17px;width:30px;height:28px;background-position:11.5px 4px;-moz-user-select:none;-webkit-user-select:none;-ms-user-select:none;user-select:none} .webix_cal_next_button,.webix_cal_prev_button{cursor:pointer;position:absolute;top:17px;width:30px;height:28px;background-position:11.5px 4px;-moz-user-select:none;-webkit-user-select:none;-ms-user-select:none;user-select:none}
.webix_cal_prev_button{left:17px;background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAALCAYAAACzkJeoAAAASUlEQVR42p2QMQoAQQgD79EZ0Mqn5xCu2cPdYgUbJ0rMMxXgLeiuKq8gsJAz8weQ0QiwJEfECnr4wdlEnwJ68+yyhReC+c85oRecj0Um+pmo9wAAAABJRU5ErkJggg==);background-repeat:no-repeat} .webix_cal_prev_button{left:17px;background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAALCAYAAACzkJeoAAAASUlEQVR42p2QMQoAQQgD79EZ0Mqn5xCu2cPdYgUbJ0rMMxXgLeiuKq8gsJAz8weQ0QiwJEfECnr4wdlEnwJ68+yyhReC+c85oRecj0Um+pmo9wAAAABJRU5ErkJggg==);background-repeat:no-repeat}
.webix_cal_next_button{right:17px;background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAALCAYAAACzkJeoAAAARElEQVR42qXPsQoAIAhF0T76PdDJT7cUmtQhcmk4N8QVQ9JXN2bmgWOgqp+BiCRG2AYAMoi3/LxYdyagAnhA6G93TrABZaJFJjrFY8IAAAAASUVORK5CYII=);background-repeat:no-repeat} .webix_cal_next_button{right:17px;background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAALCAYAAACzkJeoAAAARElEQVR42qXPsQoAIAhF0T76PdDJT7cUmtQhcmk4N8QVQ9JXN2bmgWOgqp+BiCRG2AYAMoi3/LxYdyagAnhA6G93TrABZaJFJjrFY8IAAAAASUVORK5CYII=);background-repeat:no-repeat}
.webix_view>.webix_cal_header{margin:0 17px 10px;height:auto} .webix_view>.webix_cal_header{margin:0 17px 10px 17px;height:auto}
.webix_view>.webix_cal_header div{float:left;text-align:center;height:19px;font-size:11px;color:#1e2022;border-bottom:#cecece 1px solid;margin-bottom:7px} .webix_view>.webix_cal_header div{float:left;text-align:center;height:19px;font-size:11px;color:#1e2022;border-bottom:#cecece 1px solid;margin-bottom:7px}
.webix_cal_body{margin:0 17px} .webix_cal_body{margin:0 17px 0 17px}
.webix_cal_body .webix_cal_row{clear:both} .webix_cal_body .webix_cal_row{clear:both}
.webix_cal_body .webix_cal_row>div{float:left;text-align:center;height:100%} .webix_cal_body .webix_cal_row>div{float:left;text-align:center;height:100%}
.webix_cal_body .webix_cal_select,.webix_cal_body .webix_cal_select.webix_cal_today,.webix_cal_body .webix_selected{color:#1e2022;background:#ffdb8f} .webix_cal_body .webix_cal_select,.webix_cal_body .webix_cal_select.webix_cal_today,.webix_cal_body .webix_selected{color:#1e2022;background:#ffdb8f}
@ -325,30 +328,30 @@ div.webix_modal_cover{background-color:#000;cursor:default;filter:alpha(opacity=
.webix_cal_block{float:left;text-align:center;cursor:pointer;-moz-user-select:none;-webkit-user-select:none;-ms-user-select:none;user-select:none} .webix_cal_block{float:left;text-align:center;cursor:pointer;-moz-user-select:none;-webkit-user-select:none;-ms-user-select:none;user-select:none}
.webix_selected{background:#ffdb8f;color:#1e2022} .webix_selected{background:#ffdb8f;color:#1e2022}
.webix_cal_footer{text-align:center;font-size:12px;color:#444;margin:4px 17px;padding-top:2px;cursor:pointer} .webix_cal_footer{text-align:center;font-size:12px;color:#444;margin:4px 17px;padding-top:2px;cursor:pointer}
.webix_cal_icons{float:right;padding:4px} .webix_cal_icons{float:right;padding:4px 4px}
.webix_cal_time{padding:2px 4px} .webix_cal_time{padding:2px 4px}
.webix_cal_time_icons{float:left} .webix_cal_time_icons{float:left}
.webix_cal_icons .webix_cal_icon{text-align:center;padding:2px 4px} .webix_cal_icons .webix_cal_icon{text-align:center;padding:2px 4px}
.webix_cal_icon:hover,.webix_cal_month_name:hover,.webix_cal_time:hover{text-decoration:underline;color:#d6e8ff} .webix_cal_icon:hover,.webix_cal_month_name:hover,.webix_cal_time:hover{text-decoration:underline;color:#d6e8ff}
.webix_cal_month_name.webix_readonly:hover{text-decoration:none;color:#1e2022;cursor:default} .webix_cal_month_name.webix_readonly:hover{text-decoration:none;color:#1e2022;cursor:default}
.webix_time_header{padding:17px 17px 1px} .webix_time_header{padding:17px 17px 1px 17px}
.webix_time_header .webix_cal_hours,.webix_time_header .webix_cal_minutes{display:inline-block;font-size:14px;text-align:center;height:26px;line-height:22px;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box;border-bottom:1px solid #a4bed4} .webix_time_header .webix_cal_hours,.webix_time_header .webix_cal_minutes{display:inline-block;font-size:14px;text-align:center;height:26px;line-height:22px;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box;border-bottom:1px solid #a4bed4}
.webix_time_header .webix_cal_minutes{padding-left:1px;border-left:none} .webix_time_header .webix_cal_minutes{padding-left:1px;border-left:none}
.webix_time_header .webix_cal_hours{border-right:none!important} .webix_time_header .webix_cal_hours{border-right:none!important}
.webix_cal_body .webix_hours{float:left;border-right:1px solid #cecece;padding-right:1px} .webix_cal_body .webix_hours{float:left;border-right:1px solid #cecece;padding-right:1px}
.webix_cal_body .webix_minutes{float:left;padding-left:1px} .webix_cal_body .webix_minutes{float:left;padding-left:1px}
.webix_cal_block_empty{float:left;text-align:center} .webix_cal_block_empty{float:left;text-align:center}
.webix_time_footer{text-align:center;font-size:12px;padding:7px 17px 0;cursor:pointer} .webix_time_footer{text-align:center;font-size:12px;padding:7px 17px 0 17px;cursor:pointer}
.webix_cal_done{margin-top:3px;padding:2px 7px;font-size:12px;border-radius:6px;border:1px solid #a4bed4;color:#1e2022!important;box-shadow:0 1px 1px #fff inset;background:0 1px repeat-x #e5f1ff;background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#eaf3ff),color-stop(50%,#e5f1ff),color-stop(100%,#d6e8ff)) 0 1px repeat-x;background-image:-webkit-linear-gradient(top,#eaf3ff 0,#e5f1ff 50%,#d6e8ff 100%);background-image:-moz-linear-gradient(top,#eaf3ff 0,#e5f1ff 60%,#d6e8ff 100%);background-image:-ms-linear-gradient(top,#eaf3ff 0,#e5f1ff 60%,#d6e8ff 100%);background-image:-o-linear-gradient(top,#eaf3ff 0,#e5f1ff 60%,#d6e8ff 100%);float:right} .webix_cal_done{margin-top:3px;padding:2px 7px;font-size:12px;border-radius:6px;border:1px solid #a4bed4;color:#1e2022!important;box-shadow:0 1px 1px #fff inset;background:#e5f1ff;background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#eaf3ff),color-stop(50%,#e5f1ff),color-stop(100%,#d6e8ff));background-image:-webkit-linear-gradient(top,#eaf3ff 0,#e5f1ff 50%,#d6e8ff 100%);background-image:-moz-linear-gradient(top,#eaf3ff 0,#e5f1ff 60%,#d6e8ff 100%);background-image:-ms-linear-gradient(top,#eaf3ff 0,#e5f1ff 60%,#d6e8ff 100%);background-image:-o-linear-gradient(top,#eaf3ff 0,#e5f1ff 60%,#d6e8ff 100%);background-position:0 1px;background-repeat:repeat-x;float:right}
.webix_cal_blurred_hours{background-color:#f7f7f7} .webix_cal_blurred_hours{background-color:#f7f7f7}
.webix_property_line{clear:both;width:100%;min-height:24px;height:auto} .webix_property_line{clear:both;width:100%;min-height:24px;height:auto}
.webix_property_line:nth-child(odd){background-color:#f7f7f7} .webix_property_line:nth-child(odd){background-color:#f7f7f7}
.webix_property_label,.webix_property_value{height:24px;line-height:24px;padding-left:10px;float:left;border-bottom:1px solid #a4bed4;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box;overflow:hidden} .webix_property_label,.webix_property_value{height:24px;line-height:24px;padding-left:10px;float:left;border-bottom:1px solid #a4bed4;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box;overflow:hidden}
.webix_property_label{border-right:1px solid #a4bed4} .webix_property_label{border-right:1px solid #a4bed4}
.webix_property_label_line{padding-left:10px;font-family:Helvetica,Verdana;font-size:13px;font-weight:700;color:#1e2022!important;box-shadow:0 1px 1px #fff inset;background:0 1px repeat-x #e5f1ff;background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#eaf3ff),color-stop(50%,#e5f1ff),color-stop(100%,#d6e8ff)) 0 1px repeat-x;background-image:-webkit-linear-gradient(top,#eaf3ff 0,#e5f1ff 50%,#d6e8ff 100%);background-image:-moz-linear-gradient(top,#eaf3ff 0,#e5f1ff 60%,#d6e8ff 100%);background-image:-ms-linear-gradient(top,#eaf3ff 0,#e5f1ff 60%,#d6e8ff 100%);background-image:-o-linear-gradient(top,#eaf3ff 0,#e5f1ff 60%,#d6e8ff 100%);line-height:32px;line-height:23px;border-bottom:1px solid #ebebeb} .webix_property_label_line{padding-left:10px;font-family:Helvetica,Verdana;color:#1e2022;font-size:13px;font-weight:700;color:#1e2022!important;box-shadow:0 1px 1px #fff inset;background:#e5f1ff;background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#eaf3ff),color-stop(50%,#e5f1ff),color-stop(100%,#d6e8ff));background-image:-webkit-linear-gradient(top,#eaf3ff 0,#e5f1ff 50%,#d6e8ff 100%);background-image:-moz-linear-gradient(top,#eaf3ff 0,#e5f1ff 60%,#d6e8ff 100%);background-image:-ms-linear-gradient(top,#eaf3ff 0,#e5f1ff 60%,#d6e8ff 100%);background-image:-o-linear-gradient(top,#eaf3ff 0,#e5f1ff 60%,#d6e8ff 100%);background-position:0 1px;background-repeat:repeat-x;line-height:32px;line-height:23px;border-bottom:1px solid #ebebeb}
.webix_property_label_line .webix_el_label,.webix_property_label_line .webix_inp_label{color:#1e2022} .webix_property_label_line .webix_el_label,.webix_property_label_line .webix_inp_label{color:#1e2022}
.webix_property_col_val{width:100%;height:100%;-moz-user-select:text;-webkit-user-select:text;user-select:text} .webix_property_col_val{width:100%;height:100%;-moz-user-select:text;-webkit-user-select:text;user-select:text}
.webix_property_col_ind{float:right;border:1px solid #a4bed4;border-radius:2px;width:30px;cursor:pointer;height:13px;margin:3px 10px 3px 0} .webix_property_col_ind{float:right;border:1px solid #a4bed4;border-radius:3px;width:16px;cursor:pointer;height:16px;margin:4px 10px 3px 0}
.webix_property_value{text-overflow:ellipsis;white-space:nowrap} .webix_property_value{text-overflow:ellipsis;white-space:nowrap}
.webix_property_check{margin-top:5px} .webix_property_check{margin-top:5px}
.webix_tooltip{display:none;position:absolute;z-index:10000;background-color:#fff;padding:5px 10px;border:1px solid #a4bed4;font-family:Helvetica,Verdana;font-size:13px;color:#1e2022;border-radius:0;box-shadow:1px 1px 0 0 #CCC} .webix_tooltip{display:none;position:absolute;z-index:10000;background-color:#fff;padding:5px 10px;border:1px solid #a4bed4;font-family:Helvetica,Verdana;font-size:13px;color:#1e2022;border-radius:0;box-shadow:1px 1px 0 0 #CCC}
@ -377,16 +380,16 @@ div.webix_modal_cover{background-color:#000;cursor:default;filter:alpha(opacity=
.webix_el_button button,.webix_el_button input,.webix_el_toggle button,.webix_el_toggle input,.webixbutton{border-radius:6px;border:1px solid #a4bed4;height:100%;width:100%;cursor:pointer;font-size:13px;font-family:Helvetica,Verdana;-webkit-appearance:none} .webix_el_button button,.webix_el_button input,.webix_el_toggle button,.webix_el_toggle input,.webixbutton{border-radius:6px;border:1px solid #a4bed4;height:100%;width:100%;cursor:pointer;font-size:13px;font-family:Helvetica,Verdana;-webkit-appearance:none}
.webix_el_button .webix_img_btn,.webix_el_button .webix_img_btn .webix_img_btn_text,.webix_el_button .webix_img_btn_top,.webix_el_button .webix_img_btn_top .webix_img_btn_text,.webix_el_toggle .webix_img_btn,.webix_el_toggle .webix_img_btn .webix_img_btn_text,.webix_el_toggle .webix_img_btn_top,.webix_el_toggle .webix_img_btn_top .webix_img_btn_text{color:#1e2022} .webix_el_button .webix_img_btn,.webix_el_button .webix_img_btn .webix_img_btn_text,.webix_el_button .webix_img_btn_top,.webix_el_button .webix_img_btn_top .webix_img_btn_text,.webix_el_toggle .webix_img_btn,.webix_el_toggle .webix_img_btn .webix_img_btn_text,.webix_el_toggle .webix_img_btn_top,.webix_el_toggle .webix_img_btn_top .webix_img_btn_text{color:#1e2022}
.webix_el_button .webix_img_btn,.webix_el_button .webix_img_btn_top,.webix_el_toggle .webix_img_btn,.webix_el_toggle .webix_img_btn_top{border:0} .webix_el_button .webix_img_btn,.webix_el_button .webix_img_btn_top,.webix_el_toggle .webix_img_btn,.webix_el_toggle .webix_img_btn_top{border:0}
.webixbutton,.webixtype_base,.webixtype_next,.webixtype_prev{color:#1e2022;background:#e6e6e6;background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#fff),color-stop(100%,#e6e6e6));background:-webkit-linear-gradient(#fff,#e6e6e6);background:-moz-linear-gradient(#fff,#e6e6e6);background:-ms-linear-gradient(top,#fff,#e6e6e6);background:-o-linear-gradient(top,#fff,#e6e6e6);font-size:13px;font-family:Helvetica,Verdana;padding:0;border:0} .webixbutton,.webixtype_base,.webixtype_next,.webixtype_prev{color:#1e2022;background:#e6e6e6;background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#fff),color-stop(100%,#e6e6e6));background-image:-webkit-linear-gradient(#fff,#e6e6e6);background-image:-moz-linear-gradient(#fff,#e6e6e6);background-image:-ms-linear-gradient(top,#fff,#e6e6e6);background-image:-o-linear-gradient(top,#fff,#e6e6e6);font-size:13px;font-family:Helvetica,Verdana;padding:0;border:0}
.webixtype_form{color:#1e2022!important;box-shadow:0 1px 1px #fff inset;background:0 1px repeat-x #e5f1ff;background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#eaf3ff),color-stop(50%,#e5f1ff),color-stop(100%,#d6e8ff)) 0 1px repeat-x;background-image:-webkit-linear-gradient(top,#eaf3ff 0,#e5f1ff 50%,#d6e8ff 100%);background-image:-moz-linear-gradient(top,#eaf3ff 0,#e5f1ff 60%,#d6e8ff 100%);background-image:-ms-linear-gradient(top,#eaf3ff 0,#e5f1ff 60%,#d6e8ff 100%);background-image:-o-linear-gradient(top,#eaf3ff 0,#e5f1ff 60%,#d6e8ff 100%)} .webixtype_form{color:#1e2022!important;box-shadow:0 1px 1px #fff inset;background:#e5f1ff;background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#eaf3ff),color-stop(50%,#e5f1ff),color-stop(100%,#d6e8ff));background-image:-webkit-linear-gradient(top,#eaf3ff 0,#e5f1ff 50%,#d6e8ff 100%);background-image:-moz-linear-gradient(top,#eaf3ff 0,#e5f1ff 60%,#d6e8ff 100%);background-image:-ms-linear-gradient(top,#eaf3ff 0,#e5f1ff 60%,#d6e8ff 100%);background-image:-o-linear-gradient(top,#eaf3ff 0,#e5f1ff 60%,#d6e8ff 100%);background-position:0 1px;background-repeat:repeat-x}
.webixtype_form:active{box-shadow:inset 0 3px 5px rgba(0,0,0,.125);background:no-repeat #d6e8ff} .webixtype_form:active{box-shadow:inset 0 3px 5px rgba(0,0,0,.125);background:#d6e8ff;background-repeat:no-repeat}
.webix_el_button .webixtype_danger{background:#c10;background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#e7877e),color-stop(100%,#c10));background:-webkit-linear-gradient(#e7877e,#c10);background:-moz-linear-gradient(#e7877e,#c10);background:-ms-linear-gradient(top,#e7877e,#c10);background:-o-linear-gradient(top,#e7877e,#c10);border:none!important;color:#fff} .webix_el_button .webixtype_danger{background:#c10;background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#e7877e),color-stop(100%,#c10));background-image:-webkit-linear-gradient(#e7877e,#c10);background-image:-moz-linear-gradient(#e7877e,#c10);background-image:-ms-linear-gradient(top,#e7877e,#c10);background-image:-o-linear-gradient(top,#e7877e,#c10);border:none!important;color:#fff}
.webix_el_button .webix_el_box,.webix_el_toggle .webix_el_box{position:relative} .webix_el_button .webix_el_box,.webix_el_toggle .webix_el_box{position:relative}
.webix_el_button .webix_el_arrow,.webix_el_toggle .webix_el_arrow{position:absolute;z-index:1} .webix_el_button .webix_el_arrow,.webix_el_toggle .webix_el_arrow{position:absolute}
.webixtype_next,.webixtype_prev{position:absolute;z-index:2;top:0} .webixtype_next,.webixtype_prev{position:absolute;z-index:1;top:0}
input.webixtype_prev{border-left-width:0;border-radius:0 6px 6px 0;-webkit-border-radius:0 6px 6px 0;-moz-border-radius-topleft:0;-moz-border-radius-bottomleft:0;-moz-border-radius-topright:6px;-moz-border-radius-bottomright:6px} input.webixtype_prev{border-left-width:0;border-radius:0;-webkit-border-top-left-radius:0;-webkit-border-bottom-left-radius:0;-webkit-border-top-right-radius:6px;-webkit-border-bottom-right-radius:6px;-moz-border-radius-topleft:0;-moz-border-radius-bottomleft:0;-moz-border-radius-topright:6px;-moz-border-radius-bottomright:6px;border-top-left-radius:0;border-bottom-left-radius:0;border-top-right-radius:6px;border-bottom-right-radius:6px}
input.webixtype_next{border-right-width:0;border-radius:6px 0 0 6px;-webkit-border-radius:6px 0 0 6px;-moz-border-radius-topleft:6px;-moz-border-radius-bottomleft:6px;-moz-border-radius-topright:0;-moz-border-radius-bottomright:0} input.webixtype_next{border-right-width:0;border-radius:0;-webkit-border-top-left-radius:6px;-webkit-border-bottom-left-radius:6px;-webkit-border-top-right-radius:0;-webkit-border-bottom-right-radius:0;-moz-border-radius-topleft:6px;-moz-border-radius-bottomleft:6px;-moz-border-radius-topright:0;-moz-border-radius-bottomright:0;border-top-left-radius:6px;border-bottom-left-radius:6px;border-top-right-radius:0;border-bottom-right-radius:0}
.webix_el_arrow{width:18px;height:18px;color:#1e2022;background:#e6e6e6;background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#fff),color-stop(100%,#e6e6e6));background:-webkit-linear-gradient(#fff,#e6e6e6);background:-moz-linear-gradient(#fff,#e6e6e6);background:-ms-linear-gradient(top,#fff,#e6e6e6);background:-o-linear-gradient(top,#fff,#e6e6e6);border:1px solid #a4bed4} .webix_el_arrow{width:18px;height:18px;color:#1e2022;background:#e6e6e6;background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#fff),color-stop(100%,#e6e6e6));background-image:-webkit-linear-gradient(#fff,#e6e6e6);background-image:-moz-linear-gradient(#fff,#e6e6e6);background-image:-ms-linear-gradient(top,#fff,#e6e6e6);background-image:-o-linear-gradient(top,#fff,#e6e6e6);border:1px solid #a4bed4}
.webixtype_prev_arrow{-webkit-transform:rotate(-45deg);-moz-transform:rotate(-45deg);-ms-transform:rotate(-45deg);-o-transform:rotate(-45deg)} .webixtype_prev_arrow{-webkit-transform:rotate(-45deg);-moz-transform:rotate(-45deg);-ms-transform:rotate(-45deg);-o-transform:rotate(-45deg)}
.webixtype_next_arrow{-webkit-transform:rotate(45deg);-moz-transform:rotate(45deg);-ms-transform:rotate(45deg);-o-transform:rotate(45deg)} .webixtype_next_arrow{-webkit-transform:rotate(45deg);-moz-transform:rotate(45deg);-ms-transform:rotate(45deg);-o-transform:rotate(45deg)}
.webix_img_btn_abs_top .webix_icon,.webix_img_btn_top .webix_icon{font-size:22px;position:absolute;top:8px;left:50%} .webix_img_btn_abs_top .webix_icon,.webix_img_btn_top .webix_icon{font-size:22px;position:absolute;top:8px;left:50%}
@ -405,7 +408,7 @@ input.webixtype_next{border-right-width:0;border-radius:6px 0 0 6px;-webkit-bord
.webix_img_btn_top{background-position:center top;vertical-align:top} .webix_img_btn_top{background-position:center top;vertical-align:top}
.webix_img_btn_abs .webix_img_btn_text{bottom:8px} .webix_img_btn_abs .webix_img_btn_text{bottom:8px}
.webix_pressed .webix_img_btn,.webix_pressed .webix_img_btn_top{box-shadow:inset 0 3px 5px rgba(0,0,0,.125);background-color:rgba(0,0,0,.05)} .webix_pressed .webix_img_btn,.webix_pressed .webix_img_btn_top{box-shadow:inset 0 3px 5px rgba(0,0,0,.125);background-color:rgba(0,0,0,.05)}
.webix_control .webix_disabled_box .webix_el_arrow,.webix_control .webix_disabled_box .webix_img_btn,.webix_control .webix_disabled_box .webixtype_base,.webix_control .webix_disabled_box .webixtype_danger,.webix_control .webix_disabled_box .webixtype_form,.webix_control .webix_disabled_box .webixtype_next,.webix_control .webix_disabled_box .webixtype_prev{color:#aaa!important;background:#e9e9e9;background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#fbfbfb),color-stop(100%,#e9e9e9));background:-webkit-linear-gradient(#fbfbfb,#e9e9e9);background:-moz-linear-gradient(#fbfbfb,#e9e9e9);background:-ms-linear-gradient(top,#fbfbfb,#e9e9e9);background:-o-linear-gradient(top,#fbfbfb,#e9e9e9);border-color:#d5d5d5!important} .webix_control .webix_disabled_box .webix_el_arrow,.webix_control .webix_disabled_box .webix_img_btn,.webix_control .webix_disabled_box .webixtype_base,.webix_control .webix_disabled_box .webixtype_danger,.webix_control .webix_disabled_box .webixtype_form,.webix_control .webix_disabled_box .webixtype_next,.webix_control .webix_disabled_box .webixtype_prev{color:#aaa!important;background:#e9e9e9;background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#fbfbfb),color-stop(100%,#e9e9e9));background-image:-webkit-linear-gradient(#fbfbfb,#e9e9e9);background-image:-moz-linear-gradient(#fbfbfb,#e9e9e9);background-image:-ms-linear-gradient(top,#fbfbfb,#e9e9e9);background-image:-o-linear-gradient(top,#fbfbfb,#e9e9e9);border-color:#d5d5d5!important}
.webix_control .webix_disabled_box .webix_img_btn_text{color:#aaa} .webix_control .webix_disabled_box .webix_img_btn_text{color:#aaa}
.webix_control .webix_disabled_box .webix_image{opacity:.4} .webix_control .webix_disabled_box .webix_image{opacity:.4}
.webix_disabled_view.webix_control .webix_icon,.webix_disabled_view.webix_control .webix_icon_btn,.webix_layout_toolbar.webix_toolbar .webix_disabled_view.webix_control .webix_disabled_box .webix_img_btn_text,.webix_layout_toolbar.webix_toolbar .webix_disabled_view.webix_control .webix_icon,.webix_layout_toolbar.webix_toolbar .webix_disabled_view.webix_control .webix_icon_btn{color:#aaa} .webix_disabled_view.webix_control .webix_icon,.webix_disabled_view.webix_control .webix_icon_btn,.webix_layout_toolbar.webix_toolbar .webix_disabled_view.webix_control .webix_disabled_box .webix_img_btn_text,.webix_layout_toolbar.webix_toolbar .webix_disabled_view.webix_control .webix_icon,.webix_layout_toolbar.webix_toolbar .webix_disabled_view.webix_control .webix_icon_btn{color:#aaa}
@ -415,31 +418,31 @@ input.webixtype_next{border-right-width:0;border-radius:6px 0 0 6px;-webkit-bord
.webix_richtext_container .webix_template{padding:10px;overflow-x:auto;border:solid #ccc;border-width:0 1px 1px;border-radius:0 0 6px 6px} .webix_richtext_container .webix_template{padding:10px;overflow-x:auto;border:solid #ccc;border-width:0 1px 1px;border-radius:0 0 6px 6px}
.webix_control{background:0 0} .webix_control{background:0 0}
.webix_control button,.webix_control input,.webix_control textarea,.webix_el_label,.webix_inp_bottom_label,.webix_inp_label,.webix_inp_top_label{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box;font-family:Helvetica,Verdana} .webix_control button,.webix_control input,.webix_control textarea,.webix_el_label,.webix_inp_bottom_label,.webix_inp_label,.webix_inp_top_label{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box;font-family:Helvetica,Verdana}
.webix_el_box{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box;padding:2px;height:100%;width:100%;font-size:13px;overflow:hidden} .webix_el_box{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box;padding:2px 2px;height:100%;width:100%;font-size:13px;overflow:hidden}
.webix_el_colorpicker input,.webix_el_combo input,.webix_el_datepicker input,.webix_el_search input,.webix_el_text input{height:100%;padding:0 10px;border:1px solid #a4bed4;color:#1e2022;font-size:13px;font-family:Helvetica,Verdana;-webkit-appearance:none;border-radius:0} .webix_el_colorpicker input,.webix_el_combo input,.webix_el_datepicker input,.webix_el_search input,.webix_el_text input{height:100%;padding:0 10px;border:1px solid #a4bed4;color:#1e2022;font-size:13px;font-family:Helvetica,Verdana;-webkit-appearance:none;border-radius:0}
.webix_inp_label,.webix_inp_top_label,.webix_label_right{color:#1e2022;text-transform:none;font-size:13px} .webix_inp_label,.webix_inp_top_label,.webix_label_right{color:#1e2022;text-transform:none;font-size:13px}
.webix_inp_label{float:left;padding-top:1px;padding-left:2px;padding-right:7.5px;text-overflow:ellipsis;overflow:hidden;white-space:nowrap} .webix_inp_label{float:left;padding-top:1px;padding-left:2px;padding-right:7.5px;text-overflow:ellipsis;overflow:hidden;white-space:nowrap}
.webix_inp_top_label{float:none;padding:2px 2px 0} .webix_inp_top_label{float:none;padding:2px 2px;padding-bottom:0}
.webix_inp_bottom_label{font-size:11px;color:#2a2d30} .webix_inp_bottom_label{font-size:11px;color:#2a2d30}
.webix_label_right{padding-left:7.5px} .webix_label_right{padding-left:7.5px}
.webix_el_checkbox .webix_label_right{display:inline-block;padding-top:2px} .webix_el_checkbox .webix_label_right{display:inline-block;padding-top:2px}
.webix_el_label{color:#1e2022;line-height:30px;padding:2px} .webix_el_label{color:#1e2022;line-height:30px;padding:2px 2px}
.webix_toolbar .webix_el_label{padding-left:10px;text-transform:none} .webix_toolbar .webix_el_label{padding-left:10px;text-transform:none}
.webix_el_select select{height:100%;font-size:13px;font-family:Helvetica,Verdana;border:1px solid #a4bed4} .webix_el_select select{height:100%;font-size:13px;font-family:Helvetica,Verdana;border:1px solid #a4bed4}
body:not(:-moz-handler-blocked) .webix_el_select select{padding:.2em} body:not(:-moz-handler-blocked) .webix_el_select select{padding:.2em}
.webix_all_segments,.webix_el_group{height:100%;white-space:nowrap} .webix_all_segments,.webix_el_group{height:100%;white-space:nowrap}
.webix_inp_counter_next,.webix_inp_counter_prev,.webix_inp_counter_value{border:1px solid #a4bed4;cursor:pointer;-webkit-appearance:none;width:20px;height:100%;vertical-align:top} .webix_inp_counter_next,.webix_inp_counter_prev,.webix_inp_counter_value{border:1px solid #a4bed4;cursor:pointer;-webkit-appearance:none;width:20px;height:100%;vertical-align:top}
.webix_inp_counter_value{font-size:13px;width:50px;color:#1e2022;text-align:center;border-left-width:0;border-right-width:0;-webkit-appearance:none;border-radius:0} .webix_inp_counter_value{font-size:13px;width:50px;color:#1e2022;text-align:center;border-left-width:0;border-right-width:0;-webkit-appearance:none;border-radius:0}
.webix_inp_counter_prev{padding-left:6px;color:#1e2022;background:#e6e6e6;background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#fff),color-stop(100%,#e6e6e6));background:-webkit-linear-gradient(#fff,#e6e6e6);background:-moz-linear-gradient(#fff,#e6e6e6);background:-ms-linear-gradient(top,#fff,#e6e6e6);background:-o-linear-gradient(top,#fff,#e6e6e6);-webkit-border-radius:6px 0 0 6px;-moz-border-radius-topleft:6px;-moz-border-radius-bottomleft:6px;-moz-border-radius-topright:0;-moz-border-radius-bottomright:0;border-radius:6px 0 0 6px} .webix_inp_counter_prev{padding-left:6px;color:#1e2022;background:#e6e6e6;background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#fff),color-stop(100%,#e6e6e6));background-image:-webkit-linear-gradient(#fff,#e6e6e6);background-image:-moz-linear-gradient(#fff,#e6e6e6);background-image:-ms-linear-gradient(top,#fff,#e6e6e6);background-image:-o-linear-gradient(top,#fff,#e6e6e6);-webkit-border-top-left-radius:6px;-webkit-border-bottom-left-radius:6px;-webkit-border-top-right-radius:0;-webkit-border-bottom-right-radius:0;-moz-border-radius-topleft:6px;-moz-border-radius-bottomleft:6px;-moz-border-radius-topright:0;-moz-border-radius-bottomright:0;border-top-left-radius:6px;border-bottom-left-radius:6px;border-top-right-radius:0;border-bottom-right-radius:0}
.webix_inp_counter_next{padding-left:6px;color:#1e2022;background:#e6e6e6;background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#fff),color-stop(100%,#e6e6e6));background:-webkit-linear-gradient(#fff,#e6e6e6);background:-moz-linear-gradient(#fff,#e6e6e6);background:-ms-linear-gradient(top,#fff,#e6e6e6);background:-o-linear-gradient(top,#fff,#e6e6e6);-webkit-border-radius:0 6px 6px 0;-moz-border-radius-topleft:0;-moz-border-radius-bottomleft:0;-moz-border-radius-topright:6px;-moz-border-radius-bottomright:6px;border-radius:0 6px 6px 0} .webix_inp_counter_next{padding-left:6px;color:#1e2022;background:#e6e6e6;background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#fff),color-stop(100%,#e6e6e6));background-image:-webkit-linear-gradient(#fff,#e6e6e6);background-image:-moz-linear-gradient(#fff,#e6e6e6);background-image:-ms-linear-gradient(top,#fff,#e6e6e6);background-image:-o-linear-gradient(top,#fff,#e6e6e6);-webkit-border-top-left-radius:0;-webkit-border-bottom-left-radius:0;-webkit-border-top-right-radius:6px;-webkit-border-bottom-right-radius:6px;-moz-border-radius-topleft:0;-moz-border-radius-bottomleft:0;-moz-border-radius-topright:6px;-moz-border-radius-bottomright:6px;border-top-left-radius:0;border-bottom-left-radius:0;border-top-right-radius:6px;border-bottom-right-radius:6px}
.webix_el_textarea textarea{border:1px solid #a4bed4;height:100%;margin:0;padding:5px 10px;color:#1e2022;resize:none;font-size:13px;font-family:Helvetica,Verdana} .webix_el_textarea textarea{border:1px solid #a4bed4;height:100%;margin:0;padding:5px;color:#1e2022;resize:none;font-size:13px;font-family:Helvetica,Verdana;padding-left:10px;padding-right:10px}
.webix_el_textarea .webix_inp_label{padding-top:8.5px} .webix_el_textarea .webix_inp_label{padding-top:8.5px}
.webix_segment_0,.webix_segment_1{border:1px solid #a4bed4;border-right-width:0;height:100%} .webix_segment_0,.webix_segment_1{border:1px solid #a4bed4;border-right-width:0;height:100%}
.webix_segment_0{-webkit-border-radius:6px 0 0 6px;-moz-border-radius-topleft:6px;-moz-border-radius-bottomleft:6px;-moz-border-radius-topright:0;-moz-border-radius-bottomright:0;border-radius:6px 0 0 6px} .webix_segment_0{-webkit-border-top-left-radius:6px;-webkit-border-bottom-left-radius:6px;-webkit-border-top-right-radius:0;-webkit-border-bottom-right-radius:0;-moz-border-radius-topleft:6px;-moz-border-radius-bottomleft:6px;-moz-border-radius-topright:0;-moz-border-radius-bottomright:0;border-top-left-radius:6px;border-bottom-left-radius:6px;border-top-right-radius:0;border-bottom-right-radius:0}
.webix_segment_N{border:1px solid #a4bed4;height:100%;-webkit-border-radius:0 6px 6px 0;-moz-border-radius-topleft:0;-moz-border-radius-bottomleft:0;-moz-border-radius-topright:6px;-moz-border-radius-bottomright:6px;border-radius:0 6px 6px 0} .webix_segment_N{border:1px solid #a4bed4;height:100%;-webkit-border-top-left-radius:0;-webkit-border-bottom-left-radius:0;-webkit-border-top-right-radius:6px;-webkit-border-bottom-right-radius:6px;-moz-border-radius-topleft:0;-moz-border-radius-bottomleft:0;-moz-border-radius-topright:6px;-moz-border-radius-bottomright:6px;border-top-left-radius:0;border-bottom-left-radius:0;border-top-right-radius:6px;border-bottom-right-radius:6px}
.webix_segment_N:first-child{-webkit-border-radius:6px;-moz-border-radius-topleft:6px;-moz-border-radius-bottomleft:6px;-moz-border-radius-topright:6px;-moz-border-radius-bottomright:6px;border-radius:6px} .webix_segment_N:first-child{-webkit-border-top-left-radius:6px;-webkit-border-bottom-left-radius:6px;-webkit-border-top-right-radius:6px;-webkit-border-bottom-right-radius:6px;-moz-border-radius-topleft:6px;-moz-border-radius-bottomleft:6px;-moz-border-radius-topright:6px;-moz-border-radius-bottomright:6px;border-top-left-radius:6px;border-bottom-left-radius:6px;border-top-right-radius:6px;border-bottom-right-radius:6px}
.webix_segment_0,.webix_segment_1,.webix_segment_N{-webkit-appearance:none;color:#1e2022;background:#e6e6e6;background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#fff),color-stop(100%,#e6e6e6));background:-webkit-linear-gradient(#fff,#e6e6e6);background:-moz-linear-gradient(#fff,#e6e6e6);background:-ms-linear-gradient(top,#fff,#e6e6e6);background:-o-linear-gradient(top,#fff,#e6e6e6);font-size:13px;font-family:Helvetica,Verdana;cursor:pointer;padding:0} .webix_segment_0,.webix_segment_1,.webix_segment_N{-webkit-appearance:none;color:#1e2022;background:#e6e6e6;background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#fff),color-stop(100%,#e6e6e6));background-image:-webkit-linear-gradient(#fff,#e6e6e6);background-image:-moz-linear-gradient(#fff,#e6e6e6);background-image:-ms-linear-gradient(top,#fff,#e6e6e6);background-image:-o-linear-gradient(top,#fff,#e6e6e6);font-size:13px;font-family:Helvetica,Verdana;cursor:pointer;padding:0}
.webix_segment_0.webix_selected,.webix_segment_1.webix_selected,.webix_segment_N.webix_selected{color:#1e2022;background:#ffdb8f;background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#ffebc2),color-stop(100%,#ffdb8f));background:-webkit-linear-gradient(#ffebc2,#ffdb8f);background:-moz-linear-gradient(#ffebc2,#ffdb8f);background:-ms-linear-gradient(top,#ffebc2,#ffdb8f);background:-o-linear-gradient(top,#ffebc2,#ffdb8f)} .webix_segment_0.webix_selected,.webix_segment_1.webix_selected,.webix_segment_N.webix_selected{background-size:100% 100%;background-position:0 0;color:#1e2022;background:#ffdb8f;background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#ffebc2),color-stop(100%,#ffdb8f));background-image:-webkit-linear-gradient(#ffebc2,#ffdb8f);background-image:-moz-linear-gradient(#ffebc2,#ffdb8f);background-image:-ms-linear-gradient(top,#ffebc2,#ffdb8f);background-image:-o-linear-gradient(top,#ffebc2,#ffdb8f)}
.webix_inp_static{border:1px solid #a4bed4;background:#fff;overflow:hidden;padding:0 10px;height:100%;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box;float:left} .webix_inp_static{border:1px solid #a4bed4;background:#fff;overflow:hidden;padding:0 10px;height:100%;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box;float:left}
.webix_inp_static .webix_placeholder{color:#666d73} .webix_inp_static .webix_placeholder{color:#666d73}
.webix_el_colorpicker,.webix_el_combo,.webix_el_datepicker,.webix_el_richselect,.webix_el_search{font-size:13px} .webix_el_colorpicker,.webix_el_combo,.webix_el_datepicker,.webix_el_richselect,.webix_el_search{font-size:13px}
@ -465,15 +468,15 @@ body:not(:-moz-handler-blocked) .webix_el_select select{padding:.2em}
.webix_disabled_view.webix_control .webix_image{-webkit-filter:grayscale(100%);filter:grayscale(100%)} .webix_disabled_view.webix_control .webix_image{-webkit-filter:grayscale(100%);filter:grayscale(100%)}
.webix_view.webix_control .webix_disabled_box .webix_input_icon,.webix_view.webix_control .webix_disabled_box button,.webix_view.webix_control .webix_disabled_box label{color:#aaa} .webix_view.webix_control .webix_disabled_box .webix_input_icon,.webix_view.webix_control .webix_disabled_box button,.webix_view.webix_control .webix_disabled_box label{color:#aaa}
.webix_view.webix_control .webix_disabled_box .webix_inp_static,.webix_view.webix_control .webix_disabled_box input,.webix_view.webix_control .webix_disabled_box select,.webix_view.webix_control .webix_disabled_box textarea{color:#aaa;background:#eee} .webix_view.webix_control .webix_disabled_box .webix_inp_static,.webix_view.webix_control .webix_disabled_box input,.webix_view.webix_control .webix_disabled_box select,.webix_view.webix_control .webix_disabled_box textarea{color:#aaa;background:#eee}
.webix_view.webix_control .webix_disabled_box .webix_inp_counter_next,.webix_view.webix_control .webix_disabled_box .webix_inp_counter_prev,.webix_view.webix_control .webix_disabled_box .webix_segment_0,.webix_view.webix_control .webix_disabled_box .webix_segment_1,.webix_view.webix_control .webix_disabled_box .webix_segment_N{color:#aaa!important;background:#e9e9e9;background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#fbfbfb),color-stop(100%,#e9e9e9));background:-webkit-linear-gradient(#fbfbfb,#e9e9e9);background:-moz-linear-gradient(#fbfbfb,#e9e9e9);background:-ms-linear-gradient(top,#fbfbfb,#e9e9e9);background:-o-linear-gradient(top,#fbfbfb,#e9e9e9);border-color:#d5d5d5!important} .webix_view.webix_control .webix_disabled_box .webix_inp_counter_next,.webix_view.webix_control .webix_disabled_box .webix_inp_counter_prev,.webix_view.webix_control .webix_disabled_box .webix_segment_0,.webix_view.webix_control .webix_disabled_box .webix_segment_1,.webix_view.webix_control .webix_disabled_box .webix_segment_N{color:#aaa!important;background:#e9e9e9;background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#fbfbfb),color-stop(100%,#e9e9e9));background-image:-webkit-linear-gradient(#fbfbfb,#e9e9e9);background-image:-moz-linear-gradient(#fbfbfb,#e9e9e9);background-image:-ms-linear-gradient(top,#fbfbfb,#e9e9e9);background-image:-o-linear-gradient(top,#fbfbfb,#e9e9e9);border-color:#d5d5d5!important}
.webix_disabled_top_label{color:#aaa} .webix_disabled_top_label{color:#aaa}
label.webix_required:after{padding-left:4px;content:"*";color:red} label.webix_required:after,legend.webix_required:after{padding-left:4px;content:"*";color:red}
.webix_multicombo .webix_inp_static{padding:0 3px} .webix_multicombo .webix_inp_static{padding:0 3px}
.webix_el_text .webix_multicombo_input{background-color:transparent;border:0;float:left;vertical-align:middle;padding:0;color:#1e2022;font-size:13px;font-family:Helvetica,Verdana;margin-left:7px} .webix_el_text .webix_multicombo_input{background-color:transparent;border:0;float:left;vertical-align:middle;padding:0;color:#1e2022;font-size:13px;font-family:Helvetica,Verdana;margin-left:7px}
.webix_el_text .webix_multicombo_input:focus{border:0;box-shadow:none} .webix_el_text .webix_multicombo_input:focus{border:0;box-shadow:none}
.webix_multicombo_listbox{width:auto;line-height:1;margin:0;padding:0;list-style:none} .webix_multicombo_listbox{width:auto;line-height:1;margin:0;padding:0;list-style:none}
.webix_multicombo_tag,.webix_multicombo_value{padding:0 5px 0 7px;background-color:#f2f2f2;display:inline-block;margin:3px;float:left;border-radius:6px} .webix_multicombo_tag,.webix_multicombo_value{padding:0 5px 0 7px;background-color:#f2f2f2;display:inline-block;margin:3px;float:left;border-radius:6px}
.webix_multicombo_tag{min-width:15px;text-align:center;padding:0 5px} .webix_multicombo_tag{min-width:15px;text-align:center;padding:0 5px 0 5px}
.webix_multicombo_delete{margin-left:10px;padding:0 5px;color:#000;cursor:pointer} .webix_multicombo_delete{margin-left:10px;padding:0 5px;color:#000;cursor:pointer}
.webix_multilist .webix_selected{background:#f2f2f2;color:#1e2022} .webix_multilist .webix_selected{background:#f2f2f2;color:#1e2022}
.webix_checksuggest_select_all{border:none;border-bottom:1px solid #ebebeb;border-color:#ebebeb;margin-bottom:1px} .webix_checksuggest_select_all{border:none;border-bottom:1px solid #ebebeb;border-color:#ebebeb;margin-bottom:1px}
@ -485,13 +488,13 @@ label.webix_required:after{padding-left:4px;content:"*";color:red}
.webix_tab_filler{display:table-cell;background:0 0;font-size:1px;border-bottom:1px solid #a4bed4;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box} .webix_tab_filler{display:table-cell;background:0 0;font-size:1px;border-bottom:1px solid #a4bed4;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box}
.webix_tab_filler:first-child{border-right:1px solid #a4bed4} .webix_tab_filler:first-child{border-right:1px solid #a4bed4}
.webix_item_tab:first-child{border-left:1px solid #a4bed4} .webix_item_tab:first-child{border-left:1px solid #a4bed4}
.webix_item_tab{display:table-cell;text-align:center;vertical-align:middle;overflow:hidden;cursor:pointer;border:1px solid #a4bed4;border-left-width:0;color:#1e2022!important;box-shadow:0 1px 1px #fff inset;background:0 1px repeat-x #e5f1ff;background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#eaf3ff),color-stop(50%,#e5f1ff),color-stop(100%,#d6e8ff)) 0 1px repeat-x;background-image:-webkit-linear-gradient(top,#eaf3ff 0,#e5f1ff 50%,#d6e8ff 100%);background-image:-moz-linear-gradient(top,#eaf3ff 0,#e5f1ff 60%,#d6e8ff 100%);background-image:-ms-linear-gradient(top,#eaf3ff 0,#e5f1ff 60%,#d6e8ff 100%);background-image:-o-linear-gradient(top,#eaf3ff 0,#e5f1ff 60%,#d6e8ff 100%);-webkit-border-radius:0;-moz-border-radius-topleft:0;-moz-border-radius-bottomleft:0;-moz-border-radius-topright:0;-moz-border-radius-bottomright:0;border-radius:0;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box} .webix_item_tab{display:table-cell;text-align:center;vertical-align:middle;overflow:hidden;cursor:pointer;border:1px solid #a4bed4;border-left-width:0;color:#1e2022!important;box-shadow:0 1px 1px #fff inset;background:#e5f1ff;background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#eaf3ff),color-stop(50%,#e5f1ff),color-stop(100%,#d6e8ff));background-image:-webkit-linear-gradient(top,#eaf3ff 0,#e5f1ff 50%,#d6e8ff 100%);background-image:-moz-linear-gradient(top,#eaf3ff 0,#e5f1ff 60%,#d6e8ff 100%);background-image:-ms-linear-gradient(top,#eaf3ff 0,#e5f1ff 60%,#d6e8ff 100%);background-image:-o-linear-gradient(top,#eaf3ff 0,#e5f1ff 60%,#d6e8ff 100%);background-position:0 1px;background-repeat:repeat-x;-webkit-border-top-left-radius:0;-webkit-border-bottom-left-radius:0;-webkit-border-top-right-radius:0;-webkit-border-bottom-right-radius:0;-moz-border-radius-topleft:0;-moz-border-radius-bottomleft:0;-moz-border-radius-topright:0;-moz-border-radius-bottomright:0;border-top-left-radius:0;border-bottom-left-radius:0;border-top-right-radius:0;border-bottom-right-radius:0;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box}
.webixtype_bottom .webix_item_tab{border-radius:0} .webixtype_bottom .webix_item_tab{border-radius:0}
.webix_item_tab.webix_selected{border-bottom-width:0;background:#fff;background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#e5f1ff),color-stop(100%,#fff));background:-webkit-linear-gradient(#e5f1ff,#fff);background:-moz-linear-gradient(#e5f1ff,#fff);background:-ms-linear-gradient(top,#e5f1ff,#fff);background:-o-linear-gradient(top,#e5f1ff,#fff)} .webix_item_tab.webix_selected{border-bottom-width:0;background:#fff;background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#e5f1ff),color-stop(100%,#fff));background-image:-webkit-linear-gradient(#e5f1ff,#fff);background-image:-moz-linear-gradient(#e5f1ff,#fff);background-image:-ms-linear-gradient(top,#e5f1ff,#fff);background-image:-o-linear-gradient(top,#e5f1ff,#fff)}
.webixtype_bottom .webix_item_tab.webix_selected{border-bottom-width:1px;color:#1e2022;background:#ffdb8f;background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#ffebc2),color-stop(100%,#ffdb8f));background:-webkit-linear-gradient(#ffebc2,#ffdb8f);background:-moz-linear-gradient(#ffebc2,#ffdb8f);background:-ms-linear-gradient(top,#ffebc2,#ffdb8f);background:-o-linear-gradient(top,#ffebc2,#ffdb8f)} .webixtype_bottom .webix_item_tab.webix_selected{border-bottom-width:1px;background-size:100% 100%;background-position:0 0;color:#1e2022;background:#ffdb8f;background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#ffebc2),color-stop(100%,#ffdb8f));background-image:-webkit-linear-gradient(#ffebc2,#ffdb8f);background-image:-moz-linear-gradient(#ffebc2,#ffdb8f);background-image:-ms-linear-gradient(top,#ffebc2,#ffdb8f);background-image:-o-linear-gradient(top,#ffebc2,#ffdb8f)}
.webix_before_all_tabs{width:100%} .webix_before_all_tabs{width:100%}
.webix_after_all_tabs{width:100%;border-left:1px solid #a4bed4;border-right:1px solid #a4bed4;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box} .webix_after_all_tabs{width:100%;border-left:1px solid #a4bed4;border-right:1px solid #a4bed4;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box}
.webix_view.webix_control .webix_disabled_box .webix_item_tab{color:#aaa!important;background:#e9e9e9;background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#fbfbfb),color-stop(100%,#e9e9e9));background:-webkit-linear-gradient(#fbfbfb,#e9e9e9);background:-moz-linear-gradient(#fbfbfb,#e9e9e9);background:-ms-linear-gradient(top,#fbfbfb,#e9e9e9);background:-o-linear-gradient(top,#fbfbfb,#e9e9e9);border-color:#d5d5d5!important} .webix_view.webix_control .webix_disabled_box .webix_item_tab{color:#aaa!important;background:#e9e9e9;background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#fbfbfb),color-stop(100%,#e9e9e9));background-image:-webkit-linear-gradient(#fbfbfb,#e9e9e9);background-image:-moz-linear-gradient(#fbfbfb,#e9e9e9);background-image:-ms-linear-gradient(top,#fbfbfb,#e9e9e9);background-image:-o-linear-gradient(top,#fbfbfb,#e9e9e9);border-color:#d5d5d5!important}
.webix_el_tabbar .webixtype_icon .webix_img_btn{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box;margin:0 auto;text-align:center} .webix_el_tabbar .webixtype_icon .webix_img_btn{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box;margin:0 auto;text-align:center}
.webix_el_tabbar .webixtype_icon .webix_img_btn .webix_icon_btn{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box;margin:0 3px} .webix_el_tabbar .webixtype_icon .webix_img_btn .webix_icon_btn{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box;margin:0 3px}
.webix_el_tabbar .webixtype_icon .webix_item_tab,.webix_el_tabbar .webixtype_iconTop .webix_item_tab,.webix_el_tabbar .webixtype_image .webix_item_tab{padding:2px 0;border-radius:0} .webix_el_tabbar .webixtype_icon .webix_item_tab,.webix_el_tabbar .webixtype_iconTop .webix_item_tab,.webix_el_tabbar .webixtype_image .webix_item_tab{padding:2px 0;border-radius:0}
@ -505,17 +508,17 @@ label.webix_required:after{padding-left:4px;content:"*";color:red}
.webix_menu .webix_list_item .webix_submenu_icon,.webix_menu-x .webix_list_item .webix_submenu_icon{float:right;text-align:right;position:relative;margin-left:4px} .webix_menu .webix_list_item .webix_submenu_icon,.webix_menu-x .webix_list_item .webix_submenu_icon{float:right;text-align:right;position:relative;margin-left:4px}
.webix_menu-x .webix_list_item:first-child{margin-left:0} .webix_menu-x .webix_list_item:first-child{margin-left:0}
.webix_menu-x .webix_submenu_icon{top:13px} .webix_menu-x .webix_submenu_icon{top:13px}
.webix_menu-x{color:#1e2022!important;box-shadow:0 1px 1px #fff inset;background:0 1px repeat-x #e5f1ff;background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#eaf3ff),color-stop(50%,#e5f1ff),color-stop(100%,#d6e8ff)) 0 1px repeat-x;background-image:-webkit-linear-gradient(top,#eaf3ff 0,#e5f1ff 50%,#d6e8ff 100%);background-image:-moz-linear-gradient(top,#eaf3ff 0,#e5f1ff 60%,#d6e8ff 100%);background-image:-ms-linear-gradient(top,#eaf3ff 0,#e5f1ff 60%,#d6e8ff 100%);background-image:-o-linear-gradient(top,#eaf3ff 0,#e5f1ff 60%,#d6e8ff 100%)} .webix_menu-x{background-color:#fff;color:#1e2022!important;box-shadow:0 1px 1px #fff inset;background:#e5f1ff;background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#eaf3ff),color-stop(50%,#e5f1ff),color-stop(100%,#d6e8ff));background-image:-webkit-linear-gradient(top,#eaf3ff 0,#e5f1ff 50%,#d6e8ff 100%);background-image:-moz-linear-gradient(top,#eaf3ff 0,#e5f1ff 60%,#d6e8ff 100%);background-image:-ms-linear-gradient(top,#eaf3ff 0,#e5f1ff 60%,#d6e8ff 100%);background-image:-o-linear-gradient(top,#eaf3ff 0,#e5f1ff 60%,#d6e8ff 100%);background-position:0 1px;background-repeat:repeat-x}
.webix_menu-x .webix_list_item{display:inline-block;line-height:30px;-moz-background-size:1px 100%,100% 100%,1px 100%;background-size:1px 100%,100% 100%,1px 100%;background-color:transparent;margin:1px 0;background-position:0 0,1px 0,100% 0;background-repeat:no-repeat;background-image:-webkit-gradient(linear,left top,left bottom,#f0f6ff,#fff,#f0f6ff),-webkit-gradient(linear,left top,left bottom,transparent,transparent),-webkit-gradient(linear,left top,left bottom,#f0f6ff,#a4bed4,#f0f6ff);background-image:-webkit-linear-gradient(#f0f6ff,#fff,#f0f6ff),-webkit-linear-gradient(transparent,transparent),-webkit-linear-gradient(#f0f6ff,#a4bed4,#f0f6ff);background-image:-moz-linear-gradient(#f0f6ff,#fff,#f0f6ff),-moz-linear-gradient(transparent,transparent),-moz-linear-gradient(#f0f6ff,#a4bed4,#f0f6ff);background-image:-ms-linear-gradient(top,#f0f6ff,#fff,#f0f6ff),-ms-linear-gradient(transparent,transparent),-ms-linear-gradient(top,#f0f6ff,#a4bed4,#f0f6ff);background-image:-o-linear-gradient(top,#f0f6ff,#fff,#f0f6ff),-o-linear-gradient(transparent,transparent),-o-linear-gradient(top,#f0f6ff,#a4bed4,#f0f6ff)} .webix_menu-x .webix_list_item{display:inline-block;line-height:30px;-moz-background-size:1px 100%,100% 100%,1px 100%;background-size:1px 100%,100% 100%,1px 100%;background-color:transparent;margin:1px 0;background-position:0 0,1px 0,100% 0;background-repeat:no-repeat;background-image:-webkit-gradient(linear,left top,left bottom,#f0f6ff,#fff,#f0f6ff),-webkit-gradient(linear,left top,left bottom,transparent,transparent),-webkit-gradient(linear,left top,left bottom,#f0f6ff,#a4bed4,#f0f6ff);background-image:-webkit-linear-gradient(#f0f6ff,#fff,#f0f6ff),-webkit-linear-gradient(transparent,transparent),-webkit-linear-gradient(#f0f6ff,#a4bed4,#f0f6ff);background-image:-moz-linear-gradient(#f0f6ff,#fff,#f0f6ff),-moz-linear-gradient(transparent,transparent),-moz-linear-gradient(#f0f6ff,#a4bed4,#f0f6ff);background-image:-ms-linear-gradient(top,#f0f6ff,#fff,#f0f6ff),-ms-linear-gradient(transparent,transparent),-ms-linear-gradient(top,#f0f6ff,#a4bed4,#f0f6ff);background-image:-o-linear-gradient(top,#f0f6ff,#fff,#f0f6ff),-o-linear-gradient(transparent,transparent),-o-linear-gradient(top,#f0f6ff,#a4bed4,#f0f6ff)}
.webix_menu-x .webix_list_item:last-child{background-size:1px 100%,100% 100%,1px 100%;background-repeat:no-repeat;background-image:-webkit-gradient(linear,left top,left bottom,#f0f6ff,#fff,#f0f6ff),-webkit-gradient(linear,left top,left bottom,transparent,transparent),-webkit-gradient(linear,left top,left bottom,transparent,transparent);background-image:-webkit-linear-gradient(#f0f6ff,#fff,#f0f6ff),-webkit-linear-gradient(transparent,transparent),-webkit-linear-gradient(transparent,transparent);background-image:-moz-linear-gradient(#f0f6ff,#fff,#f0f6ff),-moz-linear-gradient(transparent,transparent),-moz-linear-gradient(transparent,transparent);background-image:-ms-linear-gradient(top,#f0f6ff,#fff,#f0f6ff),-ms-linear-gradient(transparent,transparent),-ms-linear-gradient(transparent,transparent);background-image:-o-linear-gradient(top,#f0f6ff,#fff,#f0f6ff),-o-linear-gradient(transparent,transparent),-o-linear-gradient(transparent,transparent);border-right:0} .webix_menu-x .webix_list_item:last-child{background-size:1px 100%,100% 100%,1px 100%;background-repeat:no-repeat;background-image:-webkit-gradient(linear,left top,left bottom,#f0f6ff,#fff,#f0f6ff),-webkit-gradient(linear,left top,left bottom,transparent,transparent),-webkit-gradient(linear,left top,left bottom,transparent,transparent);background-image:-webkit-linear-gradient(#f0f6ff,#fff,#f0f6ff),-webkit-linear-gradient(transparent,transparent),-webkit-linear-gradient(transparent,transparent);background-image:-moz-linear-gradient(#f0f6ff,#fff,#f0f6ff),-moz-linear-gradient(transparent,transparent),-moz-linear-gradient(transparent,transparent);background-image:-ms-linear-gradient(top,#f0f6ff,#fff,#f0f6ff),-ms-linear-gradient(transparent,transparent),-ms-linear-gradient(transparent,transparent);background-image:-o-linear-gradient(top,#f0f6ff,#fff,#f0f6ff),-o-linear-gradient(transparent,transparent),-o-linear-gradient(transparent,transparent);border-right:0}
.webix_menu{background:#fff;border:1px solid #a4bed4;border-radius:0} .webix_menu{background:#fff;border:1px solid #a4bed4;border-radius:0}
.webix_menu .webix_list_item{display:block} .webix_menu .webix_list_item{display:block}
.webix_menu .webix_context_separator{height:3px;padding:1px 3px 0} .webix_menu .webix_context_separator{height:3px;padding:1px 3px 0 3px}
.webix_menu .webix_context_separator .sep_line{height:1px;border-top:1px solid #a4bed4} .webix_menu .webix_context_separator .sep_line{height:1px;border-top:1px solid #a4bed4}
.webix_menu .webix_submenu_icon{top:11px} .webix_menu .webix_submenu_icon{top:11px}
.webix_menu .webix_context_spacer{height:28px} .webix_menu .webix_context_spacer{height:28px}
.webix_menu_link{color:inherit;text-decoration:none;display:block;height:100%} .webix_menu_link{color:inherit;text-decoration:none;display:block;height:100%}
.webix_layout_toolbar,.webix_toolbar.webix_layout_subbar{color:#1e2022!important;box-shadow:0 1px 1px #fff inset;background:0 1px repeat-x #e5f1ff;background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#eaf3ff),color-stop(50%,#e5f1ff),color-stop(100%,#d6e8ff)) 0 1px repeat-x;background-image:-webkit-linear-gradient(top,#eaf3ff 0,#e5f1ff 50%,#d6e8ff 100%);background-image:-moz-linear-gradient(top,#eaf3ff 0,#e5f1ff 60%,#d6e8ff 100%);background-image:-ms-linear-gradient(top,#eaf3ff 0,#e5f1ff 60%,#d6e8ff 100%);background-image:-o-linear-gradient(top,#eaf3ff 0,#e5f1ff 60%,#d6e8ff 100%)} .webix_layout_toolbar,.webix_toolbar.webix_layout_subbar{color:#1e2022!important;box-shadow:0 1px 1px #fff inset;background:#e5f1ff;background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#eaf3ff),color-stop(50%,#e5f1ff),color-stop(100%,#d6e8ff));background-image:-webkit-linear-gradient(top,#eaf3ff 0,#e5f1ff 50%,#d6e8ff 100%);background-image:-moz-linear-gradient(top,#eaf3ff 0,#e5f1ff 60%,#d6e8ff 100%);background-image:-ms-linear-gradient(top,#eaf3ff 0,#e5f1ff 60%,#d6e8ff 100%);background-image:-o-linear-gradient(top,#eaf3ff 0,#e5f1ff 60%,#d6e8ff 100%);background-position:0 1px;background-repeat:repeat-x}
.webix_toolbar .webix_layout_clean,.webix_toolbar .webix_layout_line,.webix_toolbar .webix_layout_toolbar{background:0 0} .webix_toolbar .webix_layout_clean,.webix_toolbar .webix_layout_line,.webix_toolbar .webix_layout_toolbar{background:0 0}
.webix_el_button .webix_hidden_upload,.webix_hidden_upload{height:1px;width:1px;border:0!important;font-size:1px;position:absolute;top:0;left:0;z-index:-1} .webix_el_button .webix_hidden_upload,.webix_hidden_upload{height:1px;width:1px;border:0!important;font-size:1px;position:absolute;top:0;left:0;z-index:-1}
.webix_upload_client,.webix_upload_error,.webix_upload_server{width:60px;text-align:center;float:right} .webix_upload_client,.webix_upload_error,.webix_upload_server{width:60px;text-align:center;float:right}
@ -530,29 +533,42 @@ label.webix_required:after{padding-left:4px;content:"*";color:red}
.webix_fieldset fieldset{border:1px solid #81a5c4;margin:0;padding:5px 8px} .webix_fieldset fieldset{border:1px solid #81a5c4;margin:0;padding:5px 8px}
.webix_fieldset legend{color:#1e2022;font-size:12px} .webix_fieldset legend{color:#1e2022;font-size:12px}
.webix_forminput fieldset{border:none;margin:0;padding:0} .webix_forminput fieldset{border:none;margin:0;padding:0}
.webix_forminput legend{color:#1e2022;line-height:30px;padding:2px 7.5px 2px 2px;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box;font-family:Helvetica,Verdana;float:left} .webix_forminput legend{color:#1e2022;line-height:30px;padding:2px 5px;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box;font-family:Helvetica,Verdana;float:left;padding-right:7.5px}
.webix_slider_box{position:relative;padding:13px 10px 0;height:100%;float:left;border-radius:5px;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box;margin-top:-11px} .webix_slider_box{position:relative;padding:0 10px;height:100%;padding-top:13px;float:left;border-radius:5px;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box;margin-top:-11px}
.webix_slider_box .webix_slider_left{height:10px;float:left;width:100px;border:1px solid #b5cadc;-webkit-border-radius:5px 0 0 5px;-moz-border-radius-topleft:5px;-moz-border-radius-bottomleft:5px;-moz-border-radius-topright:0;-moz-border-radius-bottomright:0;border-radius:5px 0 0 5px;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box} .webix_slider_box .webix_slider_left{height:10px;float:left;width:100px;border:1px solid #b5cadc;-webkit-border-top-left-radius:5px;-webkit-border-bottom-left-radius:5px;-webkit-border-top-right-radius:0;-webkit-border-bottom-right-radius:0;-moz-border-radius-topleft:5px;-moz-border-radius-bottomleft:5px;-moz-border-radius-topright:0;-moz-border-radius-bottomright:0;border-top-left-radius:5px;border-bottom-left-radius:5px;border-top-right-radius:0;border-bottom-right-radius:0;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box}
.webix_slider_box .webix_slider_right{height:10px;float:left;width:100px;background:#fff;-webkit-border-radius:0 5px 5px 0;-moz-border-radius-topleft:0;-moz-border-radius-bottomleft:0;-moz-border-radius-topright:5px;-moz-border-radius-bottomright:5px;border-radius:0 5px 5px 0;border:1px solid #b5cadc;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box} .webix_slider_box .webix_slider_right{height:10px;float:left;width:100px;background:#fff;-webkit-border-top-left-radius:0;-webkit-border-bottom-left-radius:0;-webkit-border-top-right-radius:5px;-webkit-border-bottom-right-radius:5px;-moz-border-radius-topleft:0;-moz-border-radius-bottomleft:0;-moz-border-radius-topright:5px;-moz-border-radius-bottomright:5px;border-top-left-radius:0;border-bottom-left-radius:0;border-top-right-radius:5px;border-bottom-right-radius:5px;border:1px solid #b5cadc;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box}
.webix_slider_box .webix_slider_handle{z-index:1;position:absolute;width:14px;height:14px;border-radius:8px} .webix_slider_box .webix_slider_handle{z-index:1;position:absolute;width:14px;height:14px;border-radius:8px}
.webix_slider_box .webix_slider_left{background:#f5f9ff} .webix_slider_box .webix_slider_left{background:#f5f9ff}
.webix_slider_box .webix_slider_handle{border:1px solid #a4bed4;-moz-box-shadow:inset 0 1px 1px 1px #fff;-webkit-box-shadow:inset 0 1px 1px 1px #fff;box-shadow:inset 0 1px 1px #fff;background:#fafcff;background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#d6e8ff),color-stop(100%,#fafcff));background:-webkit-linear-gradient(#d6e8ff,#fafcff);background:-moz-linear-gradient(#d6e8ff,#fafcff);background:-ms-linear-gradient(top,#d6e8ff,#fafcff);background:-o-linear-gradient(top,#d6e8ff,#fafcff);top:10px} .webix_slider_box .webix_slider_handle{border:1px solid #a4bed4;-moz-box-shadow:inset 0 1px 1px 1px #fff;-webkit-box-shadow:inset 0 1px 1px 1px #fff;box-shadow:inset 0 1px 1px #fff;background:#fafcff;background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#d6e8ff),color-stop(100%,#fafcff));background-image:-webkit-linear-gradient(#d6e8ff,#fafcff);background-image:-moz-linear-gradient(#d6e8ff,#fafcff);background-image:-ms-linear-gradient(top,#d6e8ff,#fafcff);background-image:-o-linear-gradient(top,#d6e8ff,#fafcff);top:10px}
.webix_rangeslider .webix_slider_box .webix_slider_left{position:absolute} .webix_rangeslider .webix_slider_box .webix_slider_left{position:absolute}
.webix_rangeslider .webix_slider_box .webix_slider_active{z-index:1} .webix_rangeslider .webix_slider_box .webix_slider_active{z-index:1}
.webix_rangeslider .webix_slider_box .webix_slider_right{border-radius:5px} .webix_rangeslider .webix_slider_box .webix_slider_right{border-radius:5px}
.webix_rangeslider .webix_slider_title_box{position:relative;float:left;width:1px}
.webix_rangeslider .webix_slider_title.webix_slider_move{position:absolute}
.webix_rangeslider.webix_slider_vertical .webix_slider_box .webix_slider_right{-webkit-border-top-left-radius:5px;-webkit-border-bottom-left-radius:5px;-webkit-border-top-right-radius:5px;-webkit-border-bottom-right-radius:5px;-moz-border-radius-topleft:5px;-moz-border-radius-bottomleft:5px;-moz-border-radius-topright:5px;-moz-border-radius-bottomright:5px;border-top-left-radius:5px;border-bottom-left-radius:5px;border-top-right-radius:5px;border-bottom-right-radius:5px}
.webix_rangeslider.webix_slider_vertical .webix_slider_title.webix_slider_move{display:block;position:relative}
.webix_slider_alt .webix_slider_box .webix_slider_left,.webix_slider_alt .webix_slider_box .webix_slider_right{border:1px solid #e1e1e1} .webix_slider_alt .webix_slider_box .webix_slider_left,.webix_slider_alt .webix_slider_box .webix_slider_right{border:1px solid #e1e1e1}
.webix_slider_alt .webix_slider_box .webix_slider_handle{width:11px;margin:0 2px;height:14px;border-radius:2px;background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAICAYAAAAx8TU7AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoV2luZG93cykiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6OTJCMjdFNENBRTg4MTFFMjk2NjJGMTJENjkyNDA2NTkiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6OTJCMjdFNERBRTg4MTFFMjk2NjJGMTJENjkyNDA2NTkiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo5MkIyN0U0QUFFODgxMUUyOTY2MkYxMkQ2OTI0MDY1OSIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo5MkIyN0U0QkFFODgxMUUyOTY2MkYxMkQ2OTI0MDY1OSIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/Powz/icAAAAaSURBVHjaYmBgYDBjgAA4zcSABQysIECAAQBn+ACx2PqtbAAAAABJRU5ErkJggg==);background-repeat:no-repeat;background-position:3px center} .webix_slider_alt .webix_slider_box .webix_slider_handle{width:11px;margin:0 2px;height:14px;border-radius:2px;background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAICAYAAAAx8TU7AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoV2luZG93cykiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6OTJCMjdFNENBRTg4MTFFMjk2NjJGMTJENjkyNDA2NTkiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6OTJCMjdFNERBRTg4MTFFMjk2NjJGMTJENjkyNDA2NTkiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo5MkIyN0U0QUFFODgxMUUyOTY2MkYxMkQ2OTI0MDY1OSIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo5MkIyN0U0QkFFODgxMUUyOTY2MkYxMkQ2OTI0MDY1OSIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/Powz/icAAAAaSURBVHjaYmBgYDBjgAA4zcSABQysIECAAQBn+ACx2PqtbAAAAABJRU5ErkJggg==);background-repeat:no-repeat;background-position:3px center}
.webix_slider_alt .webix_slider_box .webix_slider_left{background:#f0f6ff} .webix_slider_alt .webix_slider_box .webix_slider_left{background:#f0f6ff}
.webix_slider_alt .webix_slider_box .webix_slider_right{background:#eee} .webix_slider_alt .webix_slider_box .webix_slider_right{background:#eee}
.webix_slider_alt .webix_slider_box .webix_slider_handle{background-color:#f5f9ff;top:10px} .webix_slider_alt .webix_slider_box .webix_slider_handle{background-color:#f5f9ff;top:10px}
.webix_slider_vertical .webix_slider_box .webix_slider_left,.webix_slider_vertical .webix_slider_box .webix_slider_right{width:10px;float:none} .webix_slider_vertical .webix_slider_box .webix_slider_left,.webix_slider_vertical .webix_slider_box .webix_slider_right{width:10px;float:none}
.webix_slider_vertical .webix_slider_box .webix_slider_left{-webkit-border-radius:0 0 5px 5px;-moz-border-radius-topleft:0;-moz-border-radius-bottomleft:5px;-moz-border-radius-topright:0;-moz-border-radius-bottomright:5px;border-radius:0 0 5px 5px} .webix_slider_vertical .webix_slider_box .webix_slider_left{-webkit-border-top-left-radius:0;-webkit-border-bottom-left-radius:5px;-webkit-border-top-right-radius:0;-webkit-border-bottom-right-radius:5px;-moz-border-radius-topleft:0;-moz-border-radius-bottomleft:5px;-moz-border-radius-topright:0;-moz-border-radius-bottomright:5px;border-top-left-radius:0;border-bottom-left-radius:5px;border-top-right-radius:0;border-bottom-right-radius:5px}
.webix_slider_vertical .webix_slider_box .webix_slider_right{-webkit-border-radius:5px 5px 0 0;-moz-border-radius-topleft:5px;-moz-border-radius-bottomleft:0;-moz-border-radius-topright:5px;-moz-border-radius-bottomright:0;border-radius:5px 5px 0 0} .webix_slider_vertical .webix_slider_box .webix_slider_right{-webkit-border-top-left-radius:5px;-webkit-border-bottom-left-radius:0;-webkit-border-top-right-radius:5px;-webkit-border-bottom-right-radius:0;-moz-border-radius-topleft:5px;-moz-border-radius-bottomleft:0;-moz-border-radius-topright:5px;-moz-border-radius-bottomright:0;border-top-left-radius:5px;border-bottom-left-radius:0;border-top-right-radius:5px;border-bottom-right-radius:0}
.webix_slider_vertical .webix_slider_box .webix_slider_handle{left:7px} .webix_slider_vertical .webix_slider_box .webix_slider_handle{left:7px}
.webix_slider_vertical .webix_slider_title{padding-top:1px;text-align:left} .webix_slider_vertical .webix_slider_title{padding-top:1px;text-align:left}
.webix_slider_vertical.webix_slider_alt .webix_slider_handle{height:11px;width:14px;left:5px;background-position:4px center} .webix_slider_vertical.webix_slider_alt .webix_slider_handle{height:11px;width:14px;left:5px;background-position:4px center}
.webix_slider_title{text-align:center} .webix_slider_title{text-align:center}
.webix_slider_title.webix_slider_move{position:relative;text-align:left;display:inline-block;width:auto} .webix_slider_title.webix_slider_move{position:relative;text-align:left;display:inline-block;width:auto;white-space:nowrap}
.webix_el_box .webix_switch_box{margin-top:4px}
.webix_switch_toggle{position:absolute;visibility:hidden}
.webix_switch_box{display:block;position:relative;cursor:pointer;height:24px;width:60px;background-color:#f1f1f1;border:1px solid #a4bed4;border-radius:60px;box-sizing:border-box;transition:background-color .4s ease;color:#666;text-align:center;float:left}
.webix_el_switch .webix_label_right{display:inline-block;padding-top:3px;margin-top:4px}
.webix_switch_handle{height:22px;width:22px;border-radius:100%;box-shadow:0 1px 5px rgba(0,0,0,.3);border:1px solid #eee;position:absolute;top:0;background-color:#fff;transition:left .3s ease;cursor:pointer;box-sizing:border-box}
.webix_switch_box:active .webix_switch_handle,.webix_switch_handle:focus{box-shadow:0 0 2px 2px rgba(0,0,0,.2)}
.webix_switch_text{-moz-user-select:none;-webkit-user-select:none;-ms-user-select:none;user-select:none;padding:1px 0 0 18px;display:inline-block;box-sizing:border-box;width:100%}
.webix_switch_on .webix_switch_text{padding:1px 18px 0 0}
.webix_switch_box.webix_switch_on{color:#fff}
.webix_progress_bottom,.webix_progress_top{width:100%;position:absolute;top:0;left:0;height:6px;overflow:hidden;z-index:20} .webix_progress_bottom,.webix_progress_top{width:100%;position:absolute;top:0;left:0;height:6px;overflow:hidden;z-index:20}
.webix_progress_bottom{bottom:0;top:auto;height:17px} .webix_progress_bottom{bottom:0;top:auto;height:17px}
.webix_progress_bottom .webix_progress_state,.webix_progress_top .webix_progress_state{width:0;height:6px;background:#ffdb8f;-moz-transition:width 3s;transition:width 3s} .webix_progress_bottom .webix_progress_state,.webix_progress_top .webix_progress_state{width:0;height:6px;background:#ffdb8f;-moz-transition:width 3s;transition:width 3s}
@ -562,7 +578,7 @@ label.webix_required:after{padding-left:4px;content:"*";color:red}
.webix_gage{height:100%;width:100%;transform:rotate(180deg)} .webix_gage{height:100%;width:100%;transform:rotate(180deg)}
.webix_gage_label{font-size:.9em;text-align:center;margin-top:10px} .webix_gage_label{font-size:.9em;text-align:center;margin-top:10px}
.webix_gage_placeholder{margin-top:-20px} .webix_gage_placeholder{margin-top:-20px}
.webix_gage_info{text-align:center;font-size:.7em;position:relative;margin:0 auto 10px} .webix_gage_info{text-align:center;font-size:.7em;position:relative;margin:0 auto 10px auto}
.webix_gage_min_range{left:-74px} .webix_gage_min_range{left:-74px}
.webix_gage_max_range{right:-74px} .webix_gage_max_range{right:-74px}
.webix_gage_max_range,.webix_gage_min_range{display:inline-block;margin-top:20px;position:absolute;text-align:center;width:150px} .webix_gage_max_range,.webix_gage_min_range{display:inline-block;margin-top:20px;position:absolute;text-align:center;width:150px}
@ -571,22 +587,190 @@ label.webix_required:after{padding-left:4px;content:"*";color:red}
.webix_gage_gradient_point_animated{transition:transform 1.3s linear} .webix_gage_gradient_point_animated{transition:transform 1.3s linear}
.webix_gage_animated{stroke:#0f0;animation:gage_dash 1.3s linear forwards;transition:stroke 1.3s linear,stroke-dasharray 1.3s linear} .webix_gage_animated{stroke:#0f0;animation:gage_dash 1.3s linear forwards;transition:stroke 1.3s linear,stroke-dasharray 1.3s linear}
.webix_gage_animated_first_load{stroke:#0f0;animation:gage_dash 1.3s linear forwards;transition:stroke 1.3s linear} .webix_gage_animated_first_load{stroke:#0f0;animation:gage_dash 1.3s linear forwards;transition:stroke 1.3s linear}
@keyframes gage_dash{to{stroke-dashoffset:0}} @keyframes gage_dash{to{stroke-dashoffset:0}
}
.webix_bullet_name{width:100} .webix_bullet_name{width:100}
.webix_bullet_header{font-weight:700;font-size:15px} .webix_bullet_header{font-weight:700;font-size:15px}
.webix_bullet_subheader{font-size:15px} .webix_bullet_subheader{font-size:15px}
.webix_bullet_scale{font-size:12px;font-weight:700} .webix_bullet_scale{font-size:12px;font-weight:700}
.webix_organogram canvas{position:absolute} .webix_organogram canvas{position:absolute}
.webix_organogram_item{position:absolute;z-index:1;text-align:center;border-radius:4px;background-color:#e3f2fd;border:1px solid #bbdefb;padding:7px 7px 10px;box-sizing:border-box} .webix_organogram_item{position:absolute;z-index:1;text-align:center;border-radius:4px;background-color:#e3f2fd;border:1px solid #bbdefb;padding:7px 7px 10px 7px;box-sizing:border-box}
.webix_organogram_item.webix_selected{background-color:#ffdb8f;border-color:#ffdb8f} .webix_organogram_item.webix_selected{background-color:#ffdb8f;border-color:#ffdb8f}
.webix_organogram_list{border-radius:4px;position:absolute;z-index:1;height:auto;border:1px solid #ddd;box-sizing:border-box} .webix_organogram_list{border-radius:4px;position:absolute;z-index:1;height:auto;border:1px solid #ddd;box-sizing:border-box}
.webix_organogram_list .webix_organogram_list_item{border:none;text-align:left;position:static;padding:5px;border-radius:0;box-sizing:border-box} .webix_organogram_list .webix_organogram_list_item{border:none;text-align:left;position:static;padding:5px;border-radius:0;box-sizing:border-box}
.webix_icon,.webix_icon_btn,.webix_input_icon{font-family:FontAwesome;font-style:normal;font-weight:400;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;transform:translate(0,0)}
.webix_icon,.webix_input_icon{font-size:17px;display:inline-block;width:20px}
.webix_input_icon{float:right;font-size:19px;color:#888}
.webix_icon_button{background-color:transparent;border:none;padding:0;cursor:pointer;position:relative}
.webix_icon_button .webix_icon{font-size:20px;height:23px;width:21px;display:inline-block;text-align:center;color:#606060}
.webix_icon_btn{font-size:17px;display:inline-block;text-align:center;width:24px;color:#606060}
.webix_el_iconBbutton .webix_icon_btn{text-align:center;width:100%;padding:5px 0}
.webix_el_icon{cursor:pointer}
.webix_badge{background-color:#ff8839;color:#fff;border-radius:50%;font-size:12px;height:22px;min-width:22px;box-sizing:border-box;padding:0 4px;text-align:center;line-height:21px;position:absolute;right:0;top:0}
.webix_list_item .webix_badge{position:static;float:right;margin:4px -5px 0 10px}
.webixtype_base .webix_badge{top:50%;margin-top:-11px;margin-right:8px}
.webix_treemap{background-color:#f5f5f5}
.webix_treemap .webix_scroll_cont{position:relative;height:100%;width:100%}
.webix_treemap_item{position:absolute;color:#444;overflow:hidden;text-align:left;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box;padding:3px 5px;border-width:0 1px 1px 0;border-style:solid;border-color:rgba(0,0,0,.2);background:0 0;cursor:pointer}
.webix_treemap_level_top{z-index:1;border-color:rgba(0,0,0,.4)}
.webix_treemap_item_bottom{border-bottom-width:0}
.webix_treemap_item_right{border-right-width:0}
.webix_treemap_item:hover{box-shadow:inset 0 0 5px #666}
.webix_treemap_item.webix_selected{border-color:#ffdb8f;box-shadow:inset 0 0 1px 2px #ffdb8f}
.webix_treemap_header{width:100%;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box;padding:0 10px;border-bottom:1px solid #a4bed4}
.webix_treemap_header_item{cursor:pointer}
.webix_treemap_header_item:last-child{cursor:default}
.webix_treemap_reset{float:right;width:25px;text-align:center}
.webix_treemap_reset:before{content:"\f00d";color:#1e2022;font-family:FontAwesome;font-size:16px;display:block;cursor:pointer}
.webix_treemap_path_icon{width:20px;text-align:center}
.webix_barcode{position:relative}
.webix_barcode .webix_canvas_text{font-size:13px;padding:0 2px;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box}
.portlet_drag{position:absolute;z-index:1;top:5px;right:5px;width:18px;opacity:.5;cursor:pointer}
.portlet_in_drag{opacity:.4}
.portlet_marker,.portlet_markerbottom,.portlet_markerleft,.portlet_markerright,.portlet_markertop{opacity:.5;background:#9169BE;position:absolute;z-index:2;transition:top .5s,left .5s,width .5s,height .5s;top:0;left:0;width:100%;height:100%}
.portlet_markertop{height:50%}
.portlet_markerbottom{height:50%;top:50%}
.portlet_markerleft{width:50%}
.portlet_markerright{width:50%;left:50%}
.panel_icon{position:absolute;z-index:100;top:5px;right:5px;line-height:normal;font-size:13px;width:18px;opacity:.5;cursor:pointer}
.panel_target{position:absolute;background:rgba(120,120,120,.84);box-shadow:0 0 10px 5px #666;z-index:3}
.webix_popup.webix_sidemenu.webix_sidemenu_left{border-width:0 1px 0 0;box-shadow:2px 0 2px rgba(0,0,0,.05)}
.webix_sidemenu_left.webix_animate{transition:left .6s}
.webix_popup.webix_sidemenu.webix_sidemenu_right{left:auto;border-width:0 0 0 1px;box-shadow:-2px 0 2px rgba(0,0,0,.05)}
.webix_sidemenu_right.webix_animate{transition:right .6s}
.webix_popup.webix_sidemenu.webix_sidemenu_top{border-width:0 0 1px 0;box-shadow:0 2px 2px rgba(0,0,0,.05)}
.webix_sidemenu_top .webix_win_body{position:relative}
.webix_sidemenu_top .webix_win_body>.webix_view{position:absolute;top:auto;left:0;bottom:0}
.webix_sidemenu_top.webix_animate,.webix_sidemenu_top.webix_animate .webix_win_body{transition:height .6s}
.webix_popup.webix_sidemenu.webix_sidemenu_bottom{border-width:1px 0 0 0;top:auto;box-shadow:0 -2px 2px rgba(0,0,0,.05)}
.webix_sidemenu_bottom.webix_animate,.webix_sidemenu_bottom.webix_animate .webix_win_body{transition:height .6s}
.webix_sidebar{background:#ECEFF1}
.webix_sidebar .webix_tree_item{color:#454545;height:35px;line-height:35px}
.webix_sidebar .webix_scroll_cont>.webix_tree_leaves{padding:0}
.webix_sidebar .webix_tree_leaves .webix_tree_leaves{margin-left:0}
.webix_sidebar_expanded .webix_tree_item:hover,.webix_sidebar_selected{background-color:rgba(0,0,0,.02)}
.webix_sidebar .webix_tree_item.webix_selected,.webix_sidebar .webix_tree_item.webix_selected span{background-color:#27ae60;padding-right:0}
.webix_sidebar .webix_tree_branch_1 .webix_tree_item{padding-left:40px}
.webix_sidebar .webix_tree_branch_1>.webix_tree_item{height:40px;line-height:40px;padding-left:0}
.webix_sidebar .webix_tree_branch_1{border-bottom:1px solid #e5e5e5}
.webix_sidebar .webix_tree_item span,.webix_sidebar .webix_tree_item.webix_selected span{margin:0;padding:0}
.webix_sidebar_icon{width:40px;text-align:center}
.webix_sidebar_dir_icon{float:right;line-height:inherit}
.webix_sidebar_popup{border:none!important;box-shadow:2px 3px 3px #ddd}
.webix_sidebar_popup,.webix_sidebar_popup .webix_list_item{border-radius:0}
.webix_sidebar_popup_right{box-shadow:-1px 3px 3px #ddd}
.webix_sidebar_popup_list.webix_sidebar_popup_left .webix_icon{float:right;line-height:inherit;width:7px}
.webix_sidebar_popup_title{background:#ECEFF1}
.webix_sidebar_popup_title.webix_selected{border-left-color:#27ae60;background:#27ae60}
.webix_sidebar_popup_title .webix_template{line-height:40px;padding:0 10px;border:1px solid #E5E5E5;border-left:none}
.webix_sidebar_selected.webix_sidebar_popup_title .webix_template{background:rgba(0,0,0,.03);border-left:none}
.webix_sidebar_popup_list .webix_list_item{border-left:1px solid #E5E5E5;border-right:1px solid #E5E5E5}
.webix_sidebar_popup_list .webix_list_item:first-child{border-top:1px solid #E5E5E5}
.webix_sidebar_popup_list .webix_list_item:hover{background:#f6f9fb}
.webix_sidebar_popup_list .webix_list_item.webix_selected:hover{background:#27ae60}
.webix_menu .webix_list_item.webix_sidebar_selected{background:rgba(0,0,0,.02)}
.webix_menu .webix_list_item.webix_sidebar_selected:hover{background:rgba(0,0,0,.02)!important}
.webix_view.webix_pdf{background-color:#404040;overflow:auto;-webkit-overflow-scrolling:touch}
.webix_pdf .canvas_wrapper{margin:0 auto;box-shadow:5px 5px 15px #1c1c1c;width:100%;height:100%}
.webix_toolbar.pdf_bar{background-color:#474747}
.webix_toolbar.pdf_bar .webix_icon_btn{font-size:15px}
.webix_toolbar.pdf_bar .webix_img_btn:focus,.webix_toolbar.pdf_bar .webix_img_btn:hover{background-color:#404040}
.webix_toolbar.pdf_bar .webix_template{background-color:#3498db;color:#fff;line-height:2em}
.webix_toolbar.pdf_bar .webix_el_box input,.webix_toolbar.pdf_bar .webix_inp_static{background-color:#5c5c5c;color:#fff;border-color:#404040}
.webix_toolbar.pdf_bar .webix_el_box input:focus,.webix_toolbar.pdf_bar .webix_inp_static:focus{border-color:#333}
.webix_toolbar.pdf_bar .webix_el_box .webix_input_icon{color:#fff}
.webix_view.webix_popup.pdf_opt_list{box-shadow:5px 5px 15px #1c1c1c;border:none}
.pdf_opt_list .webix_list{background-color:#404040;color:#fff}
.pdf_opt_list .webix_list .webix_list_item{border-color:#474747;line-height:1.5em}
.pdf_opt_list .webix_list .webix_list_item.webix_selected,.pdf_opt_list .webix_list .webix_list_item:hover{color:#404040;background-color:#fff}
.webix_dbllist .webix_list{background:#ededed}
.webix_dbllist .webix_list_item{background:#fff;margin:3px 4px 0 4px;box-sizing:border-box}
.webix_dbllist .webix_list_item.webix_selected{color:#FFF;background:#27ae60}
.webix_dbllist button{width:45%;height:30px;margin-top:5px;background:#fff;border-radius:6px;border:1px solid #a4bed4;cursor:pointer;font-size:13px;font-family:Helvetica,Verdana}
.webix_dbllist button:active{box-shadow:inset 1px 1px 0 silver}
.webix_dbllist button .webix_icon{color:#888;font-size:26px}
.webix_dbllist button:first-child{margin-top:32px;margin-right:5%}
.webix_dbllist button:last-child{margin-left:5%}
.webix_dbllist .bottom_label{font-size:10px;text-transform:uppercase;background:#ededed;padding-left:13px}
.webix_invalid .webix_list{background:#f8e2e2}
.webix_toolbar .webix_el_label,.webix_toolbar .webix_inp_label{color:#1e2022}
.webix_ss_sort_asc{position:absolute;float:right;right:3px;top:8px;width:7px;height:13px;background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAANCAYAAABlyXS1AAAARUlEQVR4nGNgQAKGxib/GbABkIS7b8B/DAUwCRiGK0CXwFBAb1DfP/U/LszwHwi2X7qFgUEArBtdAVwCBmAKMCSQFSDzAWXXaOHsXeqkAAAAAElFTkSuQmCC)}
.webix_ss_sort_desc{position:absolute;right:3px;top:8px;width:7px;height:13px;background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAANCAYAAABlyXS1AAAARUlEQVR42mNgQAL1/VP/M2ADIIntF2/9x1AAlrh0C47hCmA60DFYwX88gIFGwNDY5D8uDFbg7hvwHx2jmIBTAlkB0e4BAEjlaNtBWJPnAAAAAElFTkSuQmCC)}
.webix_group_back .webix_arrow_icon{color:#1e2022}
.webix_menu-x .webix_list_item:active,.webix_menu-x .webix_list_item:focus,.webix_menu-x .webix_list_item:hover{background-color:rgba(255,255,255,.4)!important}
.webix_menu .webix_list_item:active,.webix_menu .webix_list_item:hover{background-color:rgba(0,0,0,.05)!important}
.webix_list_item .webix_submenu_icon{width:0;height:0;border-width:4px;border-style:solid}
.webix_menu-x .webix_list_item .webix_submenu_icon{border-color:#375975 transparent transparent transparent}
.webix_menu .webix_list_item .webix_submenu_icon{border-color:transparent transparent transparent #375975}
.webix_popup_title{color:#1e2022!important;background:#e3e3e3!important;font-size:15px!important;font-weight:700;border-bottom:1px solid #dedede!important;text-shadow:none!important}
.webix_modal_box{background:#ebebeb!important;border:1px solid #a4bed4!important}
.confirmButton{color:#1e2022!important;box-shadow:0 1px 1px #fff inset;background:#e5f1ff;background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#eaf3ff),color-stop(50%,#e5f1ff),color-stop(100%,#d6e8ff));background-image:-webkit-linear-gradient(top,#eaf3ff 0,#e5f1ff 50%,#d6e8ff 100%);background-image:-moz-linear-gradient(top,#eaf3ff 0,#e5f1ff 60%,#d6e8ff 100%);background-image:-ms-linear-gradient(top,#eaf3ff 0,#e5f1ff 60%,#d6e8ff 100%);background-image:-o-linear-gradient(top,#eaf3ff 0,#e5f1ff 60%,#d6e8ff 100%);background-position:0 1px;background-repeat:repeat-x}
.confirmButtonActive{background:#fff}
.webix_item_tab{font-size:13px}
.buttonStyle{font-size:13px;font-family:Helvetica,Verdana}
.activeShadow{box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}
.webix_img_btn_abs:active+input,.webix_inp_counter_next:active,.webix_inp_counter_prev:active,.webixtype_base:active{background:#f5f5f5;box-shadow:inset 0 3px 5px rgba(0,0,0,.125);background-repeat:none}
.webix_icon_button:active,.webix_img_btn:active,.webix_img_btn_top:active{box-shadow:inset 0 3px 5px rgba(0,0,0,.125);background-color:rgba(0,0,0,.05)}
.webix_pressed .webix_img_btn_abs+input,.webix_pressed .webixtype_base{box-shadow:inset 0 3px 5px rgba(0,0,0,.125);background:#f5f5f5!important}
.webix_slider_vertical .webix_slider_box{margin-top:-8px}
.webix_rangeslider .webix_slider_title_box{height:17px}
.webix_switch_box.webix_switch_on{background-color:#a4bed4}
.webix_switch_text{line-height:21px}
.webix_accordionitem_label .webix_icon{font-size:19px;width:25px}
.webix_accordionitem.vertical>.webix_accordionitem_header .webix_accordionitem_button{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAsAAAAHCAYAAADebrddAAAARUlEQVR42o2PAQYAMAwD9+iEFvTpnZYZU7EQRa4kaxKAXD9y9yTZluCBALRJpATNrIG6rAdjjmBVeCuB6EyAYkNEiDE33yMJRSaycR8/AAAAAElFTkSuQmCC);background-repeat:no-repeat}
.webix_accordionitem.vertical.collapsed>.webix_accordionitem_header .webix_accordionitem_button{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAsAAAAHCAYAAADebrddAAAASElEQVR42n2PMQoAMAgD++gEdPLpFkshS9oDEcxlcA1V1euNcpI9k5m2MPfrqABCBS8KBptAR8QJZks0kGjgjMQn/oc/AKy4Acp9RSZl+HEYAAAAAElFTkSuQmCC);background-repeat:no-repeat}
.webix_accordionitem.horizontal>.webix_accordionitem_header .webix_accordionitem_button{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAALCAYAAACzkJeoAAAASUlEQVR42p2QMQoAQQgD79EZ0Mqn5xCu2cPdYgUbJ0rMMxXgLeiuKq8gsJAz8weQ0QiwJEfECnr4wdlEnwJ68+yyhReC+c85oRecj0Um+pmo9wAAAABJRU5ErkJggg==);background-repeat:no-repeat}
.webix_accordionitem.horizontal:last-child>.webix_accordionitem_header .webix_accordionitem_button{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAALCAYAAACzkJeoAAAARElEQVR42qXPsQoAIAhF0T76PdDJT7cUmtQhcmk4N8QVQ9JXN2bmgWOgqp+BiCRG2AYAMoi3/LxYdyagAnhA6G93TrABZaJFJjrFY8IAAAAASUVORK5CYII=);background-repeat:no-repeat}
.webix_accordionitem.horizontal.collapsed:last-child>.webix_accordionitem_header .webix_accordionitem_button{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAsAAAAHCAYAAADebrddAAAASElEQVR42n2PMQoAMAgD++gEdPLpFkshS9oDEcxlcA1V1euNcpI9k5m2MPfrqABCBS8KBptAR8QJZks0kGjgjMQn/oc/AKy4Acp9RSZl+HEYAAAAAElFTkSuQmCC);background-repeat:no-repeat}
.webix_accordionitem.horizontal.collapsed>.webix_accordionitem_header .webix_accordionitem_button{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAsAAAAHCAYAAADebrddAAAARUlEQVR42o2PAQYAMAwD9+iEFvTpnZYZU7EQRa4kaxKAXD9y9yTZluCBALRJpATNrIG6rAdjjmBVeCuB6EyAYkNEiDE33yMJRSaycR8/AAAAAElFTkSuQmCC);background-repeat:no-repeat}
.webix_progress_bottom,.webix_progress_top{background:#f7f7f7;background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#f2f2f2),color-stop(50%,#f7f7f7),color-stop(100%,#f2f2f2));background-image:-webkit-linear-gradient(top,#f2f2f2 0,#f7f7f7 50%,#f2f2f2 100%);background-image:-moz-linear-gradient(top,#f2f2f2 0,#f7f7f7 60%,#f2f2f2 100%);background-image:-ms-linear-gradient(top,#f2f2f2 0,#f7f7f7 60%,#f2f2f2 100%);background-image:-o-linear-gradient(top,#f2f2f2 0,#f7f7f7 60%,#f2f2f2 100%);background-position:0 1px;background-repeat:repeat-x}
.webix_daterange .webix_range_timepicker .webix_cal_time{padding:5px 15px}
.webix_daterange .webix_range_footer .webix_template{padding:10px 0}
.webix_cal_range{background-color:#fff4dc}
.webix_item_tab .webix_icon{width:30px;font-size:19px}
.webix_tab_more_icon{border-bottom:1px solid #a4bed4}
.webix_skin_mark{height:110px}
.webix_sparklines{padding:1px 0;box-sizing:border-box}
.webix_sparklines svg{box-sizing:border-box}
.webix_sparklines_line{stroke:#3498db;stroke-width:1;fill:transparent;stroke-linecap:round}
.webix_sparklines_item{fill:#3498db;stroke-width:1}
.webix_sparklines_bar{fill:#6dbcf0}
.webix_sparklines_bar_negative{fill:#d86c79}
.webix_sparklines_area{fill:#e5eef4}
.webix_sparklines_origin{stroke:#888;stroke-width:1}
.webix_sparklines_event_area{fill:transparent}
.webix_sparklines_area_chart .webix_sparklines_event_area:hover,.webix_sparklines_line_chart .webix_sparklines_event_area:hover,.webix_sparklines_splinearea_chart .webix_sparklines_event_area:hover{fill:rgba(220,220,220,.4);stroke:rgba(255,255,255,.6)}
.webix_sparklines_bar_chart .webix_sparklines_event_area:hover,.webix_sparklines_pie_chart .webix_sparklines_event_area:hover{fill:rgba(255,255,255,.3)}
.webix_ui_print{display:none}
body.webix_print{margin-top:0}
@media print{body.webix_print{overflow:visible!important;background-color:none!important}
body.webix_print>*{display:none}
body.webix_print *{visibility:hidden}
.webix_ui_print{display:block!important;margin:0!important;visibility:visible!important}
.webix_ui_print *{visibility:visible!important}
.webix_print_noscroll{height:auto!important;width:auto!important}
.webix_print_noscroll,.webix_print_noscroll .webix_scroll_cont{overflow:visible!important}
.webix_print_pagebreak{page-break-after:always}
.webix_print_footer,.webix_print_header{display:block!important;margin:20px 0;text-align:center;height:auto!important;visibility:visible!important}
.webix_print_footer *,.webix_print_header *{visibility:visible!important}
.webix_table_print{display:table;visibility:visible!important;table-layout:fixed;width:100%;border:1px solid #ebebeb;margin-bottom:20px;position:initial}
.webix_table_print td{overflow:hidden}
.webix_table_print tr{page-break-inside:avoid}
.webix_table_print .webix_cell{display:table-cell!important;border-right:1px solid #ebebeb;border-bottom:1px solid #ebebeb;padding:0 10px;white-space:nowrap}
.webix_table_print .webix_header_cell{display:table-cell;color:#1e2022!important;box-shadow:0 1px 1px #fff inset;background:#e5f1ff;background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#eaf3ff),color-stop(50%,#e5f1ff),color-stop(100%,#d6e8ff));background-image:-webkit-linear-gradient(top,#eaf3ff 0,#e5f1ff 50%,#d6e8ff 100%);background-image:-moz-linear-gradient(top,#eaf3ff 0,#e5f1ff 60%,#d6e8ff 100%);background-image:-ms-linear-gradient(top,#eaf3ff 0,#e5f1ff 60%,#d6e8ff 100%);background-image:-o-linear-gradient(top,#eaf3ff 0,#e5f1ff 60%,#d6e8ff 100%);background-position:0 1px;background-repeat:repeat-x;border-right:1px solid #a4bed4;border-bottom:1px solid #a4bed4}
.webix_table_print .webix_footer_cell{display:table-cell;background:#fafafa;border-right:1px solid #ebebeb;border-top:1px solid #ebebeb;border-bottom:1px solid #ebebeb}
.webix_table_print .webix_cell.webix_dtable_span{position:initial!important;white-space:normal}
.webix_table_print .webix_rotate{transform-origin:center 40%!important;-webkit-transform-origin:center 40%!important}
.webix_table_print.borderless,.webix_table_print.borderless .webix_cell{border:none}
.webix_table_print.webix_view.webix_list-x{white-space:normal}
.webix_table_print.webix_view.webix_list-x .webix_list_item{display:table-cell}
}
/*! /*!
* Font Awesome 4.3.0 by @davegandy - http://fontawesome.io - @fontawesome * Font Awesome 4.3.0 by @davegandy - http://fontawesome.io - @fontawesome
* License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License)
*/ */
@font-face{font-family:FontAwesome;src:url(../fonts/fontawesome-webfont.eot?v=4.7.0);src:url(../fonts/fontawesome-webfont.eot?#iefix&v=4.7.0) format('embedded-opentype'),url(../fonts/fontawesome-webfont.woff2?v=4.7.0) format('woff2'),url(../fonts/fontawesome-webfont.woff?v=4.7.0) format('woff'),url(../fonts/fontawesome-webfont.ttf?v=4.7.0) format('truetype'),url(../fonts/fontawesome-webfont.svg?v=4.7.0#fontawesomeregular) format('svg');font-weight:400;font-style:normal} @font-face{font-family:FontAwesome;src:url(../fonts/fontawesome-webfont.eot?v=4.7.0);src:url(../fonts/fontawesome-webfont.eot?#iefix&v=4.7.0) format('embedded-opentype'),url(../fonts/fontawesome-webfont.woff2?v=4.7.0) format('woff2'),url(../fonts/fontawesome-webfont.woff?v=4.7.0) format('woff'),url(../fonts/fontawesome-webfont.ttf?v=4.7.0) format('truetype'),url(../fonts/fontawesome-webfont.svg?v=4.7.0#fontawesomeregular) format('svg');font-weight:400;font-style:normal}
.fa,.webix_icon,.webix_icon_btn,.webix_input_icon{display:inline-block;font-family:FontAwesome;font-style:normal;font-weight:400;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;transform:translate(0,0)} .fa{display:inline-block;font-family:FontAwesome;font-style:normal;font-weight:400;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;transform:translate(0,0)}
.fa-lg{font-size:1.33333333em;line-height:.75em;vertical-align:-15%} .fa-lg{font-size:1.33333333em;line-height:.75em;vertical-align:-15%}
.fa-2x{font-size:2em} .fa-2x{font-size:2em}
.fa-3x{font-size:3em} .fa-3x{font-size:3em}
@ -597,7 +781,7 @@ label.webix_required:after{padding-left:4px;content:"*";color:red}
.fa-ul>li{position:relative} .fa-ul>li{position:relative}
.fa-li{position:absolute;left:-2.14285714em;width:2.14285714em;top:.14285714em;text-align:center} .fa-li{position:absolute;left:-2.14285714em;width:2.14285714em;top:.14285714em;text-align:center}
.fa-li.fa-lg{left:-1.85714286em} .fa-li.fa-lg{left:-1.85714286em}
.fa-border{padding:.2em .25em .15em;border:.08em solid #eee;border-radius:.1em} .fa-border{padding:.2em .25em .15em;border:solid .08em #eee;border-radius:.1em}
.fa-pull-left{float:left} .fa-pull-left{float:left}
.fa-pull-right{float:right} .fa-pull-right{float:right}
.fa.fa-pull-left{margin-right:.3em} .fa.fa-pull-left{margin-right:.3em}
@ -609,9 +793,11 @@ label.webix_required:after{padding-left:4px;content:"*";color:red}
.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear} .fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}
.fa-pulse{-webkit-animation:fa-spin 1s infinite steps(8);animation:fa-spin 1s infinite steps(8)} .fa-pulse{-webkit-animation:fa-spin 1s infinite steps(8);animation:fa-spin 1s infinite steps(8)}
@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)} @-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}
100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}} 100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}
}
@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)} @keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}
100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}} 100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}
}
.fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)} .fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}
.fa-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)} .fa-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}
.fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)} .fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}
@ -1300,140 +1486,6 @@ label.webix_required:after{padding-left:4px;content:"*";color:red}
.fa-meetup:before{content:"\f2e0"} .fa-meetup:before{content:"\f2e0"}
.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0} .sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}
.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto} .sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}
.webix_icon,.webix_input_icon{font-size:17px;display:inline-block;width:20px}
.webix_input_icon{float:right;font-size:19px;color:#888}
.webix_icon_button{background-color:transparent;border:none;padding:0;cursor:pointer;position:relative}
.webix_icon_button .webix_icon{font-size:20px;height:23px;width:21px;display:inline-block;text-align:center;color:#606060}
.webix_icon_btn{font-size:17px;display:inline-block;text-align:center;width:24px;color:#606060}
.webix_el_iconBbutton .webix_icon_btn{text-align:center;width:100%;padding:5px 0}
.webix_el_icon{cursor:pointer}
.webix_badge{background-color:#ff8839;color:#fff;border-radius:50%;font-size:12px;height:22px;min-width:22px;box-sizing:border-box;padding:0 4px;text-align:center;line-height:21px;position:absolute;right:0;top:0}
.webix_list_item .webix_badge{position:static;float:right;margin:4px -5px 0 10px}
.webixtype_base .webix_badge{top:50%;margin-top:-11px;margin-right:8px}
.webix_treemap{background-color:#f5f5f5}
.webix_treemap .webix_scroll_cont{position:relative;height:100%;width:100%}
.webix_treemap_item{position:absolute;color:#444;overflow:hidden;text-align:left;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box;padding:3px 5px;border-width:0 1px 1px 0;border-style:solid;border-color:rgba(0,0,0,.2);background:0 0}
.webix_treemap_level_top{z-index:1;border-color:rgba(0,0,0,.4)}
.webix_treemap_item_bottom{border-bottom-width:0}
.webix_treemap_item_right{border-right-width:0}
.webix_treemap_item:hover{box-shadow:inset 0 0 5px #666}
.webix_treemap_item.webix_selected{border-color:#ffdb8f;box-shadow:inset 0 0 1px 2px #ffdb8f}
.webix_treemap_header{width:100%;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box;padding:0 10px;border-bottom:1px solid #a4bed4}
.webix_treemap_header_item{cursor:pointer}
.webix_treemap_header_item:last-child{cursor:default}
.webix_treemap_reset{float:right;width:25px;text-align:center}
.webix_treemap_reset:before{content:"\f00d";color:#1e2022;font-family:FontAwesome;font-size:16px;display:block;cursor:pointer}
.webix_treemap_path_icon{width:20px;text-align:center}
.webix_barcode{position:relative}
.webix_barcode .webix_canvas_text{font-size:13px;padding:0 2px;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box}
.portlet_drag{position:absolute;z-index:1;top:5px;right:5px;width:18px;opacity:.5;cursor:pointer}
.portlet_in_drag{opacity:.4}
.portlet_marker,.portlet_markerbottom,.portlet_markerleft,.portlet_markerright,.portlet_markertop{opacity:.5;background:#9169BE;position:absolute;transition:top .5s,left .5s,width .5s,height .5s;top:0;left:0;width:100%;height:100%}
.portlet_markertop{height:50%}
.portlet_markerbottom{height:50%;top:50%}
.portlet_markerleft{width:50%}
.portlet_markerright{width:50%;left:50%}
.webix_popup.webix_sidemenu.webix_sidemenu_left{border-width:0 1px 0 0;box-shadow:2px 0 2px rgba(0,0,0,.05)}
.webix_sidemenu_left.webix_animate{transition:left .6s}
.webix_popup.webix_sidemenu.webix_sidemenu_right{left:auto;border-width:0 0 0 1px;box-shadow:-2px 0 2px rgba(0,0,0,.05)}
.webix_sidemenu_right.webix_animate{transition:right .6s}
.webix_popup.webix_sidemenu.webix_sidemenu_top{border-width:0 0 1px;box-shadow:0 2px 2px rgba(0,0,0,.05)}
.webix_sidemenu_top .webix_win_body{position:relative}
.webix_sidemenu_top .webix_win_body>.webix_view{position:absolute;top:auto;left:0;bottom:0}
.webix_sidemenu_top.webix_animate,.webix_sidemenu_top.webix_animate .webix_win_body{transition:height .6s}
.webix_popup.webix_sidemenu.webix_sidemenu_bottom{border-width:1px 0 0;top:auto;box-shadow:0 -2px 2px rgba(0,0,0,.05)}
.webix_sidemenu_bottom.webix_animate,.webix_sidemenu_bottom.webix_animate .webix_win_body{transition:height .6s}
.webix_view.webix_pdf{background-color:#404040;overflow:auto;-webkit-overflow-scrolling:touch}
.webix_pdf .canvas_wrapper{margin:0 auto;box-shadow:5px 5px 15px #1c1c1c;width:100%;height:100%}
.webix_toolbar.pdf_bar{background-color:#474747}
.webix_toolbar.pdf_bar .webix_icon_btn{font-size:15px}
.webix_toolbar.pdf_bar .webix_img_btn:focus,.webix_toolbar.pdf_bar .webix_img_btn:hover{background-color:#404040}
.webix_toolbar.pdf_bar .webix_template{background-color:#3498db;color:#fff;line-height:2em}
.webix_toolbar.pdf_bar .webix_el_box input,.webix_toolbar.pdf_bar .webix_inp_static{background-color:#5c5c5c;color:#fff;border-color:#404040}
.webix_toolbar.pdf_bar .webix_el_box input:focus,.webix_toolbar.pdf_bar .webix_inp_static:focus{border-color:#333}
.webix_toolbar.pdf_bar .webix_el_box .webix_input_icon{color:#fff}
.webix_view.webix_popup.pdf_opt_list{box-shadow:5px 5px 15px #1c1c1c;border:none}
.pdf_opt_list .webix_list{background-color:#404040;color:#fff}
.pdf_opt_list .webix_list .webix_list_item{border-color:#474747;line-height:1.5em}
.pdf_opt_list .webix_list .webix_list_item.webix_selected,.pdf_opt_list .webix_list .webix_list_item:hover{color:#404040;background-color:#fff}
.webix_dbllist .webix_list{background:#ededed}
.webix_dbllist .webix_list_item{background:#fff;margin:3px 4px 0;box-sizing:border-box}
.webix_dbllist .webix_list_item.webix_selected{color:#FFF;background:#27ae60}
.webix_dbllist button{width:45%;height:30px;margin-top:5px;background:#fff;border-radius:6px;border:1px solid #a4bed4;cursor:pointer;font-size:13px;font-family:Helvetica,Verdana}
.webix_dbllist button:active{box-shadow:inset 1px 1px 0 silver}
.webix_dbllist button .webix_icon{color:#888;font-size:26px}
.webix_dbllist button:first-child{margin-top:32px;margin-right:5%}
.webix_dbllist button:last-child{margin-left:5%}
.webix_dbllist .bottom_label{font-size:10px;text-transform:uppercase;background:#ededed;padding-left:13px}
.webix_invalid .webix_list{background:#f8e2e2}
.webix_toolbar .webix_el_label,.webix_toolbar .webix_inp_label{color:#1e2022}
.webix_ss_sort_asc{position:absolute;float:right;right:3px;top:8px;width:7px;height:13px;background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAANCAYAAABlyXS1AAAARUlEQVR4nGNgQAKGxib/GbABkIS7b8B/DAUwCRiGK0CXwFBAb1DfP/U/LszwHwi2X7qFgUEArBtdAVwCBmAKMCSQFSDzAWXXaOHsXeqkAAAAAElFTkSuQmCC)}
.webix_ss_sort_desc{position:absolute;right:3px;top:8px;width:7px;height:13px;background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAANCAYAAABlyXS1AAAARUlEQVR42mNgQAL1/VP/M2ADIIntF2/9x1AAlrh0C47hCmA60DFYwX88gIFGwNDY5D8uDFbg7hvwHx2jmIBTAlkB0e4BAEjlaNtBWJPnAAAAAElFTkSuQmCC)}
.webix_group_back .webix_arrow_icon{color:#1e2022}
.webix_menu-x .webix_list_item:active,.webix_menu-x .webix_list_item:focus,.webix_menu-x .webix_list_item:hover{background-color:rgba(255,255,255,.4)!important}
.webix_menu .webix_list_item:active,.webix_menu .webix_list_item:hover{background-color:rgba(0,0,0,.05)!important}
.webix_list_item .webix_submenu_icon{width:0;height:0;border-width:4px;border-style:solid}
.webix_menu-x .webix_list_item .webix_submenu_icon{border-color:#375975 transparent transparent}
.webix_menu .webix_list_item .webix_submenu_icon{border-color:transparent transparent transparent #375975}
.webix_popup_title{color:#1e2022!important;background:#e3e3e3!important;font-size:15px!important;font-weight:700;border-bottom:1px solid #dedede!important;text-shadow:none!important}
.webix_modal_box{background:#ebebeb!important;border:1px solid #a4bed4!important}
.confirmButton{color:#1e2022!important;box-shadow:0 1px 1px #fff inset;background:0 1px repeat-x #e5f1ff;background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#eaf3ff),color-stop(50%,#e5f1ff),color-stop(100%,#d6e8ff)) 0 1px repeat-x;background-image:-webkit-linear-gradient(top,#eaf3ff 0,#e5f1ff 50%,#d6e8ff 100%);background-image:-moz-linear-gradient(top,#eaf3ff 0,#e5f1ff 60%,#d6e8ff 100%);background-image:-ms-linear-gradient(top,#eaf3ff 0,#e5f1ff 60%,#d6e8ff 100%);background-image:-o-linear-gradient(top,#eaf3ff 0,#e5f1ff 60%,#d6e8ff 100%)}
.confirmButtonActive{background:#fff}
.webix_item_tab{font-size:13px}
.buttonStyle{font-size:13px;font-family:Helvetica,Verdana}
.activeShadow{box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}
.webix_img_btn_abs:active+input,.webix_inp_counter_next:active,.webix_inp_counter_prev:active,.webixtype_base:active{background:none #f5f5f5;box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}
.webix_icon_button:active,.webix_img_btn:active,.webix_img_btn_top:active{box-shadow:inset 0 3px 5px rgba(0,0,0,.125);background-color:rgba(0,0,0,.05)}
.webix_pressed .webix_img_btn_abs+input,.webix_pressed .webixtype_base{box-shadow:inset 0 3px 5px rgba(0,0,0,.125);background:#f5f5f5!important}
.webix_slider_vertical .webix_slider_box{margin-top:-8px}
.webix_accordionitem_label .webix_icon{font-size:19px;width:25px}
.webix_accordionitem.vertical>.webix_accordionitem_header .webix_accordionitem_button{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAsAAAAHCAYAAADebrddAAAARUlEQVR42o2PAQYAMAwD9+iEFvTpnZYZU7EQRa4kaxKAXD9y9yTZluCBALRJpATNrIG6rAdjjmBVeCuB6EyAYkNEiDE33yMJRSaycR8/AAAAAElFTkSuQmCC);background-repeat:no-repeat}
.webix_accordionitem.vertical.collapsed>.webix_accordionitem_header .webix_accordionitem_button{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAsAAAAHCAYAAADebrddAAAASElEQVR42n2PMQoAMAgD++gEdPLpFkshS9oDEcxlcA1V1euNcpI9k5m2MPfrqABCBS8KBptAR8QJZks0kGjgjMQn/oc/AKy4Acp9RSZl+HEYAAAAAElFTkSuQmCC);background-repeat:no-repeat}
.webix_accordionitem.horizontal>.webix_accordionitem_header .webix_accordionitem_button{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAALCAYAAACzkJeoAAAASUlEQVR42p2QMQoAQQgD79EZ0Mqn5xCu2cPdYgUbJ0rMMxXgLeiuKq8gsJAz8weQ0QiwJEfECnr4wdlEnwJ68+yyhReC+c85oRecj0Um+pmo9wAAAABJRU5ErkJggg==);background-repeat:no-repeat}
.webix_accordionitem.horizontal:last-child>.webix_accordionitem_header .webix_accordionitem_button{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAALCAYAAACzkJeoAAAARElEQVR42qXPsQoAIAhF0T76PdDJT7cUmtQhcmk4N8QVQ9JXN2bmgWOgqp+BiCRG2AYAMoi3/LxYdyagAnhA6G93TrABZaJFJjrFY8IAAAAASUVORK5CYII=);background-repeat:no-repeat}
.webix_accordionitem.horizontal.collapsed:last-child>.webix_accordionitem_header .webix_accordionitem_button{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAsAAAAHCAYAAADebrddAAAASElEQVR42n2PMQoAMAgD++gEdPLpFkshS9oDEcxlcA1V1euNcpI9k5m2MPfrqABCBS8KBptAR8QJZks0kGjgjMQn/oc/AKy4Acp9RSZl+HEYAAAAAElFTkSuQmCC);background-repeat:no-repeat}
.webix_accordionitem.horizontal.collapsed>.webix_accordionitem_header .webix_accordionitem_button{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAsAAAAHCAYAAADebrddAAAARUlEQVR42o2PAQYAMAwD9+iEFvTpnZYZU7EQRa4kaxKAXD9y9yTZluCBALRJpATNrIG6rAdjjmBVeCuB6EyAYkNEiDE33yMJRSaycR8/AAAAAElFTkSuQmCC);background-repeat:no-repeat}
.webix_progress_bottom,.webix_progress_top{background:0 1px repeat-x #f7f7f7;background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#f2f2f2),color-stop(50%,#f7f7f7),color-stop(100%,#f2f2f2)) 0 1px repeat-x;background-image:-webkit-linear-gradient(top,#f2f2f2 0,#f7f7f7 50%,#f2f2f2 100%);background-image:-moz-linear-gradient(top,#f2f2f2 0,#f7f7f7 60%,#f2f2f2 100%);background-image:-ms-linear-gradient(top,#f2f2f2 0,#f7f7f7 60%,#f2f2f2 100%);background-image:-o-linear-gradient(top,#f2f2f2 0,#f7f7f7 60%,#f2f2f2 100%)}
.webix_daterange .webix_range_timepicker .webix_cal_time{padding:5px 15px}
.webix_daterange .webix_range_footer .webix_template{padding:10px 0}
.webix_cal_range{background-color:#fff4dc}
.webix_item_tab .webix_icon{width:30px;font-size:19px}
.webix_tab_more_icon{border-bottom:1px solid #a4bed4}
.webix_skin_mark{height:110px}
.webix_sparklines{padding:1px 0;box-sizing:border-box}
.webix_sparklines svg{box-sizing:border-box}
.webix_sparklines_line{stroke:#3498db;stroke-width:1;fill:transparent;stroke-linecap:round}
.webix_sparklines_item{fill:#3498db;stroke-width:1}
.webix_sparklines_bar{fill:#6dbcf0}
.webix_sparklines_bar_negative{fill:#d86c79}
.webix_sparklines_area{fill:#e5eef4}
.webix_sparklines_origin{stroke:#888;stroke-width:1}
.webix_sparklines_event_area{fill:transparent}
.webix_sparklines_area_chart .webix_sparklines_event_area:hover,.webix_sparklines_line_chart .webix_sparklines_event_area:hover,.webix_sparklines_splinearea_chart .webix_sparklines_event_area:hover{fill:rgba(220,220,220,.4);stroke:rgba(255,255,255,.6)}
.webix_sparklines_bar_chart .webix_sparklines_event_area:hover,.webix_sparklines_pie_chart .webix_sparklines_event_area:hover{fill:rgba(255,255,255,.3)}
.webix_ui_print{display:none}
body.webix_print{margin-top:0}
@media print{body.webix_print{overflow:visible!important;background-color:none!important}
body.webix_print>*{display:none}
body.webix_print *{visibility:hidden}
.webix_ui_print{display:block!important;margin:0!important;visibility:visible!important}
.webix_ui_print *{visibility:visible!important}
.webix_print_noscroll{height:auto!important;width:auto!important}
.webix_print_noscroll,.webix_print_noscroll .webix_scroll_cont{overflow:visible!important}
.webix_print_pagebreak{page-break-after:always}
.webix_print_footer,.webix_print_header{display:block!important;margin:20px 0;text-align:center;height:auto!important;visibility:visible!important}
.webix_print_footer *,.webix_print_header *{visibility:visible!important}
.webix_table_print{display:table;visibility:visible!important;table-layout:fixed;width:100%;border:1px solid #ebebeb;margin-bottom:20px;position:initial}
.webix_table_print td{overflow:hidden}
.webix_table_print tr{page-break-inside:avoid}
.webix_table_print .webix_cell{display:table-cell!important;border-right:1px solid #ebebeb;border-bottom:1px solid #ebebeb;padding:0 10px;white-space:nowrap}
.webix_table_print .webix_header_cell{display:table-cell;color:#1e2022!important;box-shadow:0 1px 1px #fff inset;background:0 1px repeat-x #e5f1ff;background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#eaf3ff),color-stop(50%,#e5f1ff),color-stop(100%,#d6e8ff)) 0 1px repeat-x;background-image:-webkit-linear-gradient(top,#eaf3ff 0,#e5f1ff 50%,#d6e8ff 100%);background-image:-moz-linear-gradient(top,#eaf3ff 0,#e5f1ff 60%,#d6e8ff 100%);background-image:-ms-linear-gradient(top,#eaf3ff 0,#e5f1ff 60%,#d6e8ff 100%);background-image:-o-linear-gradient(top,#eaf3ff 0,#e5f1ff 60%,#d6e8ff 100%);border-right:1px solid #a4bed4;border-bottom:1px solid #a4bed4}
.webix_table_print .webix_footer_cell{display:table-cell;background:#fafafa;border-right:1px solid #ebebeb;border-top:1px solid #ebebeb;border-bottom:1px solid #ebebeb}
.webix_table_print .webix_cell.webix_dtable_span{position:initial!important;white-space:normal}
.webix_table_print .webix_rotate{transform-origin:center 40%!important;-webkit-transform-origin:center 40%!important}
.webix_table_print.borderless,.webix_table_print.borderless .webix_cell{border:none}
.webix_table_print.webix_view.webix_list-x{white-space:normal}
.webix_table_print.webix_view.webix_list-x .webix_list_item{display:table-cell}}
.webix_strong{font-weight:700;white-space:nowrap} .webix_strong{font-weight:700;white-space:nowrap}
.webix_light{color:#666d73} .webix_light{color:#666d73}
.webix_debug div{border:1px solid orange;background-color:#FF9;color:#000} .webix_debug div{border:1px solid orange;background-color:#FF9;color:#000}

View File

@ -24,12 +24,43 @@
font-size: 125%; font-size: 125%;
color: DarkRed; color: DarkRed;
} }
.right_footer2 {
text-align: right;
font-weight: bold;
font-size: 150%;
color: DarkRed;
}
.right_footer3 {
text-align: right;
font-weight: bold;
font-size: 120%;
color: DarkBlue;
}
.footer3 {
font-weight: bold;
font-size: 120%;
color: DarkBlue;
}
.lbl_partner { .lbl_partner {
font-weight: bold; font-weight: bold;
font-size: 125%; font-size: 125%;
} }
.link_default {
font-weight: bold;
color: #610B0B;
text-decoration: none;
}
.link_default:hover {text-decoration:underline;}
.link_forum {
font-weight: bold;
color: #610B0B;
text-decoration: none;
}
.link_forum:hover {text-decoration:underline;}
.delete { .delete {
text-align: center; text-align: center;
font-weight: bold; font-weight: bold;
@ -37,6 +68,10 @@
color: red; color: red;
} }
.cancel {
color: red;
}
.cmd_close_partner div button { .cmd_close_partner div button {
background-color: red !important; background-color: red !important;
@ -56,6 +91,28 @@
.webix_success div { .webix_success div {
background-color: #00a65a !important; background-color: #00a65a !important;
font-size: 1vw; font-weight: bold;
font-size: 1.5vw;
color: white; color: white;
} }
.webix_error div {
background-color: #DF0101 !important;
font-weight: bold;
font-size: 1.5vw;
color: white;
}
.table_sg .webix_hcell{
background: #A64C4C;
color: white;
font-weight: bold;
}
.table_sg .webix_column{
font-style: italic;
background: #FFF8DC;
}
.table_sg .webix_column > div{
border-color: #ddd;
}

View File

@ -0,0 +1,519 @@
@page{
size: Letter;
margin: 0.5cm;
background-color: white !important;
}
@media print {
thead {display: table-header-group;}
body {
background-color: #fff;
}
}
.emisor-cintilla{
background-color: #975759;
color: #fff;
}
.fiscales-emisor{
color: #7d1916;
}
.fiscales-emisor .telefono, .fiscales-emisor .correo, .fiscales-emisor .web{
color: #333;
}
header .titulo-vertical{
background-color: #dbc5c6;
color: #000;
}
.receptor .nombre{
color: #000;
}
.receptor .rfc{
color: #000;
}
.receptor .direccion{
color: #000;
}
.receptor .estado{
color: #000;
}
.cfdi .folio span{
color: #ed483d;
}
.cfdi .tipo span{
color: #ed483d;
}
.cfdi .folio-fiscal span{
color: #ed483d;
}
.conceptos th{
background-color: #975759;
}
.conceptos td.clave, .conceptos td.unidad, .conceptos td.valor-unitario{
background-color: #dbc5c6;
}
.conceptos td.descripcion, .conceptos td.cantidad, .conceptos td.importe{
background-color: #f0e7e7;
}
table.subtotal th{
background-color: #dbc5c6;
}
table.subtotal td{
background-color: #f0e7e7;
}
.sello .cadenas-sello .cadena div {
background-color: #dbc5c6;
color: #7d1916;
}
.rfc-pac {
background-color: #dbc5c6;
color: #7d1916;
}
.cancelada{
color: #ed3833;
}
@font-face {
font-family: 'Avenir Next';
src: url('/static/fonts/avenir_next/AvenirNextLTPro-Regular.eot');
src: url('/static/fonts/avenir_next/AvenirNextLTPro-Regular.eot?#iefix') format('embedded-opentype'),
url('/static/fonts/avenir_next/AvenirNextLTPro-Regular.woff2') format('woff2'),
url('/static/fonts/avenir_next/AvenirNextLTPro-Regular.woff') format('woff'),
url('/static/fonts/avenir_next/AvenirNextLTPro-Regular.ttf') format('truetype'),
url('/static/fonts/avenir_next/AvenirNextLTPro-Regular.svg#AvenirNextLTPro-Regular') format('svg');
font-weight: normal;
font-style: normal;
}
@font-face {
font-family: 'Avenir Next';
src: url('/static/fonts/avenir_next/AvenirNextLTPro-Bold.eot');
src: url('/static/fonts/avenir_next/AvenirNextLTPro-Bold.eot?#iefix') format('embedded-opentype'),
url('/static/fonts/avenir_next/AvenirNextLTPro-Bold.woff2') format('woff2'),
url('/static/fonts/avenir_next/AvenirNextLTPro-Bold.woff') format('woff'),
url('/static/fonts/avenir_next/AvenirNextLTPro-Bold.ttf') format('truetype'),
url('/static/fonts/avenir_next/AvenirNextLTPro-Bold.svg#AvenirNextLTPro-Bold') format('svg');
font-weight: bold;
font-style: normal;
}
@font-face {
font-family: 'Avenir Next';
src: url('/static/fonts/avenir_next/AvenirNextLTPro-It.eot');
src: url('/static/fonts/avenir_next/AvenirNextLTPro-It.eot?#iefix') format('embedded-opentype'),
url('/static/fonts/avenir_next/AvenirNextLTPro-It.woff2') format('woff2'),
url('/static/fonts/avenir_next/AvenirNextLTPro-It.woff') format('woff'),
url('/static/fonts/avenir_next/AvenirNextLTPro-It.ttf') format('truetype'),
url('/static/fonts/avenir_next/AvenirNextLTPro-It.svg#AvenirNextLTPro-It') format('svg');
font-weight: normal;
font-style: italic;
}
body {
/*
font-family: 'Avenir Next';
*/
}
#plantilla {
border: 1px solid #fff;
border-color: rgb(204,204,204);
background: #fff;
display: block;
margin: 0 auto;
width: 19.5cm;
height: 25.9cm;
position: relative;
}
.cancelada{
-ms-transform: rotate(320deg);
-webkit-transform: rotate(320deg);
transform: rotate(320deg);
color: #ed3833;
font-size: 100px;
font-weight: bold;
left: 18%;
position: absolute;
text-align: center;
top: 30%;
z-index: 1000;
}
.cancelada div{
font-size: 20px;
}
.emisor-cintilla{
background-color: #975759;
color: #fff;
font-size: 7pt;
padding: 5px;
text-align: center;
}
.datos-emisor .logo{
margin-left: 20px;
max-width: 180px;
}
.datos-emisor .fiscales-emisor{
float: right;
}
.fiscales-emisor{
color: #7d1916;
font-weight: bold;
margin-top: 20px;
text-align: right;
}
.fiscales-emisor .nombre{
font-size: 18px;
padding-right: 30px;
}
.fiscales-emisor .rfc{
font-size: 16px;
padding-right: 30px;
}
.fiscales-emisor .regimen{
font-size: 12px;
padding-right: 30px;
}
.fiscales-emisor .telefono, .fiscales-emisor .correo, .fiscales-emisor .web{
color: #333;
font-size: 14px;
line-height: 15px;
padding-right: 10px;
}
.fiscales-emisor img{
margin-left: 10px;
width: 10px;
}
.clear{
clear: both;
}
header .titulo-vertical{
background-color: #dbc5c6;
-ms-transform: rotate(270deg);
-webkit-transform: rotate(270deg);
transform: rotate(270deg);
font-size: 15px;
font-weight: bold;
left: -31px;
line-height: 35px;
position: absolute;
text-align: center;
top: 35px;
width: 105px;
}
header .receptor{
box-sizing: border-box;
float: left;
font-size: 10px;
height: 106px;
margin-top: 10px;
position: relative;
padding-bottom: 5px;
padding-left: 45px;
padding-top: 5px;
width: 60%;
}
.receptor .nombre{
color: #000;
font-size: 17px;
font-weight: bold;
}
.receptor .rfc{
color: #000;
font-size: 15px;
font-weight: bold;
}
.receptor .direccion,
.receptor .estado{
color: #000;
font-size: 10px;
}
.receptor img{
width: 10px;
}
.receptor .correo,
.receptor .telefono{
float: right;
width: 40%;
}
.receptor .uso-cfdi{
color: #000;
font-size: 12px;
font-weight: bold;
float: left;
width: 60%;
}
header .cfdi{
box-sizing: border-box;
float: right;
font-size: 10px;
height: 100px;
margin-top: 10px;
position: relative;
padding-bottom: 5px;
padding-left: 45px;
padding-top: 5px;
width: 35%;
}
.cfdi .folio, .cfdi .tipo{
color: #000;
float: left;
font-size: 11px;
font-weight: bold;
line-height: 15px;
width: 50%;
}
.cfdi .folio span, .cfdi .tipo span{
color: #ed483d;
}
.cfdi .folio span{
font-size: 10px;
}
.cfdi .tipo span{
font-size: 10px;
}
.cfdi .folio-fiscal{
color: #000;
font-size: 9px;
font-weight: bold;
line-height: 15px;
text-align: center;
}
.cfdi .folio-fiscal span{
color: #ed483d;
}
.cfdi .fecha-emision, .cfdi .fecha-certificacion, .cfdi .lugar-expedicion{
color: #333;
font-size: 10px;
font-weight: bold;
line-height: 15px;
}
.conceptos{
margin-bottom: 10px;
margin-top: 20px;
width: 100%;
text-align: center;
}
.conceptos th{
background-color: #975759;
color: #fff;
font-size: 11px;
line-height: 15px;
}
.conceptos .clave{
width: 10%;
}
.conceptos .descripcion{
width: 45%;
}
.conceptos .descripcion div{
text-align: justify;
}
.conceptos .unidad{
width: 8%;
}
.conceptos .cantidad{
width: 9%;
}
.conceptos .valor-unitario{
width: 13%;
}
.conceptos .importe{
width: 15%;
}
.conceptos td{
background-color: #975759;
color: #000;
font-size: 11px;
line-height: 15px;
}
.conceptos td.clave, .conceptos td.unidad, .conceptos td.valor-unitario{
background-color: #dbc5c6;
}
.conceptos td.descripcion, .conceptos td.cantidad, .conceptos td.importe{
background-color: #f0e7e7;
}
.conceptos td.valor-unitario, .conceptos td.importe{
text-align: right;
}
.total-letras{
float: left;
font-size: 12px;
line-height: 15px;
margin: 5px 0;
width: 63%;
}
table.subtotal{
float: right;
font-size: 12px;
line-height: 15px;
text-align: right;
width: 37%;
font-weight: bold;
}
table.subtotal th{
background-color: #f0e7e7;
width: 60%;
padding: 3px 3px 3px 3px;
}
table.subtotal td{
background-color: #dbc5c6;
width: 40%;
padding: 3px 3px 3px 3px;
}
.condiciones-pago{
font-size: 11px;
line-height: 14px;
}
.notas{
float: left;
font-size: 11px;
margin: 20px 0;
width: 70%;
}
.formapago-metodopago, .moneda-tipocambio, .tiporelacion, .relacionados{
float: left;
font-size: 11px;
line-height: 12px;
width: 70%;
}
.factura-info{
float: left;
font-size: 11px;
line-height: 13px;
width: 70%;
}
.tipocomite, .tipoproceso, .idcontabilidad{
float: right;
font-size: 10px;
line-height: 12px;
width: 20%;
}
.sello{
margin: 10px 0;
}
.sello .cbb{
border: 1px solid #000;
height: auto;
margin-top: 10px;
margin-right: 1px;
width: 18%;
}
.sello .cadenas-sello{
color: #000;
float: right;
font-size: 8px;
font-weight: bold;
line-height: 15px;
width: 79%;
margin-left: 5px;
margin-right: 5px;
word-break: break-all;
}
.sello .cadenas-sello .cadena{
margin: 5px 0;
}
.sello .cadenas-sello .cadena div{
font-weight: normal;
background-color: #dbc5c6;
color: #7d1916;
}
.cadena-original{
color: #000;
float: right;
font-size: 8px;
font-weight: bold;
line-height: 15px;
width: 99%;
margin: 5px;
word-break: break-all;
}
.cadena-original div{
font-weight: normal;
background-color: #dbc5c6;
color: #7d1916;
}
.rfc-pac{
background-color: #dbc5c6;
color: #7d1916;
font-size: 10px;
line-height: 15px;
text-align: center;
margin: 5px;
}

View File

@ -1,95 +0,0 @@
/* SideBar*/
.webix_sidebar{
background: #ECEFF1;
}
.webix_sidebar .webix_tree_item {
color: #454545;
height: 35px;
line-height: 35px;
}
.webix_sidebar .webix_scroll_cont > .webix_tree_leaves {
padding: 0;
}
.webix_sidebar .webix_tree_leaves .webix_tree_leaves {
margin-left: 0px;
}
.webix_sidebar_selected,
.webix_sidebar_expanded .webix_tree_item:hover{
background-color: rgba(0,0,0,0.02);
}
.webix_sidebar .webix_tree_item.webix_selected,
.webix_sidebar .webix_tree_item.webix_selected span{
background-color: #27ae60;
padding-right:0;
}
.webix_sidebar .webix_tree_branch_1 .webix_tree_item{
padding-left:40px;
}
.webix_sidebar .webix_tree_branch_1>.webix_tree_item{
height: 40px;
line-height: 40px;
padding-left:0;
}
.webix_sidebar .webix_tree_branch_1{
border-bottom:1px solid #e5e5e5;
}
.webix_sidebar .webix_tree_item.webix_selected span,
.webix_sidebar .webix_tree_item span{
margin:0;
padding:0px;
}
.webix_sidebar_icon{
width: 40px;
text-align: center;
}
.webix_sidebar_dir_icon{
float: right;
line-height: inherit;
}
/*SubMenu (Popup) */
.webix_sidebar_popup{
border:none !important;
box-shadow: 2px 3px 3px #ddd;
}
.webix_sidebar_popup, .webix_sidebar_popup .webix_list_item{
border-radius:0;
}
.webix_sidebar_popup_right{
box-shadow: -1px 3px 3px #ddd;
}
/*SubMenu: title*/
.webix_sidebar_popup_title{
background: #ECEFF1;
}
.webix_sidebar_popup_title.webix_selected{
border-left-color: #27ae60;
background: #27ae60;
}
.webix_sidebar_popup_title .webix_template{
line-height: 40px;
padding: 0 10px;
border: 1px solid #E5E5E5;
border-left:none;
}
.webix_sidebar_selected.webix_sidebar_popup_title .webix_template{
background: rgba(0,0,0,0.03);
border-left: none;
}
.webix_sidebar_popup_list .webix_list_item{
border-left: 1px solid #E5E5E5;
border-right: 1px solid #E5E5E5;
}
/*SubMenu: list*/
.webix_sidebar_popup_list .webix_list_item:first-child{
border-top: 1px solid #E5E5E5;
}
.webix_sidebar_popup_list .webix_list_item:hover{
background: #f6f9fb;
}
.webix_sidebar_popup_list .webix_list_item.webix_selected:hover{
background: #27ae60;
}

File diff suppressed because it is too large Load Diff

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 188 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 169 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 191 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 754 B

After

Width:  |  Height:  |  Size: 380 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

BIN
source/static/img/web.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,57 +1,87 @@
var gi = null var gi = null
function configuracion_inicial(){
webix.ajax().get('/values/admin', function(text, data){
var values = data.json()
show('cmd_ir_al_admin', values)
})
webix.ajax().sync().get('/values/main', function(text, data){
var values = data.json()
$$('lbl_title_main').setValue(values.empresa)
var pos = 4
if(values.escuela){
var node = {
id: 'app_school',
icon: 'graduation-cap',
value: 'Escuela'}
$$('main_sidebar').add(node, pos)
pos += 1
}
if(values.nomina){
var node = {
id: 'app_nomina',
icon: 'users',
value: 'Nómina'}
$$('main_sidebar').add(node, pos)
pos += 1
}
if(values.punto_de_venta){
var node = {
id: 'app_tickets',
icon: 'money',
value: 'Punto de venta'}
$$('main_sidebar').add(node, pos)
}
$$('cmd_update_timbres').define('badge', values.timbres)
$$('cmd_update_timbres').refresh()
add_config({'key': 'decimales_precios', 'value': values.decimales_precios})
add_config({'key': 'used_cfdi_pays', 'value': values.pagos})
add_config({'key': 'multi_currency', 'value': values.multi_currency})
add_config({'key': 'pays_data_bank', 'value': values.pays_data_bank})
add_config({'key': 'show_filter_by_day', 'value': values.show_filter_by_day})
})
get_way_payment()
}
function cmd_ir_al_admin_click(){
window.location = '/admin'
}
var controllers = { var controllers = {
init: function(){ init: function(){
//~ Main //~ Main
$$('menu_user').attachEvent('onMenuItemClick', menu_user_click); $$('menu_user').attachEvent('onMenuItemClick', menu_user_click);
//~ Partner configuracion_inicial()
$$('cmd_new_partner').attachEvent('onItemClick', cmd_new_partner_click);
$$('cmd_new_contact').attachEvent('onItemClick', cmd_new_contact_click); var tb_invoice = $$('tv_invoice').getTabbar()
$$('cmd_edit_partner').attachEvent('onItemClick', cmd_edit_partner_click); tb_invoice.attachEvent('onChange', tb_invoice_change)
$$('cmd_delete_partner').attachEvent('onItemClick', cmd_delete_partner_click); $$('prefilter_year').attachEvent('onChange', prefilter_year_change)
$$('cmd_save_partner').attachEvent('onItemClick', cmd_save_partner_click); $$('prefilter_month').attachEvent('onChange', prefilter_month_change)
$$('cmd_cancel_partner').attachEvent('onItemClick', cmd_cancel_partner_click); $$('cmd_delete_preinvoice').attachEvent('onItemClick', cmd_delete_preinvoice_click)
$$('cmd_cancel_contact').attachEvent('onItemClick', cmd_cancel_contact_click); $$('cmd_facturar_preinvoice').attachEvent('onItemClick', cmd_facturar_preinvoice_click)
$$('codigo_postal').attachEvent('onKeyPress', postal_code_key_press); $$('cmd_update_timbres').attachEvent('onItemClick', cmd_update_timbres_click)
$$('codigo_postal').attachEvent('onTimedKeyPress', postal_code_key_up); $$('grid_preinvoices').attachEvent('onItemClick', grid_preinvoices_click)
$$('colonia').attachEvent('onFocus', colonia_on_focus)
$$("tipo_persona").attachEvent( "onChange", opt_tipo_change) partners_controllers.init()
$$("es_cliente").attachEvent( "onChange", is_client_change) products_controllers.init()
$$("es_proveedor").attachEvent( "onChange", is_supplier_change) bancos_controllers.init()
$$("rfc").attachEvent( "onBlur", rfc_lost_focus) invoices_controllers.init()
$$('multi').attachEvent('onViewChange', multi_change) controllers_school.init()
//~ Products nomina_controllers.init()
$$("cmd_new_product").attachEvent("onItemClick", cmd_new_product_click) tickets_controllers.init()
$$("cmd_edit_product").attachEvent("onItemClick", cmd_edit_product_click)
$$("cmd_delete_product").attachEvent("onItemClick", cmd_delete_product_click)
$$("cmd_save_product").attachEvent("onItemClick", cmd_save_product_click)
$$("cmd_cancel_product").attachEvent("onItemClick", cmd_cancel_product_click)
$$("chk_automatica").attachEvent("onChange", chk_automatica_change)
$$("valor_unitario").attachEvent("onChange", valor_unitario_change)
//~ Invoices
$$('cmd_new_invoice').attachEvent("onItemClick", cmd_new_invoice_click)
$$('cmd_refacturar').attachEvent("onItemClick", cmd_refacturar_click)
$$('cmd_delete_invoice').attachEvent("onItemClick", cmd_delete_invoice_click)
$$('cmd_timbrar').attachEvent('onItemClick', cmd_timbrar_click)
$$('cmd_close_invoice').attachEvent('onItemClick', cmd_close_invoice_click)
$$('search_client_id').attachEvent('onKeyPress', search_client_id_key_press)
$$('grid_clients_found').attachEvent('onValueSuggest', grid_clients_found_click)
$$('search_product_id').attachEvent('onKeyPress', search_product_id_key_press)
$$('grid_products_found').attachEvent('onValueSuggest', grid_products_found_click)
$$('grid_details').attachEvent('onItemClick', grid_details_click)
$$('grid_details').attachEvent('onHeaderClick', grid_details_header_click)
$$('grid_details').attachEvent('onBeforeEditStart', grid_details_before_edit_start)
$$('grid_details').attachEvent('onBeforeEditStop', grid_details_before_edit_stop)
$$('cmd_invoice_timbrar').attachEvent('onItemClick', cmd_invoice_timbrar_click)
$$('cmd_invoice_cancelar').attachEvent('onItemClick', cmd_invoice_cancelar_click)
$$('grid_invoices').attachEvent('onItemClick', grid_invoices_click)
} }
} }
function get_uso_cfdi_to_table(args){ function get_uso_cfdi_to_table(){
webix.ajax().sync().get('/values/usocfdi', args, function(text, data){ webix.ajax().sync().get('/values/usocfdi', function(text, data){
var values = data.json() var values = data.json()
table_usocfdi.clear() table_usocfdi.clear()
table_usocfdi.insert(values) table_usocfdi.insert(values)
@ -60,52 +90,18 @@ function get_uso_cfdi_to_table(args){
function get_partners(){ function get_partners(){
webix.ajax().get("/partners", {}, { webix.ajax().get('/partners', {}, {
error: function(text, data, xhr) { error: function(text, data, xhr) {
webix.message({ type:"error", text: "Error al consultar"}); msg_error('Error al consultar')
}, },
success: function(text, data, xhr) { success: function(text, data, xhr) {
var values = data.json(); var values = data.json();
$$("grid_partners").clearAll(); $$('grid_partners').clearAll();
if (values.ok){ if (values.data){
$$("grid_partners").parse(values.rows, 'json'); $$('grid_partners').parse(values.data, 'json');
}; };
} }
}); })
}
function get_products(){
var grid = $$('grid_products')
webix.ajax().get('/products', {}, {
error: function(text, data, xhr) {
webix.message({type: 'error', text: 'Error al consultar'})
},
success: function(text, data, xhr) {
var values = data.json();
grid.clearAll();
if (values.ok){
grid.parse(values.rows, 'json');
};
}
});
}
function get_invoices(){
var grid = $$('grid_invoices')
webix.ajax().get('/invoices', {}, {
error: function(text, data, xhr) {
webix.message({type: 'error', text: 'Error al consultar'})
},
success: function(text, data, xhr) {
var values = data.json();
grid.clearAll();
if (values.ok){
grid.parse(values.rows, 'json');
};
}
});
} }
@ -117,8 +113,41 @@ function menu_user_click(id, e, node){
} }
function current_dates(){
var fy = $$('filter_year')
var fm = $$('filter_month')
var fd = $$('filter_day')
var pfy = $$('prefilter_year')
var pfm = $$('prefilter_month')
var d = new Date()
fy.blockEvent()
fm.blockEvent()
fd.blockEvent()
pfy.blockEvent()
pfm.blockEvent()
fm.setValue(d.getMonth() + 1)
fd.setValue(d.getDate())
pfm.setValue(d.getMonth() + 1)
webix.ajax().sync().get('/values/filteryears', function(text, data){
var values = data.json()
fy.getList().parse(values[0])
pfy.getList().parse(values[1])
fy.setValue(d.getFullYear())
pfy.setValue(d.getFullYear())
})
fy.unblockEvent()
fm.unblockEvent()
fd.unblockEvent()
pfy.unblockEvent()
pfm.unblockEvent()
}
function multi_change(prevID, nextID){ function multi_change(prevID, nextID){
//~ webix.message(nextID)
if(nextID == 'app_partners'){ if(nextID == 'app_partners'){
active = $$('multi_partners').getActiveId() active = $$('multi_partners').getActiveId()
if(active == 'partners_home'){ if(active == 'partners_home'){
@ -135,10 +164,44 @@ function multi_change(prevID, nextID){
return return
} }
if(nextID == 'app_bancos'){
active = $$('multi_bancos').getActiveId()
if(active == 'banco_home'){
get_cuentas_banco()
}
return
}
if(nextID == 'app_school'){
active = $$('multi_school').getActiveId()
if(active == 'school_home'){
init_config_school()
}
return
}
if(nextID == 'app_nomina'){
active = $$('multi_nomina').getActiveId()
if(active == 'nomina_home'){
default_config_nomina()
}
return
}
if(nextID == 'app_tickets'){
active = $$('multi_tickets').getActiveId()
if(active == 'tickets_home'){
configuracion_inicial_ticket()
}
return
}
if(nextID == 'app_invoices'){ if(nextID == 'app_invoices'){
active = $$('multi_invoices').getActiveId() active = $$('multi_invoices').getActiveId()
if(active == 'invoices_home'){ if(active == 'invoices_home'){
current_dates()
get_invoices() get_invoices()
validar_timbrar()
} }
gi = $$('grid_invoices') gi = $$('grid_invoices')
return return
@ -156,3 +219,12 @@ function get_taxes(){
$$("grid_product_taxes").parse(values, 'json') $$("grid_product_taxes").parse(values, 'json')
}) })
} }
function cmd_update_timbres_click(){
webix.ajax().get('/values/timbres', function(text, data){
var value = data.json()
$$('cmd_update_timbres').define('badge', value)
$$('cmd_update_timbres').refresh()
})
}

View File

@ -0,0 +1,627 @@
var query = []
var cfg_nomina = new Object()
var nomina_controllers = {
init: function(){
$$('cmd_nomina_import').attachEvent('onItemClick', cmd_nomina_import_click)
$$('cmd_empleados').attachEvent('onItemClick', cmd_empleados_click)
$$('cmd_close_empleados').attachEvent('onItemClick', cmd_close_empleados_click)
$$('cmd_delete_empleado').attachEvent('onItemClick', cmd_delete_empleado_click)
$$('cmd_import_empleados').attachEvent('onItemClick', cmd_import_empleados_click)
$$('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)
$$('grid_nomina').attachEvent('onItemClick', grid_nomina_click)
$$('filter_year_nomina').attachEvent('onChange', filter_year_nomina_change)
$$('filter_month_nomina').attachEvent('onChange', filter_month_nomina_change)
$$('filter_dates_nomina').attachEvent('onChange', filter_dates_nomina_change)
$$('grid_nomina').attachEvent('onSelectChange', grid_nomina_on_select_change)
webix.extend($$('grid_nomina'), webix.ProgressBar)
}
}
function default_config_nomina(){
current_dates_nomina()
get_nomina()
}
function current_dates_nomina(){
var fy = $$('filter_year_nomina')
var fm = $$('filter_month_nomina')
var d = new Date()
fy.blockEvent()
fm.blockEvent()
fm.setValue(d.getMonth() + 1)
webix.ajax().sync().get('/values/filteryearsnomina', function(text, data){
var values = data.json()
fy.getList().parse(values)
fy.setValue(d.getFullYear())
})
fy.unblockEvent()
fm.unblockEvent()
}
function get_nomina(filters){
var grid = $$('grid_nomina')
grid.showProgress({type: 'icon'})
webix.ajax().get('/nomina', filters, {
error: function(text, data, xhr) {
msg = 'Error al consultar'
msg_error(msg)
},
success: function(text, data, xhr) {
var values = data.json();
if (values.ok){
grid.clearAll();
grid.parse(values.rows, 'json');
}else{
msg_error(values.msg)
}
}
})
}
function cmd_nomina_import_click(){
win_import_nomina.init()
$$('win_import_nomina').show()
}
function cmd_import_template_nomina_click(){
var form = $$('form_upload_nomina')
var values = form.getValues()
if(!$$('lst_upload_nomina').count()){
$$('win_import_nomina').close()
return
}
if($$('lst_upload_nomina').count() > 1){
msg = 'Selecciona solo un archivo'
msg_error(msg)
return
}
var template = $$('up_nomina').files.getItem($$('up_nomina').files.getFirstId())
if(template.type.toLowerCase() != 'ods'){
msg = 'Archivo inválido.\n\nSe requiere un archivo ODS'
msg_error(msg)
return
}
msg = '¿Estás seguro de importar este archivo?'
webix.confirm({
title: 'Importar Nómina',
ok: 'Si',
cancel: 'No',
type: 'confirm-error',
text: msg,
callback:function(result){
if(result){
$$('up_nomina').send()
}
}
})
}
function up_nomina_upload_complete(response){
if(response.status != 'server'){
msg = 'Ocurrio un error al subir el archivo'
msg_error(msg)
return
}
msg = 'Archivo subido correctamente.\n\nComenzando importación.'
msg_ok(msg)
$$('win_import_nomina').close()
webix.ajax().get('/nomina', {opt: 'import'}, {
error: function(text, data, xhr) {
msg = 'Error al importar'
msg_error(msg)
},
success: function(text, data, xhr) {
var values = data.json();
if (values.ok){
msg_ok(values.msg)
get_nomina()
}else{
msg_error(values.msg)
}
}
})
}
function get_employees(){
webix.ajax().get('/employees', {
error: function(text, data, xhr) {
msg = 'Error al consultar'
msg_error(msg)
},
success: function(text, data, xhr) {
var values = data.json();
if (values.ok){
$$('grid_employees').clearAll();
$$('grid_employees').parse(values.rows, 'json');
}else{
msg_error(values.msg)
}
}
})
}
function cmd_empleados_click(){
get_employees()
$$('multi_nomina').setValue('nomina_empleados')
}
function cmd_close_empleados_click(){
$$('multi_nomina').setValue('nomina_home')
}
function cmd_import_empleados_click(){
win_import_employees.init()
$$('win_import_employees').show()
}
function cmd_import_employees_click(){
var form = $$('form_upload_employees')
var values = form.getValues()
if(!$$('lst_upload_employees').count()){
$$('win_import_employees').close()
return
}
if($$('lst_upload_employees').count() > 1){
msg = 'Selecciona solo un archivo'
msg_error(msg)
return
}
var template = $$('up_employees').files.getItem($$('up_employees').files.getFirstId())
if(template.type.toLowerCase() != 'ods'){
msg = 'Archivo inválido.\n\nSe requiere un archivo ODS'
msg_error(msg)
return
}
msg = '¿Estás seguro de importar este archivo?'
webix.confirm({
title: 'Importar Empleados',
ok: 'Si',
cancel: 'No',
type: 'confirm-error',
text: msg,
callback:function(result){
if(result){
$$('up_employees').send()
}
}
})
}
function up_employees_upload_complete(response){
if(response.status != 'server'){
msg = 'Ocurrio un error al subir el archivo'
msg_error(msg)
return
}
msg = 'Archivo subido correctamente.\n\nComenzando importación.'
msg_ok(msg)
$$('win_import_employees').close()
webix.ajax().get('/employees', {opt: 'import'}, {
error: function(text, data, xhr) {
msg = 'Error al importar'
msg_error(msg)
},
success: function(text, data, xhr) {
var values = data.json();
if (values.ok){
msg_ok(values.msg)
get_employees()
}else{
msg_error(values.msg)
}
}
})
}
function delete_empleado(id){
webix.ajax().del('/employees', {id: id}, function(text, xml, xhr){
var msg = 'Empleado eliminado correctamente'
if(xhr.status == 200){
$$('grid_employees').remove(id);
msg_ok(msg)
}else{
msg = 'El Empleado tiene recibos timbrados'
msg_error(msg)
}
})
}
function cmd_delete_empleado_click(){
var row = $$('grid_employees').getSelectedItem()
if (row == undefined){
msg = 'Selecciona un Empleado'
msg_error(msg)
return
}
msg = '¿Estás seguro de eliminar al Empleado?<BR><BR>'
msg += row['nombre_completo'] + ' (' + row['rfc'] + ')'
msg += '<BR><BR>ESTA ACCIÓN NO SE PUEDE DESHACER<BR><BR>'
webix.confirm({
title: 'Eliminar Empleado',
ok: 'Si',
cancel: 'No',
type: 'confirm-error',
text: msg,
callback:function(result){
if (result){
delete_empleado(row['id'])
}
}
})
}
function cmd_nomina_without_stamp_click(){
get_nomina()
}
function cmd_nomina_delete_click(){
var rows = $$('grid_nomina').getSelectedItem()
if (rows == undefined){
msg = 'Selecciona al menos un registro'
msg_error(msg)
return
}
var ids = []
if(Array.isArray(rows)){
for(var i in rows){
ids.push(rows[i].id)
}
}else{
ids.push(rows.id)
}
msg = '¿Estás seguro de eliminar los recibos seleccionado?<BR><BR>'
msg += 'ESTA ACCIÓN NO SE PUEDE DESHACER<BR><BR>'
msg += 'Solo se eliminan recibos no timbrados'
webix.confirm({
title: 'Eliminar Nomina',
ok: 'Si',
cancel: 'No',
type: 'confirm-error',
text: msg,
callback:function(result){
if (result){
delete_nomina(ids)
}
}
})
}
function delete_nomina(ids){
webix.ajax().del('/nomina', {id: ids}, function(text, xml, xhr){
var msg = 'Registros eliminados correctamente'
if (xhr.status == 200){
get_nomina()
msg_ok(msg)
} else {
msg = 'No se pudo eliminar.'
msg_error(msg)
}
})
}
function cmd_nomina_timbrar_click(){
get_nomina()
msg = 'Se enviarán a timbrar todos los recibos sin timbrar<BR><BR>'
msg += '¿Estás seguro de continuar?<BR><BR>'
webix.confirm({
title: 'Enviar a timbrar',
ok: 'Si',
cancel: 'No',
type: 'confirm-error',
text: msg,
callback:function(result){
if (result){
timbrar_nomina()
}
}
})
}
function timbrar_nomina(){
webix.ajax().get('/nomina', {opt: 'stamp'}, {
error: function(text, data, xhr) {
msg = 'Error al timbrar'
msg_error(msg)
},
success: function(text, data, xhr) {
var values = data.json();
if(values.ok){
cmd_update_timbres_click()
get_nomina()
msg_ok(values.msg_ok)
}
if(values.error){
webix.alert({
title: 'Error al Timbrar',
text: values.msg_error,
type: 'alert-error'
})
}
}
})
}
function send_mail(row){
if(!row.uuid){
msg_error('La nómina no esta timbrada')
return
}
var data = {'opt': 'send_mail', 'id': row.id}
msg = '¿Estás seguro de enviar por correo este Recibo de Nómina?'
webix.confirm({
title: 'Enviar Recibo de Nómina',
ok: 'Si',
cancel: 'No',
type: 'confirm-error',
text: msg,
callback:function(result){
if(result){
webix.ajax().post('/nomina', data, {
error:function(text, data, XmlHttpRequest){
msg = 'Ocurrio un error, consulta a soporte técnico'
msg_error(msg)
},
success:function(text, data, XmlHttpRequest){
values = data.json();
if(values.ok){
msg_ok(values.msg)
}else{
msg_error(values.msg)
}
}
})
}
}
})
}
function grid_nomina_click(id, e, node){
var row = this.getItem(id)
if(id.column == 'xml'){
location = '/doc/nomxml/' + row.id
}else if(id.column == 'pdf'){
window.open('/doc/nompdf/' + row.id, '_blank')
}else if(id.column == 'email'){
send_mail(row)
}
}
function filter_year_nomina_change(nv, ov){
var fm = $$('filter_month_nomina')
filters = {'opt': 'yearmonth', 'year': nv, 'month': fm.getValue()}
get_nomina(filters)
}
function filter_month_nomina_change(nv, ov){
var fy = $$('filter_year_nomina')
filters = {'opt': 'yearmonth', 'year': fy.getValue(), 'month': nv}
get_nomina(filters)
}
function filter_dates_nomina_change(range){
if(range.start != null && range.end != null){
filters = {'opt': 'dates', 'range': range}
get_nomina(filters)
}
}
function cmd_nomina_cancel_click(){
var row = $$('grid_nomina').getSelectedItem()
if(row == undefined){
msg = 'Selecciona un registro'
msg_error(msg)
return
}
if(Array.isArray(row)){
msg = 'Selecciona solo un registro'
msg_error(msg)
return
}
if(row['estatus'] != 'Timbrado'){
msg = 'Solo se pueden cancelar recibos timbrados'
msg_error(msg)
return
}
win_invoice_cancel_nomina.init()
$$('win_invoice_cancel_nomina').show()
}
function cmd_win_cancel_nomina_close_click(){
$$('win_invoice_cancel_nomina').close()
}
function cmd_invoice_cancel_nomina_click(){
var reason = $$('lst_reasons_cancel').getValue()
var uuid = $$('txt_cancel_uuid').getValue()
if(!reason){
msg = 'Selecciona un motivo para esta cancelación'
msg_error(msg)
return
}
if(reason=='01' & !uuid){
msg = 'Debes de capturar el UUID que reemplaza a este CFDI'
msg_error(msg)
return
}
$$('win_invoice_cancel_nomina').close()
send_cancel_nomina(reason, uuid)
}
function send_cancel_nomina(reason, uuid){
var grid = $$('grid_nomina')
var row = grid.getSelectedItem()
var data = new Object()
data['opt'] = 'cancel'
data['id'] = row.id
data['args'] = {reason: reason, uuid: uuid}
webix.ajax().sync().post('nomina', data, {
error:function(text, data, XmlHttpRequest){
msg = 'Ocurrio un error, consulta a soporte técnico'
msg_error(msg)
},
success:function(text, data, XmlHttpRequest){
values = data.json();
if(values.ok){
grid.updateItem(row.id, values.row)
msg_ok(values.msg)
}else{
msg_error(values.msg)
}
}
})
}
function cmd_nomina_log_click(){
location = '/doc/nomlog/0'
}
function cmd_nomina_download_click(){
var grid = $$('grid_nomina')
if(!grid.count()){
msg = 'Sin documentos a descargar'
msg_error(msg)
return
}
var ids = []
grid.eachRow(function(row){
var r = grid.getItem(row)
ids.push(r.id)
})
var filters = {'by': 'download', 'ids': ids}
webix.ajax().response('blob').get('/nomina', filters, function(text, data){
webix.html.download(data, 'nomina.zip');
});
}
function grid_nomina_on_select_change(){
var g = $$('grid_nomina')
var rows = g.getSelectedItem(true)
var total = 0
for (i = 0; i < rows.length; i++) {
if(typeof(rows[i].total) == 'string'){
total += rows[i].total.to_float()
}else{
total += rows[i].total
}
}
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,16 +1,101 @@
//~ Empresa Libre
//~ Copyright (C) 2016-2018 Mauricio Baeza Servin (web@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
//~ the Free Software Foundation, either version 3 of the License, or
//~ (at your option) any later version.
//~
//~ This program is distributed in the hope that it will be useful,
//~ but WITHOUT ANY WARRANTY; without even the implied warranty of
//~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
//~ GNU General Public License for more details.
//~
//~ You should have received a copy of the GNU General Public License
//~ along with this program. If not, see <http://www.gnu.org/licenses/>.
var cfg_partners = new Object()
var partners_controllers = {
init: function(){
$$('cmd_new_partner').attachEvent('onItemClick', cmd_new_partner_click);
//~ $$('cmd_new_contact').attachEvent('onItemClick', cmd_new_contact_click);
$$('cmd_edit_partner').attachEvent('onItemClick', cmd_edit_partner_click);
$$('cmd_delete_partner').attachEvent('onItemClick', cmd_delete_partner_click);
$$('cmd_save_partner').attachEvent('onItemClick', cmd_save_partner_click);
$$('cmd_cancel_partner').attachEvent('onItemClick', cmd_cancel_partner_click);
//~ $$('cmd_cancel_contact').attachEvent('onItemClick', cmd_cancel_contact_click);
//~ $$('cmd_partner_zero').attachEvent('onItemClick', cmd_partner_zero_click);
$$('codigo_postal').attachEvent('onKeyPress', postal_code_key_press);
$$('codigo_postal').attachEvent('onTimedKeyPress', postal_code_key_up);
$$('colonia').attachEvent('onFocus', colonia_on_focus)
$$("tipo_persona").attachEvent( "onChange", opt_tipo_change)
$$("es_cliente").attachEvent( "onChange", is_client_change)
$$("es_proveedor").attachEvent( "onChange", is_supplier_change)
$$("rfc").attachEvent( "onBlur", rfc_lost_focus)
$$('multi').attachEvent('onViewChange', multi_change)
$$('grid_partners').attachEvent('onItemDblClick', grid_partners_double_click)
//~ $$('grid_partners').attachEvent('onSelectChange', grid_partners_on_select_change)
$$('partner_balance').attachEvent('onChange', partner_balance_on_change)
$$('cmd_partner_add_account_bank').attachEvent('onItemClick', cmd_partner_add_account_bank_click)
$$('grid_partner_account_bank').attachEvent('onItemClick', grid_partner_account_bank_click)
default_config_partners()
}
}
function default_config_partners(){
webix.ajax().get('/config', {'fields': 'partners'}, {
error: function(text, data, xhr) {
msg = 'Error al consultar'
msg_error(msg)
},
success: function(text, data, xhr) {
var values = data.json()
cfg_partners = values
//~ show('cmd_partner_zero', cfg_partners['chk_config_change_balance_partner'])
}
})
}
function get_condicion_pago(){
webix.ajax().get('/values/condicionespago', {
error: function(text, data, xhr) {
},
success: function(text, data, xhr) {
var values = data.json();
$$('condicion_pago').define('suggest', values)
$$('condicion_pago').refresh()
}
})
}
function cmd_new_partner_click(id, e, node){ function cmd_new_partner_click(id, e, node){
$$('form_partner').clearValidation()
$$('form_partner_account_bank').clearValidation()
$$('form_partner').setValues({ $$('form_partner').setValues({
id: 0, pais: 'México', tipo_persona: 1, es_activo: true}) id: 0, pais: 'México', tipo_persona: 1, es_activo: true,
partner_balance: 0.00})
$$('forma_pago').getList().load('/values/formapago') $$('forma_pago').getList().load('/values/formapago')
get_condicion_pago()
$$('grid_partners').clearSelection() $$('grid_partners').clearSelection()
$$('multi_partners').setValue('partners_new') $$('multi_partners').setValue('partners_new')
$$('tab_partner').setValue('Datos Fiscales') $$('tab_partner').setValue('Datos Fiscales')
get_uso_cfdi_to_table({}) get_uso_cfdi_to_table()
query = table_usocfdi.chain().find({fisica: true}).data() query = table_usocfdi.chain().find({fisica: true}).data()
$$('lst_uso_cfdi_socio').getList().parse(query) $$('lst_uso_cfdi_socio').getList().parse(query)
$$('partner_balance').define('readonly', !cfg_partners['chk_config_change_balance_partner'])
get_partner_banks()
get_partner_accounts_bank(0)
get_sat_regimenes()
} }
@ -20,30 +105,39 @@ function cmd_new_contact_click(id, e, node){
} }
function cmd_edit_partner_click(id, e, node){ function cmd_edit_partner_click(){
var msg = '' var msg = ''
var row = $$('grid_partners').getSelectedItem() var row = $$('grid_partners').getSelectedItem()
$$('form_partner_account_bank').clearValidation()
if (row == undefined){ if (row == undefined){
msg = 'Selecciona un Socio de Negocio' msg = 'Selecciona un Socio de Negocio'
webix.message({type:'error', text: msg}) msg_error(msg)
return return
} }
get_condicion_pago()
webix.ajax().get("/partners", {id: row['id']}, { webix.ajax().get("/partners", {id: row['id']}, {
error: function(text, data, xhr) { error: function(text, data, xhr) {
webix.message({type:"error", text: "Error al consultar"}) msg_error()
}, },
success: function(text, data, xhr){ success: function(text, data, xhr){
var values = data.json() var values = data.json()
$$('form_partner').clearValidation()
$$('form_partner').setValues(values) $$('form_partner').setValues(values)
$$('forma_pago').getList().load('/values/formapago') $$('forma_pago').getList().load('/values/formapago')
get_uso_cfdi_to_table({}) $$('partner_balance').define('readonly', !cfg_partners['chk_config_change_balance_partner'])
get_uso_cfdi_to_table()
if(values.tipo_persona == 1){ if(values.tipo_persona == 1){
query = table_usocfdi.chain().find({fisica: true}).data() query = table_usocfdi.chain().find({fisica: true}).data()
get_sat_regimenes()
}else if(values.tipo_persona == 2){ }else if(values.tipo_persona == 2){
query = table_usocfdi.chain().find({moral: true}).data() query = table_usocfdi.chain().find({moral: true}).data()
get_sat_regimenes(true)
}else{ }else{
query = [{id: 'P01', value: 'Por definir'}] query = [{id: 'P01', value: 'Por definir'}]
} }
@ -54,12 +148,17 @@ function cmd_edit_partner_click(id, e, node){
if(values.es_proveedor){ if(values.es_proveedor){
$$('cuenta_proveedor').enable() $$('cuenta_proveedor').enable()
} }
get_partner_accounts_bank(row['id'])
pause(250)
$$('lst_receptor_regimenes_fiscales').select(values.regimenes)
} }
}) })
$$('multi_partners').setValue('partners_new') $$('multi_partners').setValue('partners_new')
$$('tab_partner').setValue('Datos Fiscales') $$('tab_partner').setValue('Datos Fiscales')
}; get_partner_banks()
}
function cmd_delete_partner_click(id, e, node){ function cmd_delete_partner_click(id, e, node){
@ -68,7 +167,7 @@ function cmd_delete_partner_click(id, e, node){
if (row == undefined){ if (row == undefined){
msg = 'Selecciona un Cliente o Proveedor' msg = 'Selecciona un Cliente o Proveedor'
webix.message({type:'error', text: msg}) msg_error(msg)
return return
} }
@ -97,10 +196,10 @@ function delete_partner(id){
var msg = 'Socio eliminado correctamente' var msg = 'Socio eliminado correctamente'
if (xhr.status == 200){ if (xhr.status == 200){
$$('grid_partners').remove(id); $$('grid_partners').remove(id);
webix.message({type: 'success', text: msg}) msg_ok(msg)
} else { } else {
msg = 'No se pudo eliminar. Asegurate de que no tenga documentos relacionados' msg = 'No se pudo eliminar. Asegurate de que no tenga documentos relacionados'
webix.message({type: 'error', text: msg}) msg_error(msg)
} }
}) })
} }
@ -111,18 +210,27 @@ function cmd_save_partner_click(id, e, node){
var form = this.getFormView(); var form = this.getFormView();
if (!form.validate()) { if (!form.validate()) {
webix.message({type: 'error', text: msg}) msg_error(msg)
return return
} }
var values = form.getValues(); var values = form.getValues();
if(values.codigo_postal && values.codigo_postal.length != 5){ if(!values.rfc){
msg = 'Longitud inválida del C.P.' msg = 'Captura el RFC'
msg_error(msg) msg_error(msg)
$$('tab_partner').setValue('Datos Fiscales')
return return
} }
if(values.tipo_persona != 4){
if(values.codigo_postal && values.codigo_postal.length != 5){
msg = 'Longitud inválida del C.P.'
msg_error(msg)
return
}
}
if (!values.es_cliente && !values.es_proveedor){ if (!values.es_cliente && !values.es_proveedor){
msg = 'Selecciona si es cliente, proveedor o ambos' msg = 'Selecciona si es cliente, proveedor o ambos'
msg_error(msg) msg_error(msg)
@ -130,17 +238,37 @@ function cmd_save_partner_click(id, e, node){
return return
} }
if(values.tipo_persona == 4){
if(values.pais && values.pais.length != 3){
msg = 'Longitud de país inválida'
msg_error(msg)
return
}
}
var ids_regimenes = $$('lst_receptor_regimenes_fiscales').getSelectedId()
if(values.tipo_persona < 3){
if(!ids_regimenes){
msg = 'Selecciona al menos un Regimen Fiscal'
msg_error(msg)
return
}
}
values['accounts'] = $$('grid_partner_account_bank').data.getRange()
values['regimenes'] = ids_regimenes
webix.ajax().post('/partners', values, { webix.ajax().post('/partners', values, {
error:function(text, data, XmlHttpRequest){ error:function(text, data, XmlHttpRequest){
msg = 'Ocurrio un error, consulta a soporte técnico'; msg = 'Ocurrio un error, consulta a soporte técnico';
webix.message({type:'error', text:msg}); msg_error(msg)
}, },
success:function(text, data, XmlHttpRequest){ success:function(text, data, XmlHttpRequest){
var values = data.json(); var values = data.json();
if (values.ok) { if (values.ok) {
update_grid_partner(values) update_grid_partner(values)
} else { } else {
webix.message({type:'error', text:values.msg}); msg_error(values.msg)
} }
} }
}) })
@ -158,7 +286,7 @@ function update_grid_partner(values){
$$("grid_partners").updateItem(values.row['id'], values.row) $$("grid_partners").updateItem(values.row['id'], values.row)
} }
$$('multi_partners').setValue('partners_home') $$('multi_partners').setValue('partners_home')
webix.message({type:'success', text: msg}) msg_ok(msg)
} }
@ -178,13 +306,13 @@ function postal_code_key_up(){
if( value.length == 5 ){ if( value.length == 5 ){
webix.ajax().get('/values/cp', {cp: value}, { webix.ajax().get('/values/cp', {cp: value}, {
error: function(text, data, xhr) { error: function(text, data, xhr) {
webix.message({type:'error', text:'Error al consultar el C.P.'}) msg_error('Error al consultar el C.P.')
}, },
success: function(text, data, xhr) { success: function(text, data, xhr) {
var values = data.json(); var values = data.json();
if (values.estado == undefined){ if (values.estado == undefined){
msg = 'No se encontró el C.P., asegurate de que sea correcto' msg = 'No se encontró el C.P., asegurate de que sea correcto'
webix.message({type:'error', text:msg}) msg_error(msg)
} else { } else {
$$('form_partner').setValues({ $$('form_partner').setValues({
estado: values.estado, estado: values.estado,
@ -229,20 +357,31 @@ function opt_tipo_change(new_value, old_value){
$$("nombre").define("value", "") $$("nombre").define("value", "")
$$("pais").define("readonly", true) $$("pais").define("readonly", true)
$$("pais").define("value", PAIS) $$("pais").define("value", PAIS)
$$('id_fiscal').define('value', '')
show('id_fiscal', new_value == 4)
$$('lst_receptor_regimenes_fiscales').clearAll()
var regimen_616 = {id: 11, value: '[616] Sin obligaciones fiscales'}
if (new_value == 1 || new_value == 2){ if (new_value == 1 || new_value == 2){
$$("rfc").define("value", ""); $$("rfc").define("value", "")
$$("rfc").define("readonly", false); $$("rfc").define("readonly", false)
moral = false
if(new_value == 2){
moral = true
}
get_sat_regimenes(moral)
} else if (new_value == 3) { } else if (new_value == 3) {
$$("rfc").define("value", RFC_PUBLICO); $$("rfc").define("value", RFC_PUBLICO)
$$("nombre").define("value", PUBLICO); $$("nombre").define("value", PUBLICO)
$$("rfc").define("readonly", true); $$("rfc").define("readonly", true)
$$('lst_receptor_regimenes_fiscales').parse(regimen_616)
} else if (new_value == 4) { } else if (new_value == 4) {
$$("rfc").define("value", RFC_EXTRANJERO); $$("rfc").define("value", RFC_EXTRANJERO)
$$("rfc").define("readonly", true); $$("rfc").define("readonly", true)
$$("pais").define("readonly", false); $$("pais").define("readonly", false)
$$("pais").define("value", ""); $$("pais").define("value", "")
$$('lst_receptor_regimenes_fiscales').parse(regimen_616)
} }
$$("nombre").refresh(); $$("nombre").refresh();
@ -260,10 +399,12 @@ function opt_tipo_change(new_value, old_value){
}else if (new_value == 2){ }else if (new_value == 2){
query = table_usocfdi.chain().find({moral: true}).data() query = table_usocfdi.chain().find({moral: true}).data()
}else{ }else{
query = [{id: 'P01', value: 'Por definir'}] query = [{id: 'S01', value: '[S01] Sin efectos fiscales. '}]
} }
$$('lst_uso_cfdi_socio').getList().parse(query) $$('lst_uso_cfdi_socio').getList().parse(query)
$$('lst_uso_cfdi_socio').refresh() $$('lst_uso_cfdi_socio').refresh()
} }
@ -280,9 +421,9 @@ function is_client_change(new_value, old_value){
function is_supplier_change(new_value, old_value){ function is_supplier_change(new_value, old_value){
var value = Boolean(new_value) var value = Boolean(new_value)
if (value){ if (value){
$$("cuenta_proveedor").enable(); $$("cuenta_proveedor").enable()
} else { } else {
$$("cuenta_proveedor").disable(); $$("cuenta_proveedor").disable()
} }
} }
@ -324,6 +465,204 @@ function rfc_lost_focus(prev_view){
function multi_partners_change(prevID, nextID){ function multi_partners_change(prevID, nextID){
webix.message(prevID) //~ webix.message(prevID)
webix.message(nextID) //~ webix.message(nextID)
} }
function grid_partners_double_click(id, e, node){
//~ if(id.column!='saldo_cliente'){
cmd_edit_partner_click()
//~ }
}
function partner_balance_on_change(new_value, old_value){
if(!isFinite(new_value)){
this.config.value = old_value
this.refresh()
}
}
function cmd_partner_add_account_bank_click(){
var form = $$('form_partner_account_bank')
if (!form.validate()){
msg = 'Valores inválidos'
msg_error(msg)
return
}
var values = form.getValues()
var id_partner = $$('form_partner').getValues().id
var account = {
id_partner: id_partner,
delete: '-',
banco: $$('lst_partner_bank').getText(),
cuenta: values.partner_account.trim(),
clabe: values.partner_clabe.trim(),
}
if(!account.cuenta){
msg = 'La cuenta es requerida'
msg_error(msg)
return
}
if(!account.cuenta.is_number()){
msg = 'Solo digitos en la cuenta'
msg_error(msg)
return
}
if(account.cuenta.length < 10){
msg = 'Longitud incorrecta de la cuenta'
msg_error(msg)
return
}
if(!account.clabe){
msg = 'La CLABE es requerida'
msg_error(msg)
return
}
if(account.clabe.length != 18){
msg = 'La CLABE debe ser de 18 digitos'
msg_error(msg)
return
}
if(!account.clabe.is_number()){
msg = 'Solo digitos en la CLABE'
msg_error(msg)
return
}
var grid = $$('grid_partner_account_bank')
if(id_partner){
partner_new_account_bank(account, grid)
}else{
grid.add(account)
}
form.setValues({})
}
function get_partner_banks(){
webix.ajax().get('/satbancos', {opt: 'active'}, function(text, data){
var values = data.json()
$$('lst_partner_bank').getList().parse(values)
})
}
function partner_new_account_bank(account, grid){
webix.ajax().post('/socioscb', {opt: 'new', values: account}, {
error: function(text, data, xhr) {
msg = 'Error al guardar'
msg_error(msg)
},
success: function(text, data, xhr) {
var values = data.json()
if(values.ok){
account['id'] = values.id
grid.add(account)
msg_ok(values.msg)
}else{
msg_error(values.msg)
}
}
})
}
function get_partner_accounts_bank(id_partner){
var grid = $$('grid_partner_account_bank')
grid.clearAll()
if(id_partner){
var data = {opt: 'by_partner', id_partner: id_partner}
webix.ajax().get('/socioscb', data, {
error: function(text, data, xhr) {
msg = 'Error al consultar'
msg_error(msg)
},
success: function(text, data, xhr) {
var values = data.json()
grid.parse(values)
}
})
}
}
function grid_partner_account_bank_click(id, e, node){
if(id.column != 'delete'){
return
}
var id_partner = $$('form_partner').getValues().id
var grid = $$('grid_partner_account_bank')
var msg = '¿Estás seguro de eliminar la cuenta de banco seleccionada?<BR><BR>'
msg += 'ESTA ACCION NO SE PUEDE DESHACER'
webix.confirm({
title: 'Eliminar cuenta de banco',
ok: 'Si',
cancel: 'No',
type: 'confirm-error',
text: msg,
callback:function(result){
if (result){
if(id_partner){
partner_delete_account_bank(id.row)
}else{
grid.remove(id.row)
}
}
}
})
}
function partner_delete_account_bank(row){
var grid = $$('grid_partner_account_bank')
webix.ajax().post('/socioscb', {opt: 'delete', values: {id: row}}, {
error: function(text, data, xhr) {
msg = 'Error al eliminar'
msg_error(msg)
},
success: function(text, data, xhr) {
var values = data.json()
if(values.ok){
grid.remove(row)
msg_ok(values.msg)
}else{
msg_error(values.msg)
}
}
})
}
function get_sat_regimenes(morales=false){
var data = {opt: 'actives', morales: morales}
webix.ajax().sync().get('/satregimenes', data, {
error: function(text, data, xhr) {
msg = 'Error al consultar'
msg_error(msg)
},
success: function(text, data, xhr) {
var values = data.json()
$$('lst_receptor_regimenes_fiscales').clearAll()
$$('lst_receptor_regimenes_fiscales').parse(values)
}
})
}

View File

@ -1,33 +1,141 @@
var cfg_products = new Object()
var gis_admin = false
function products_default_config(){
webix.ajax().get('/config', {'fields': 'main_products'}, {
error: function(text, data, xhr) {
msg = 'Error al consultar'
msg_error(msg)
},
success: function(text, data, xhr) {
var values = data.json()
cfg_products['inventario'] = values.chk_llevar_inventario
cfg_products['predial'] = values.chk_config_cuenta_predial
cfg_products['codigo_barras'] = values.chk_config_codigo_barras
cfg_products['con_impuestos'] = values.chk_config_precio_con_impuestos
cfg_products['default_unit'] = values.default_unidad
cfg_products['default_tax'] = values.default_tax
if(cfg_products['inventario']){
$$('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()
}
}
})
}
var products_controllers = {
init: function(){
$$('cmd_new_product').attachEvent('onItemClick', cmd_new_product_click)
$$("cmd_edit_product").attachEvent("onItemClick", cmd_edit_product_click)
$$("cmd_delete_product").attachEvent("onItemClick", cmd_delete_product_click)
$$("cmd_save_product").attachEvent("onItemClick", cmd_save_product_click)
$$("cmd_cancel_product").attachEvent("onItemClick", cmd_cancel_product_click)
$$("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)
$$('precio_con_impuestos').attachEvent('onTimedKeyPress', precio_con_impuestos_key_up);
$$("chk_inventario").attachEvent("onChange", chk_inventario_change)
$$('grid_products').attachEvent('onItemDblClick', cmd_edit_product_click)
products_default_config()
}
}
function configurar_producto(){
show('cuenta_predial', cfg_products['predial'])
show('codigo_barras', cfg_products['codigo_barras'])
show('precio_con_impuestos', cfg_products['con_impuestos'])
show('chk_inventario', cfg_products['inventario'])
show('txt_existencia', cfg_products['inventario'])
show('txt_minimo', cfg_products['inventario'])
}
function get_categorias(){
webix.ajax().sync().get('/values/categorias', function(text, data){
var values = data.json()
$$('categoria').getList().parse(values, 'plainjs')
})
}
function get_products(){
var grid = $$('grid_products')
webix.ajax().get('/products', {}, {
error: function(text, data, xhr) {
msg_error('Error al consultar')
},
success: function(text, data, xhr) {
var values = data.json();
grid.clearAll();
if (values.ok){
grid.parse(values.rows, 'json');
grid.refresh()
};
}
});
}
function cmd_new_product_click(id, e, node){ function cmd_new_product_click(id, e, node){
get_taxes()
$$('unidad').getList().load('/values/unidades')
configurar_producto()
$$('form_product').setValues({ $$('form_product').setValues({
id: 0, es_activo_producto: true}) id: 0, es_activo_producto: true})
add_config({'key': 'id_product', 'value': ''}) add_config({'key': 'id_product', 'value': ''})
get_new_key() get_new_key()
get_taxes() get_categorias()
$$('unidad').setValue(cfg_products['default_unit'])
$$('grid_product_taxes').select(cfg_products['default_tax'])
$$('grid_products').clearSelection() $$('grid_products').clearSelection()
$$('categoria').getList().load('/values/categorias')
$$('unidad').getList().load('/values/unidades')
$$("multi_products").setValue("product_new") $$("multi_products").setValue("product_new")
} }
function cmd_edit_product_click(id, e, node){ function cmd_edit_product_click(){
get_taxes()
$$('unidad').getList().load('/values/unidades')
configurar_producto()
var grid = $$('grid_products') var grid = $$('grid_products')
var row = grid.getSelectedItem() var row = grid.getSelectedItem()
if(row == undefined){ if(row == undefined){
webix.message({type: 'error', text: 'Selecciona un Producto'}) msg_error('Selecciona un Producto')
return return
} }
get_taxes()
$$('categoria').getList().load('/values/categorias') $$('categoria').getList().load('/values/categorias')
$$('unidad').getList().load('/values/unidades')
webix.ajax().get('/products', {id:row['id']}, { webix.ajax().get('/products', {id: row['id']}, {
error: function(text, data, xhr) { error: function(text, data, xhr) {
webix.message({type: 'error', text: 'Error al consultar'}) msg_error(text)
}, },
success: function(text, data, xhr){ success: function(text, data, xhr){
var values = data.json() var values = data.json()
@ -48,10 +156,10 @@ function delete_product(id){
var msg = 'Producto eliminado correctamente' var msg = 'Producto eliminado correctamente'
if(xhr.status == 200){ if(xhr.status == 200){
$$('grid_products').remove(id) $$('grid_products').remove(id)
webix.message({type:'success', text:msg}) msg_ok(msg)
}else{ }else{
msg = 'No se pudo eliminar' msg = 'No se pudo eliminar'
webix.message({type:'error', text:msg}) msg_error(msg)
} }
}) })
} }
@ -60,7 +168,7 @@ function delete_product(id){
function cmd_delete_product_click(id, e, node){ function cmd_delete_product_click(id, e, node){
var row = $$('grid_products').getSelectedItem() var row = $$('grid_products').getSelectedItem()
if (row == undefined){ if (row == undefined){
webix.message({type:'error', text: 'Selecciona un Producto'}) msg_error('Selecciona un Producto')
return return
} }
@ -108,8 +216,9 @@ function update_grid_products(values){
msg = 'Producto actualizado correctamente' msg = 'Producto actualizado correctamente'
$$("grid_products").updateItem(values.row['id'], values.row) $$("grid_products").updateItem(values.row['id'], values.row)
} }
$$('grid_products').refresh()
$$('multi_products').setValue('products_home') $$('multi_products').setValue('products_home')
webix.message({type: 'success', text: msg}) msg_ok(msg)
} }
@ -118,35 +227,48 @@ function cmd_save_product_click(id, e, node){
var form = this.getFormView() var form = this.getFormView()
if(!form.validate()){ if(!form.validate()){
webix.message({type: 'error', text: 'Valores inválidos'}) msg_error('Valores inválidos')
return return
} }
var rows = $$('grid_product_taxes').getSelectedId(true, true) var rows = $$('grid_product_taxes').getSelectedId(true, true)
if (rows.length == 0){
webix.message({type: 'error', text: 'Selecciona un impuesto'})
return
}
var values = form.getValues(); var values = form.getValues();
if (!validate_sat_key_product(values.clave_sat, false)){ if(!validate_sat_key_product(values.clave_sat, false)){
webix.message({ type:'error', text:'La clave SAT no existe' }) msg_error('La clave SAT no existe')
return
}
if(values.descripcion.length > 1000){
msg_error('Descripción con ' + values.descripcion.length + 'caracteres, captura solo 1000 caracteres')
return
}
if(values['objeto_impuesto']=='01' && rows.length > 0){
msg = 'Si Objeto de Impuestos = 01, no debes seleccionar ningún impuesto'
msg_error(msg)
return
}
if(values['objeto_impuesto']=='02' && rows.length == 0){
msg = 'Si Objeto de Impuestos = 02, debes de seleccionar al menos un impuesto'
msg_error(msg)
return return
} }
values['taxes'] = JSON.stringify(rows) values['taxes'] = JSON.stringify(rows)
webix.ajax().sync().post('products', values, { webix.ajax().sync().post('products', values, {
error:function(text, data, XmlHttpRequest){ error:function(text, data, XmlHttpRequest){
msg = 'Ocurrio un error, consulta a soporte técnico' msg = 'Ocurrio un error, consulta a soporte técnico'
webix.message({type: 'error', text: msg}) msg_error(msg)
}, },
success:function(text, data, XmlHttpRequest){ success:function(text, data, XmlHttpRequest){
var values = data.json(); var values = data.json();
if (values.ok) { if (values.ok) {
update_grid_products(values) update_grid_products(values)
}else{ }else{
webix.message({type:'error', text:values.msg}) msg_error(values.msg)
} }
} }
}) })
@ -154,9 +276,7 @@ function cmd_save_product_click(id, e, node){
function cmd_cancel_product_click(id, e, node){ function cmd_cancel_product_click(id, e, node){
$$("multi_products").setValue("products_home") $$("multi_products").setValue("products_home")
}; };
@ -181,10 +301,24 @@ function chk_automatica_change(new_value, old_value){
} }
function chk_inventario_change(new_value, old_value){
var value = Boolean(new_value)
if(value){
$$('txt_existencia').enable()
$$('txt_minimo').enable()
}else{
$$('txt_existencia').disable()
$$('txt_minimo').disable()
$$('txt_existencia').setValue(0)
$$('txt_minimo').setValue(0)
}
}
function get_new_key(){ function get_new_key(){
webix.ajax().get('/values/newkey', { webix.ajax().get('/values/newkey', {
error: function(text, data, xhr) { error: function(text, data, xhr) {
webix.message({type:'error', text: text}) msg_error(text)
}, },
success: function(text, data, xhr) { success: function(text, data, xhr) {
var values = data.json(); var values = data.json();
@ -201,3 +335,657 @@ function valor_unitario_change(new_value, old_value){
this.refresh() this.refresh()
} }
} }
function precio_con_impuestos_change(new_value, old_value){
if(!isFinite(new_value)){
this.config.value = old_value
this.refresh()
}
}
function calcular_sin_impuestos(value, taxes){
var vu = $$('valor_unitario')
var precio = value
taxes.forEach(function(tax){
var tasa = 1.00 + tax.tasa.to_float()
if(tax.tipo == 'T' && tax.name == 'IVA'){
precio = (value / tasa).round(DECIMALES)
}
})
vu.setValue(precio)
}
function precio_con_impuestos_key_up(){
var value = this.getValue()
if(!value){
return
}
var taxes = $$('grid_product_taxes').getSelectedItem(true)
if (taxes.length == 0){
msg = 'Selecciona al menos un impuesto'
msg_error(msg)
return
}
if(!isFinite(value)){
msg = 'Captura un valor válido'
msg_error(msg)
return
}
calcular_sin_impuestos(parseFloat(value), taxes)
}
function cmd_import_products_click(){
win_import_products.init()
$$('win_import_products').show()
}
function cmd_upload_products_click(){
var form = $$('form_upload_products')
var values = form.getValues()
if(!$$('lst_upload_products').count()){
$$('win_import_products').close()
return
}
if($$('lst_upload_products').count() > 1){
msg = 'Selecciona solo un archivo'
msg_error(msg)
return
}
var template = $$('up_products').files.getItem($$('up_products').files.getFirstId())
if(template.type.toLowerCase() != 'ods'){
msg = 'Archivo inválido.\n\nSe requiere un archivo ODS'
msg_error(msg)
return
}
msg = '¿Estás seguro de importar este archivo?'
webix.confirm({
title: 'Importar Productos',
ok: 'Si',
cancel: 'No',
type: 'confirm-error',
text: msg,
callback:function(result){
if(result){
$$('up_products').send()
}
}
})
}
function up_products_upload_complete(response){
if(response.status != 'server'){
msg = 'Ocurrio un error al subir el archivo'
msg_error(msg)
return
}
msg = 'Archivo subido correctamente.\n\nComenzando importación.'
msg_ok(msg)
$$('win_import_products').close()
webix.ajax().post('/products', {opt: 'import'}, {
error: function(text, data, xhr) {
msg = 'Error al importar'
msg_error(msg)
},
success: function(text, data, xhr) {
var values = data.json();
if (values.ok){
get_products()
webix.alert({
title: 'Importación terminada',
text: values.msg,
})
}else{
msg_error(values.msg)
}
}
})
}
//~ 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

@ -0,0 +1,155 @@
var controllers_school = {
init: function(){
$$('cmd_new_student').attachEvent('onItemClick', cmd_new_student_click)
$$('cmd_edit_student').attachEvent('onItemClick', cmd_edit_student_click)
$$('cmd_delete_student').attachEvent('onItemClick', cmd_delete_student_click)
$$('cmd_save_student').attachEvent('onItemClick', cmd_save_student_click)
$$('cmd_cancel_student').attachEvent('onItemClick', cmd_cancel_student_click)
}
}
function get_school_groups(){
webix.ajax().get('/values/schoolgroups', {
error: function(text, data, xhr) {
},
success: function(text, data, xhr) {
var values = data.json();
$$('student_grupo').define('suggest', values)
$$('student_grupo').refresh()
}
})
}
function init_config_school(){
get_students()
get_school_groups()
}
function cmd_new_student_click(){
$$('form_student').setValues({})
$$('grid_students').clearSelection()
$$('multi_school').setValue('new_student')
}
function cmd_edit_student_click(){
var row = $$('grid_students').getSelectedItem()
if (row == undefined){
msg = 'Selecciona un Alumno'
msg_error(msg)
return
}
webix.ajax().get('/students', {id: row['id']}, {
error: function(text, data, xhr) {
msg_error()
},
success: function(text, data, xhr){
var values = data.json()
$$('form_student').setValues(values)
}
})
$$('multi_school').setValue('new_student')
}
function delete_student(id){
webix.ajax().del('/students', {id: id}, function(text, xml, xhr){
msg = 'Alumno eliminado correctamente'
if (xhr.status == 200){
$$('grid_students').remove(id);
msg_ok(msg)
} else {
msg = 'No se pudo eliminar.'
msg_error(msg)
}
})
}
function cmd_delete_student_click(){
var row = $$('grid_students').getSelectedItem()
if (row == undefined){
msg = 'Selecciona un Alumno'
msg_error(msg)
return
}
msg = '¿Estás seguro de eliminar al Alumno?<BR><BR>'
msg += row['nombre'] + ' ' + row['paterno'] + ' (' + row['rfc'] + ')'
msg += '<BR><BR>ESTA ACCIÓN NO SE PUEDE DESHACER<BR><BR>'
webix.confirm({
title:'Eliminar Alumno',
ok:'Si',
cancel:'No',
type:'confirm-error',
text:msg,
callback:function(result){
if (result){
delete_student(row['id'])
}
}
})
}
function cmd_cancel_student_click(){
$$('multi_school').setValue('school_home')
}
function cmd_save_student_click(){
msg = ''
var form = this.getFormView();
if (!form.validate()) {
msg_error(msg)
return
}
var values = form.getValues();
opt = 'add'
if(values.id){
opt = 'edit'
}
webix.ajax().post('/students', {opt: opt, values: 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) {
form.setValues({})
$$('multi_school').setValue('school_home')
get_students()
} else {
msg_error(values.msg)
}
}
})
}
function get_students(){
webix.ajax().get('/students', {}, {
error: function(text, data, xhr) {
msg_error('Error al consultar')
},
success: function(text, data, xhr) {
var values = data.json()
$$('grid_students').clearAll()
$$('grid_students').parse(values)
}
})
}

View File

@ -0,0 +1,903 @@
var query = []
var msg = ''
var cfg_ticket = new Object()
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)
$$('cmd_generar_ticket').attachEvent('onItemClick', cmd_generar_ticket_click)
$$('cmd_cerrar_ticket').attachEvent('onItemClick', cmd_cerrar_ticket_click)
$$('cmd_new_invoice_from_ticket').attachEvent('onItemClick', cmd_new_invoice_from_ticket_click)
$$('cmd_close_ticket_invoice').attachEvent('onItemClick', cmd_cerrar_ticket_click)
$$('cmd_cancelar_ticket').attachEvent('onItemClick', cmd_cancelar_ticket_click)
$$('cmd_move_tickets_right').attachEvent('onItemClick', cmd_move_tickets_right_click)
$$('cmd_move_tickets_left').attachEvent('onItemClick', cmd_move_tickets_left_click)
$$('cmd_ticket_notes').attachEvent('onItemClick', cmd_ticket_notes_click)
$$('tsearch_product_key').attachEvent('onKeyPress', tsearch_product_key_press)
$$('grid_tickets').attachEvent('onItemClick', grid_tickets_click)
$$('grid_tdetails').attachEvent('onItemClick', grid_ticket_details_click)
$$('grid_tdetails').attachEvent('onBeforeEditStop', grid_tickets_details_before_edit_stop)
$$('gt_productos_found').attachEvent('onValueSuggest', gt_productos_found_click)
$$('cmd_ticket_filter_today').attachEvent('onItemClick', cmd_ticket_filter_today_click)
$$('filter_year_ticket').attachEvent('onChange', filter_year_ticket_change)
$$('filter_month_ticket').attachEvent('onChange', filter_month_ticket_change)
$$('filter_dates_ticket').attachEvent('onChange', filter_dates_ticket_change)
$$('chk_is_invoice_day').attachEvent('onChange', chk_is_invoice_day_change)
$$('grid_tickets_active').attachEvent('onItemDblClick', grid_tickets_active_double_click)
$$('grid_tickets_invoice').attachEvent('onItemDblClick', grid_tickets_invoice_double_click)
$$('tsearch_client_key').attachEvent('onKeyPress', tsearch_client_key_press)
$$('grid_ticket_clients_found').attachEvent('onValueSuggest', grid_ticket_clients_found_click)
$$('grid_tdetails').attachEvent('onAfterRender', grid_tdetails_render)
webix.extend($$('grid_tickets'), webix.ProgressBar)
webix.extend($$('grid_tickets_active'), webix.ProgressBar)
}
}
function current_dates_tickets(){
var fy = $$('filter_year_ticket')
var fm = $$('filter_month_ticket')
var d = new Date()
fy.blockEvent()
fm.blockEvent()
fm.setValue(d.getMonth() + 1)
webix.ajax().sync().get('/values/filteryearsticket', function(text, data){
var values = data.json()
fy.getList().parse(values)
fy.setValue(d.getFullYear())
})
fy.unblockEvent()
fm.unblockEvent()
}
function get_tickets(filters){
if(filters == undefined){
filters = {'opt': 'today'}
}
var grid = $$('grid_tickets')
grid.showProgress({type: 'icon'})
webix.ajax().get('/tickets', filters, {
error: function(text, data, xhr) {
msg_error('Error al consultar')
},
success: function(text, data, xhr) {
var values = data.json();
grid.clearAll();
if (values.ok){
grid.parse(values.rows, 'json')
}
}
})
}
function cmd_ticket_filter_today_click(){
get_tickets()
}
function filter_year_ticket_change(nv, ov){
var fm = $$('filter_month_ticket')
filters = {'opt': 'yearmonth', 'year': nv, 'month': fm.getValue()}
get_tickets(filters)
}
function filter_month_ticket_change(nv, ov){
var fy = $$('filter_year_ticket')
filters = {'opt': 'yearmonth', 'year': fy.getValue(), 'month': nv}
get_tickets(filters)
}
function filter_dates_ticket_change(range){
if(range.start != null && range.end != null){
filters = {'opt': 'dates', 'range': range}
get_tickets(filters)
}
}
function configuracion_inicial_ticket(){
current_dates_tickets()
get_tickets()
webix.ajax().sync().get('/values/configticket', function(text, data){
var values = data.json()
//~ showvar(values)
cfg_ticket['open_pdf'] = values.open_pdf
cfg_ticket['direct_print'] = values.direct_print
cfg_ticket['edit_cant'] = values.edit_cant
cfg_ticket['total_up'] = values.total_up
})
}
function get_active_tickets(grid){
filters = {'opt': 'active'}
grid.showProgress({type: 'icon'})
webix.ajax().get('/tickets', filters, {
error: function(text, data, xhr) {
msg_error('Error al consultar')
},
success: function(text, data, xhr) {
var values = data.json();
grid.clearAll();
if (values.ok){
grid.parse(values.rows, 'json')
}
}
})
}
function configuracion_inicial_ticket_to_invoice(){
var grid = $$('grid_tickets_active')
var gridt = $$('grid_tickets_invoice')
var form = $$('form_ticket_invoice')
var chk = $$('chk_is_invoice_day')
chk.setValue(false)
get_active_tickets(grid)
form.setValues({id_partner: 0, lbl_tclient: 'Ninguno'})
gridt.attachEvent('onAfterAdd', function(id, index){
gridt.adjustColumn('index')
gridt.adjustColumn('folio', 'all')
gridt.adjustColumn('fecha', 'all')
});
gridt.clearAll()
}
function configuracion_inicial_nuevo_ticket(){
var grid = $$('grid_tdetails')
webix.ajax().sync().get('/values/taxes', function(text, data){
var values = data.json()
table_taxes.clear()
table_taxes.insert(values)
})
get_forma_pago('lst_ticket_forma_pago')
grid.clearAll()
table_pt.clear()
table_totals.clear()
show('grid_ticket_total_up', cfg_ticket['total_up'])
$$('form_new_ticket').setValues({notas: '', forma_pago: last_forma_pago})
}
function cmd_nuevo_ticket_click(){
configuracion_inicial_nuevo_ticket()
$$('multi_tickets').setValue('tickets_new')
}
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')
}
function validar_ticket(){
var grid = $$('grid_tdetails')
if(!grid.count()){
webix.UIManager.setFocus('tsearch_product_key')
msg = 'Agrega al menos un producto'
msg_error(msg)
return false
}
var values = $$('form_new_ticket').getValues()
if(!values.forma_pago){
msg = 'La forma de pago es requerida'
msg_error(msg)
return false
}
return true
}
function get_ticket_pdf(id){
window.open('/doc/tpdf/' + id, '_blank')
}
function guardar_ticket(values){
var gd = $$('grid_tdetails')
var grid = $$('grid_tickets')
var rows = gd.data.getRange()
for (i = 0; i < rows.length; i++) {
delete rows[i]['id']
delete rows[i]['delete']
delete rows[i]['clave']
delete rows[i]['clave_sat']
delete rows[i]['unidad']
delete rows[i]['importe']
rows[i]['valor_unitario'] = parseFloat(rows[i]['valor_unitario'])
rows[i]['descuento'] = parseFloat(rows[i]['descuento'])
}
last_forma_pago = values.forma_pago
var data = new Object()
data['opt'] = 'add'
data['productos'] = rows
data['forma_pago'] = values.forma_pago
data['notas'] = values.notas
webix.ajax().sync().post('tickets', data, {
error:function(text, data, XmlHttpRequest){
msg = 'Ocurrio un error, consulta a soporte técnico'
msg_error(msg)
},
success:function(text, data, XmlHttpRequest){
values = data.json();
if(values.ok){
msg_ok('Ticket generado correctamente')
$$('form_new_ticket').setValues({})
gd.clearAll()
grid.add(values.row)
$$('multi_tickets').setValue('tickets_home')
if(cfg_ticket.open_pdf){
get_ticket_pdf(values.row.id)
}
if(cfg_ticket.direct_print){
print_ticket(values.row.id)
}
}else{
msg_error(values.msg)
}
}
})
}
function cmd_generar_ticket_click(){
var form = $$('form_new_ticket')
if(!form.validate()) {
msg_error('Valores inválidos')
return
}
var values = form.getValues()
if(!validar_ticket()){
return
}
msg = '¿Todos los datos son correctos?<BR><BR>'
msg += '¿Estás seguro de generar este Ticket?'
webix.confirm({
title: 'Generar Ticket',
ok: 'Si',
cancel: 'No',
type: 'confirm-error',
text: msg,
callback:function(result){
if(result){
guardar_ticket(values)
}
}
})
}
function cmd_cerrar_ticket_click(){
$$('multi_tickets').setValue('tickets_home')
}
function calcular_precio_con_impuestos(precio, taxes){
var precio_final = precio
for(var tax of taxes){
impuesto = table_taxes.findOne({'id': tax.tax})
if(impuesto.tipo == 'E'){
continue
}
var base = precio
if(impuesto.tipo == 'R'){
base = (precio * -1).round(DECIMALES)
}
precio_final += (impuesto.tasa * base).round(DECIMALES)
}
return precio_final
}
function edit_cant(id){
if(!cfg_ticket['edit_cant']){
return
}
$$('grid_tdetails').edit({row: id, column: 'cantidad'})
}
function agregar_producto(values){
var taxes = values.taxes
var producto = values.row
var form = $$('form_new_ticket')
var grid = $$('grid_tdetails')
var row = grid.getItem(producto.id)
var precio_final = 0.0
if(row == undefined){
producto['cantidad'] = 1
producto['valor_unitario'] = calcular_precio_con_impuestos(
parseFloat(producto['valor_unitario']), taxes)
producto['importe'] = producto['valor_unitario']
//~ 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)
}
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){
var filters = {
opt: 'by_key',
key: key,
}
webix.ajax().get('/products', filters, {
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)
}
}
})
//~ webix.ajax().get('/values/productokey', {'key': key}, {
//~ error: function(text, data, xhr) {
//~ msg_error('Error al consultar')
//~ },
//~ success: function(text, data, xhr){
//~ var values = data.json()
//~ if (values.ok){
//~ agregar_producto(values)
//~ } else {
//~ msg = 'No se encontró la clave<BR><BR>' + key
//~ msg_error(msg)
//~ }
//~ }
//~ })
}
function tsearch_product_key_press(code, e){
var value = this.getValue()
if(code == 13 && value.trim().length > 0){
buscar_producto_key(value.trim())
}
}
function grid_ticket_details_click(id, e, node){
if(id.column != 'delete'){
return
}
var grid = $$('grid_tdetails')
grid.remove(id.row)
}
function grid_tickets_details_before_edit_stop(state, editor){
var grid = $$('grid_tdetails')
pause(500)
var row = grid.getItem(editor.row)
if(editor.column == 'cantidad'){
var cantidad = parseFloat(state.value)
if(isNaN(cantidad)){
msg = 'La cantidad debe ser un número'
msg_error(msg)
grid.blockEvent()
state.value = state.old
grid.editCancel()
grid.unblockEvent()
return true
}else if(cantidad <= 0){
msg = 'La cantidad no puede cero o menor'
msg_error(msg)
grid.blockEvent()
state.value = state.old
grid.editCancel()
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'])
}
if(editor.column == 'valor_unitario'){
var valor_unitario = parseFloat(state.value)
if(isNaN(valor_unitario)){
msg = 'El valor unitario debe ser un número'
msg_error(msg)
grid.blockEvent()
state.value = state.old
grid.editCancel()
grid.unblockEvent()
return true
}else if(valor_unitario <= 0){
msg = 'El valor unitario no puede cero o menor'
msg_error(msg)
grid.blockEvent()
state.value = state.old
grid.editCancel()
grid.unblockEvent()
return true
}
var cantidad = parseFloat(row['cantidad'])
var descuento = parseFloat(row['descuento'])
}
if(editor.column == 'descuento'){
var descuento = parseFloat(state.value)
if(isNaN(descuento)){
msg = 'El descuento debe ser un número'
msg_error(msg)
grid.blockEvent()
state.value = state.old
grid.editCancel()
grid.unblockEvent()
return true
}
var cantidad = parseFloat(row['cantidad'])
var valor_unitario = parseFloat(row['valor_unitario'])
}
var precio_final = valor_unitario - descuento
row['importe'] = (cantidad * precio_final).round(DECIMALES)
grid.refresh()
if(cfg_ticket['edit_cant']){
focus('tsearch_product_name')
}
}
function gt_productos_found_click(obj){
buscar_producto_key(obj.clave)
}
function cancel_ticket(id){
var grid = $$('grid_tickets')
var data = new Object()
data['opt'] = 'cancel'
data['id'] = id
webix.ajax().sync().post('tickets', data, {
error:function(text, data, XmlHttpRequest){
msg = 'Ocurrio un error, consulta a soporte técnico'
msg_error(msg)
},
success:function(text, data, XmlHttpRequest){
values = data.json();
if(values.ok){
grid.updateItem(id, values.row)
msg_ok(values.msg)
}else{
msg_error(values.msg)
}
}
})
}
function cmd_cancelar_ticket_click(){
var grid = $$('grid_tickets')
if(grid.count() == 0){
return
}
var row = grid.getSelectedItem()
if (row == undefined){
msg_error('Selecciona un ticket')
return
}
if(row['estatus']=='Cancelado'){
msg_error('El ticket ya esta cancelado')
return
}
if(row['estatus']=='Facturado'){
msg_error('El ticket esta facturado')
return
}
msg = '¿Estás seguro de cancelar el siguiente Ticket?<BR><BR>'
msg += 'Folio: ' + row['folio']
msg += '<BR><BR>ESTA ACCIÓN NO SE PUEDE DESHACER'
webix.confirm({
title:'Cancelar Ticket',
ok:'Si',
cancel:'No',
type:'confirm-error',
text:msg,
callback:function(result){
if (result){
cancel_ticket(row['id'])
}
}
})
}
function chk_is_invoice_day_change(new_value, old_value){
var value = Boolean(new_value)
show('fs_ticket_search_client', !value)
enable('lst_global_periodicidad_2', value)
enable('lst_global_months_2', value)
var current_date = new Date()
var current_month = (current_date.getMonth() + 1).toString().padStart(2, '0')
$$('lst_global_months_2').setValue(current_month)
}
function send_timbrar_invoice(id){
webix.ajax().post('invoices', {opt: 'timbrar', id: id, update: false}, function(text, data){
var values = data.json()
if(values.ok){
msg_ok(values.msg)
}else{
webix.alert({
title: 'Error al Timbrar',
text: values.msg,
type: 'alert-error'
})
}
})
}
function save_ticket_to_invoice(data){
webix.ajax().sync().post('tickets', data, {
error:function(text, data, XmlHttpRequest){
msg = 'Ocurrio un error, consulta a soporte técnico'
msg_error(msg)
},
success:function(text, data, XmlHttpRequest){
values = data.json();
if(values.ok){
msg_ok(values.msg)
send_timbrar_invoice(values.id)
$$('chk_is_invoice_day').setValue(false)
$$('multi_tickets').setValue('tickets_home')
}else{
msg_error(values.msg)
}
}
})
}
function cmd_new_invoice_from_ticket_click(){
var form = this.getFormView();
var chk = $$('chk_is_invoice_day')
var grid = $$('grid_tickets_invoice')
var values = form.getValues()
var tickets = []
if(!chk.getValue()){
if(values.id_partner == 0){
webix.UIManager.setFocus('tsearch_client_name')
msg = 'Selecciona un cliente'
msg_error(msg)
return false
}
}
if(!grid.count()){
msg = 'Agrega al menos un ticket a facturar'
msg_error(msg)
return false
}
grid.eachRow(function(row){
tickets.push(row)
})
var data = new Object()
data['opt'] = 'invoice'
data['client'] = values.id_partner
data['tickets'] = tickets
data['is_invoice_day'] = chk.getValue()
var periodicidad = ''
if(data['is_invoice_day']){
periodicidad = $$('lst_global_periodicidad_2').getValue() + '|'
periodicidad += $$('lst_global_months_2').getValue() + '|'
periodicidad += new Date().getFullYear()
}
data['periodicidad'] = periodicidad
msg = 'Todos los datos son correctos.<BR><BR>'
if(data['is_invoice_day']){
msg += 'Es Factura Global.<BR><BR>'
}
msg += '¿Estás seguro de generar esta factura?'
webix.confirm({
title: 'Generar Factura',
ok: 'Si',
cancel: 'No',
type: 'confirm-error',
text: msg,
callback:function(result){
if(result){
save_ticket_to_invoice(data)
}
}
})
}
function grid_tickets_active_double_click(id, e, node){
this.move(id.row, -1, $$('grid_tickets_invoice'))
}
function grid_tickets_invoice_double_click(id, e, node){
this.move(id.row, -1, $$('grid_tickets_active'))
}
function cmd_move_tickets_right_click(){
var source = $$('grid_tickets_active')
var target = $$('grid_tickets_invoice')
_move_tickets(source, target)
}
function cmd_move_tickets_left_click(){
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()
}
}
function ticket_set_client(row){
var form = $$('form_ticket_invoice')
var html = '<span class="webix_icon fa-user"></span><span class="lbl_partner">'
form.setValues({
id_partner: row.id,
tsearch_client_key: '',
tsearch_client_name: ''}, true)
html += row.nombre + ' (' + row.rfc + ')</span>'
$$('lbl_tclient').setValue(html)
}
function ticket_search_client_by_id(id){
webix.ajax().get('/values/client', {'id': id}, {
error: function(text, data, xhr) {
msg_error('Error al consultar')
},
success: function(text, data, xhr){
var values = data.json()
if (values.ok){
ticket_set_client(values.row)
}else{
msg = 'No se encontró un cliente con la clave: ' + id
msg_error(msg)
}
}
})
}
function tsearch_client_key_press(code, e){
var value = this.getValue()
if(code == 13 && value.length > 0){
var id = parseInt(value, 10)
if (isNaN(id)){
msg_error('Captura una clave válida')
}else{
ticket_search_client_by_id(id)
}
}
}
function grid_ticket_clients_found_click(obj){
ticket_set_client(obj)
}
function cmd_ticket_notes_click(){
win_ticket_notes.init()
var values = $$('form_new_ticket').getValues()
$$('ticket_notes').setValue(values.notas)
$$('win_ticket_notes').show()
to_end('ticket_notes')
}
function cmd_ticket_save_note_click(){
var value = $$('ticket_notes').getValue()
$$('form_new_ticket').setValues({notas: value}, true)
$$('win_ticket_notes').close()
}
function print_ticket(id){
var data = new Object()
data['opt'] = 'print'
data['id'] = id
webix.ajax().post('tickets', data, {
error:function(text, data, XmlHttpRequest){
msg = 'Ocurrio un error, consulta a soporte técnico'
msg_error(msg)
},
success:function(text, data, XmlHttpRequest){
values = data.json();
if(values.ok){
msg_ok(values.msg)
}else{
msg_error(values.msg)
}
}
})
}
function grid_tickets_click(id, e, node){
if(id.column == 'pdf'){
//~ window.open('/doc/tpdf/' + id, '_blank')
get_ticket_pdf(id.row)
return
}
if(id.column == 'print'){
print_ticket(id.row)
return
}
}
function cmd_ticket_report_pdf_click(){
webix.toPDF($$('grid_tickets'), {
ignore: {'pdf': true, 'print': true},
filename: 'Reporte_Tickets',
width: 612,
height: 792,
columns:{
index: true,
serie: {width: 50},
folio: {width: 50},
fecha: {width: 125},
estatus: true,
total: {css: 'right'},
}
})
}
function cmd_ticket_report_xls_click(){
webix.toExcel($$('grid_tickets'), {
filename: 'Reporte_Tickets',
name: 'Tickets',
ignore: {'pdf': true, 'print': true},
rawValues: true,
})
}
function grid_tdetails_render(data){
var total = 0.0
this.eachRow(function(id){
var item = this.getItem(id)
total += item.importe
})
var id = $$('grid_ticket_total_up').getFirstId()
$$('grid_ticket_total_up').updateItem(id, {total: total})
}

View File

@ -1,8 +1,30 @@
var PUBLICO = "Público en general"; //~ Empresa Libre
//~ Copyright (C) 2016-2018 Mauricio Baeza Servin (web@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
//~ the Free Software Foundation, either version 3 of the License, or
//~ (at your option) any later version.
//~
//~ This program is distributed in the hope that it will be useful,
//~ but WITHOUT ANY WARRANTY; without even the implied warranty of
//~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
//~ GNU General Public License for more details.
//~
//~ You should have received a copy of the GNU General Public License
//~ along with this program. If not, see <http://www.gnu.org/licenses/>.
var PUBLICO = "PUBLICO EN GENERAL";
var RFC_PUBLICO = "XAXX010101000"; var RFC_PUBLICO = "XAXX010101000";
var RFC_EXTRANJERO = "XEXX010101000"; var RFC_EXTRANJERO = "XEXX010101000";
var PAIS = "México"; var PAIS = "México";
var DECIMALES = 2; var DECIMALES = 2;
//~ var DECIMALES_PU = 4;
var DECIMALES_TAX = 4;
var CLAVE_ANTICIPOS = '84111506';
var CURRENCY_MN = 'MXN';
var KEY_SAT_01 = '01010101';
var db = new loki('data.db'); var db = new loki('data.db');
@ -12,20 +34,149 @@ var table_pt = db.addCollection('productstaxes')
var table_totals = db.addCollection('totals', {unique: ['tax']}) var table_totals = db.addCollection('totals', {unique: ['tax']})
var table_series = db.addCollection('series') var table_series = db.addCollection('series')
var table_usocfdi = db.addCollection('usocfdi') var table_usocfdi = db.addCollection('usocfdi')
var table_relaciones = db.addCollection('relaciones')
var table_waypayment = db.addCollection('waypayment')
var msg = ''
var months = [
{id: -1, value: 'Todos'},
{id: 1, value: 'Enero'},
{id: 2, value: 'Febrero'},
{id: 3, value: 'Marzo'},
{id: 4, value: 'Abril'},
{id: 5, value: 'Mayo'},
{id: 6, value: 'Junio'},
{id: 7, value: 'Julio'},
{id: 8, value: 'Agosto'},
{id: 9, value: 'Septiembre'},
{id: 10, value: 'Octubre'},
{id: 11, value: 'Noviembre'},
{id: 12, value: 'Diciembre'},
]
var days = [
{id: -1, value: '00'},
{id: 1, value: '01'},
{id: 2, value: '02'},
{id: 3, value: '03'},
{id: 4, value: '04'},
{id: 5, value: '05'},
{id: 6, value: '06'},
{id: 7, value: '07'},
{id: 8, value: '08'},
{id: 9, value: '09'},
{id: 10, value: '10'},
{id: 11, value: '11'},
{id: 12, value: '12'},
{id: 13, value: '13'},
{id: 14, value: '14'},
{id: 15, value: '15'},
{id: 16, value: '16'},
{id: 17, value: '17'},
{id: 18, value: '18'},
{id: 19, value: '19'},
{id: 20, value: '20'},
{id: 21, value: '21'},
{id: 22, value: '22'},
{id: 23, value: '23'},
{id: 24, value: '24'},
{id: 25, value: '25'},
{id: 26, value: '26'},
{id: 27, value: '27'},
{id: 28, value: '28'},
{id: 29, value: '29'},
{id: 30, value: '30'},
{id: 31, value: '31'},
]
function show(values){
function get_icon(tipo){
icons = {
xml: 'fa-file-code-o',
pdf: 'fa-file-pdf-o',
html: 'fa-html5',
zip: 'fa-file-zip-o',
email: 'fa-envelope-o',
print: 'fa-print',
table: 'fa-table',
}
return "<span class='webix_icon " + icons[tipo] + "'></span>"
}
function focus(name){
webix.UIManager.setFocus(name)
}
function select_all(name){
focus(name)
$$(name).getInputNode().select()
}
function to_end(name){
focus(name)
var txt = $$(name)
var pos = txt.getValue().length
var height = txt.getInputNode().scrollHeight
webix.html.setSelectionRange(txt.getInputNode(), pos)
txt.getInputNode().scrollTop = height
}
function showvar(values){
webix.message(JSON.stringify(values, null, 2)) webix.message(JSON.stringify(values, null, 2))
} }
function showtype(values){
webix.message(typeof(values))
}
function show(nombre, value){
if(value == '0'){
value = false
}
if(value){
$$(nombre).show()
}else{
$$(nombre).hide()
}
}
function show_column(table, column){
$$(table).showColumn(column)
}
function enable(nombre, value){
if(value == '0'){
value = false
}
if(value){
$$(nombre).enable()
}else{
$$(nombre).disable()
}
}
function msg_error(msg){ function msg_error(msg){
if(!msg){
msg = 'Error al consultar'
}
webix.message({type: 'error', text: msg}) webix.message({type: 'error', text: msg})
} }
function msg_sucess(msg){ function msg_ok(msg){
webix.message({type: 'sucess', text: msg}) webix.message({type: 'success', text: msg})
} }
@ -34,12 +185,78 @@ Number.prototype.round = function(decimals){
} }
String.prototype.is_number = function(){
return /^\d+$/.test(this)
}
String.prototype.to_float = function(){
return get_float(this)
}
String.prototype.to_float4 = function(){
return get_float4(this)
}
function get_float(value){
var f = parseFloat(value.replace('$', '').replace(/,/g, '').trim()).round(DECIMALES)
if(!f){
f = 0.00
}
return f
}
function get_float4(value){
var f = parseFloat(value.replace('$', '').replace(/,/g, '').trim()).round(DECIMALES_TAX)
if(!f){
f = 0.00
}
return f
}
var format_decimal_2 = webix.Number.numToStr({
groupSize: 3,
decimalSize: 2,
groupDelimiter: ",",
decimalDelimiter: "."
})
var format_decimal_4 = webix.Number.numToStr({
groupSize: 3,
decimalSize: 4,
groupDelimiter: ",",
decimalDelimiter: "."
})
function format_currency(value){
var fv = ''
if(get_config('decimales_precios') == 4){
fv = '$ ' + format_decimal_4(value)
}else{
fv = '$ ' + format_decimal_2(value)
}
return fv
}
function format_currency2(value){
return '$ ' + format_decimal_2(value)
}
function format_currency4(value){
return '$ ' + format_decimal_4(value)
}
webix.protoUI({ webix.protoUI({
$cssName: "text", $cssName: "text",
name: "currency", name: "currency",
$init:function(){ $init:function(){
this.attachEvent("onItemClick", function(){ this.attachEvent("onItemClick", function(){
this.$setValue(this.config.raw, true) this.$setValue(this.config.raw, true)
this.getInputNode().select()
}) })
this.attachEvent("onBlur", function(){ this.attachEvent("onBlur", function(){
this.$setValue(this.config.value) this.$setValue(this.config.value)
@ -51,24 +268,138 @@ webix.protoUI({
$setValue:function(value, raw){ $setValue:function(value, raw){
this.config.raw = value this.config.raw = value
if(!raw){ if(!raw){
value = webix.i18n.priceFormat(value) value = format_currency(value)
} }
this.getInputNode().value = value this.getInputNode().value = value
} }
}, webix.ui.text) }, webix.ui.text)
webix.ui.datafilter.rowCount = webix.extend({ webix.protoUI({
refresh:function(master, node, value){ $cssName: "text",
node.firstChild.innerHTML = master.count(); name: "currency4",
$init:function(){
this.attachEvent("onItemClick", function(){
this.$setValue(this.config.raw, true)
this.getInputNode().select()
})
this.attachEvent("onBlur", function(){
this.$setValue(this.config.value)
})
},
$render:function(){
this.$setValue(this.config.value)
},
$setValue:function(value, raw){
this.config.raw = value
if(!raw){
value = format_currency4(value)
}
this.getInputNode().value = value
} }
}, webix.ui.datafilter.summColumn) }, webix.ui.text)
webix.protoUI({
$cssName: "text",
name: "currency2",
$init:function(){
this.attachEvent("onItemClick", function(){
this.$setValue(this.config.raw, true)
this.getInputNode().select()
})
this.attachEvent("onBlur", function(){
this.$setValue(this.config.value)
})
},
$render:function(){
this.$setValue(this.config.value)
},
$setValue:function(value, raw){
this.config.raw = value
if(!raw){
value = format_currency2(value)
}
this.getInputNode().value = value
}
}, webix.ui.text)
webix.ui.datafilter.countRows = webix.extend({
refresh:function(master, node, value){
node.firstChild.innerHTML = master.count();
}
}, webix.ui.datafilter.summColumn);
webix.ui.datafilter.summActive = webix.extend({
refresh:function(master, node, value){
node.firstChild.innerHTML = this.summGenerate(master);
},
summGenerate:function(master){
var sum = 0
master.eachRow(function(id){
var row = master.getItem(id)
if(row.estatus == 'Generado'){
var importe = row.total
if(typeof importe === 'string'){
importe = row.total.to_float()
}
sum += importe
}
})
return webix.i18n.priceFormat(sum)
}
}, webix.ui.datafilter.summColumn);
webix.ui.datafilter.summTimbrada = webix.extend({
refresh:function(master, node, value){
node.firstChild.innerHTML = this.summGenerate(master);
},
summGenerate:function(master){
var sum = 0
master.eachRow(function(id){
var row = master.getItem(id)
if(row.estatus == 'Timbrada'){
var importe = row.total_mn
if(typeof importe === 'string'){
importe = row.total_mn.to_float()
}
sum += importe
}
})
return webix.i18n.priceFormat(sum)
}
}, webix.ui.datafilter.summColumn);
webix.ui.datafilter.summTimbradaN = webix.extend({
refresh:function(master, node, value){
node.firstChild.innerHTML = this.summGenerate(master);
},
summGenerate:function(master){
var sum = 0
master.eachRow(function(id){
var row = master.getItem(id)
if(row.estatus == 'Timbrado'){
var importe = row.total
if(typeof importe === 'string'){
importe = row.total.to_float()
}
sum += importe
}
})
return webix.i18n.priceFormat(sum)
}
}, webix.ui.datafilter.summColumn);
function validate_rfc(value){ function validate_rfc(value){
rfc = value.trim().toUpperCase(); rfc = value.trim().toUpperCase();
if ( rfc == ""){ if ( rfc == ""){
webix.message({ type:"error", text:"El RFC no puede estar vacío" }); msg_error('El RFC no puede estar vacío')
return false return false
} }
@ -80,41 +411,93 @@ function validate_rfc(value){
start = 2 start = 2
} }
if (rfc.length != length){ if (rfc.length != length){
webix.message({ type:"error", text:"Longitud incorrecta del RFC" }); msg_error('Longitud incorrecta del RFC')
return false return false
} }
if (tipo_persona < 3 && (rfc == RFC_PUBLICO || rfc == RFC_EXTRANJERO)){ if (tipo_persona < 3 && (rfc == RFC_PUBLICO || rfc == RFC_EXTRANJERO)){
webix.message({ type:"error", text:"RFC incorrecto" }); msg_error('RFC incorrecto')
return false return false
} }
var part = rfc.slice(0, start); var part = rfc.slice(0, start);
var re = new RegExp('[a-z&Ñ]{' + start + '}', 'i'); var re = new RegExp('[a-z&Ñ]{' + start + '}', 'i');
if (!part.match(re)){ if (!part.match(re)){
webix.message({ type:"error", text: "El RFC tiene caractéres inválidos al inicio" }); msg_error('El RFC tiene caractéres inválidos al inicio')
return false return false
} }
part = rfc.slice(-3); part = rfc.slice(-3);
re = new RegExp('[a-z0-9]{3}', 'i'); re = new RegExp('[a-z0-9]{3}', 'i');
if (!part.match(re)){ if (!part.match(re)){
webix.message({ type:"error", text: "El RFC tiene caractéres inválidos al final" }); msg_error('El RFC tiene caractéres inválidos al final')
return false return false
} }
part = rfc.slice(-9, -3); part = rfc.slice(-9, -3);
re = new RegExp('[0-9]{6}', 'i'); re = new RegExp('[0-9]{6}', 'i');
if (!part.match(re)){ if (!part.match(re)){
webix.message({ type:"error", text: "Fecha inválida" }); msg_error('Fecha inválida')
return false return false
} }
var month = parseInt(part.slice(-4, -2)) var month = parseInt(part.slice(-4, -2))
if (month == 0 || month > 12 ){ if (month == 0 || month > 12 ){
webix.message({ type:"error", text: "Fecha inválida" }); msg_error('Fecha inválida')
return false return false
} }
var day = parseInt(part.slice(-2)) var day = parseInt(part.slice(-2))
if (day == 0 || day > 31 ){ if (day == 0 || day > 31 ){
webix.message({ type:"error", text: "Fecha inválida" }); msg_error('Fecha inválida')
return false
}
return true
}
function validar_rfc(value){
rfc = value.trim().toUpperCase();
if (rfc == ""){
msg_error('El RFC no puede estar vacío')
return false
}
if (rfc.length < 12 || rfc.length > 13){
msg_error('Longitud incorrecta del RFC')
return false
}
var length = rfc.length
var start = 4
if(length==12){
start = 2
}
var part = rfc.slice(0, start);
var re = new RegExp('[a-z&Ñ]{' + start + '}', 'i');
if (!part.match(re)){
msg_error('El RFC tiene caractéres inválidos al inicio')
return false
}
part = rfc.slice(-3);
re = new RegExp('[a-z0-9]{3}', 'i');
if (!part.match(re)){
msg_error('El RFC tiene caractéres inválidos al final')
return false
}
part = rfc.slice(-9, -3);
re = new RegExp('[0-9]{6}', 'i');
if (!part.match(re)){
msg_error('Fecha inválida')
return false
}
var month = parseInt(part.slice(-4, -2))
if (month == 0 || month > 12 ){
msg_error('Fecha inválida')
return false
}
var day = parseInt(part.slice(-2))
if (day == 0 || day > 31 ){
msg_error('Fecha inválida')
return false return false
} }
@ -147,3 +530,167 @@ function get_config(value){
return key.value return key.value
} }
} }
webix.DataDriver.plainjs = webix.extend({
arr2hash:function(data){
var hash = {};
for (var i=0; i<data.length; i++){
var pid = data[i].parent_id;
if (!hash[pid]) hash[pid]=[];
hash[pid].push(data[i]);
}
return hash;
},
hash2tree:function(hash, level){
var top = hash[level];
for (var i=0; i<top.length; i++){
var branch = top[i].id;
if (hash[branch])
top[i].data = this.hash2tree(hash, branch);
}
return top;
},
getRecords:function(data, id){
var hash = this.arr2hash(data);
return this.hash2tree(hash, 0);
}
}, webix.DataDriver.json)
function get_forma_pago(control){
webix.ajax().get('/values/formapago', {key: true}, function(text, data){
var values = data.json()
$$(control).getList().parse(values)
})
}
function get_way_payment(){
webix.ajax().get('/satformapago', {opt: 'active_by_id'}, function(text, data){
var values = data.json()
table_waypayment.clear()
table_waypayment.insert(values)
})
}
function set_way_payment(control, filter99=false, current_way_payment=''){
if(filter99){
var values = table_waypayment.chain().find({'value': { '$ne' : 'Por definir' }}).data()
}else{
var values = table_waypayment.chain().data()
}
$$(control).getList().parse(values)
if(current_way_payment){
$$(control).setValue(current_way_payment)
}
}
function validate_regexp(value, pattern){
re = new RegExp(pattern, 'i');
if(value.match(re)){
return true
}else{
return false
}
}
function validate_pedimento(value){
var pattern = '[0-9]{2} [0-9]{2} [0-9]{4} [0-9]{7}'
return validate_regexp(value, pattern)
}
function validate_curp(value){
var pattern = '[A-Z][A,E,I,O,U,X][A-Z]{2}[0-9]{2}[0-1][0-9][0-3][0-9][M,H][A-Z]{2}[B,C,D,F,G,H,J,K,L,M,N,Ñ,P,Q,R,S,T,V,W,X,Y,Z]{3}[0-9,A-Z][0-9]'
return validate_regexp(value, pattern)
}
//config may as well include only text, color and date hash
webix.editors.$popup = {
text:{
view: 'popup', width:500, height:200,
body: {view: 'textarea'}
},
};
function pause(milliseconds) {
var dt = new Date();
while ((new Date()) - dt <= milliseconds) { /* Do nothing */ }
}
//~ Revisado
function lst_clear(lst){
lst.setValue('')
lst.define('options', [])
lst.refresh()
}
function lst_parse(lst, values){
lst.getList().parse(values)
}
function lst_parse2(lst_name, values){
obj = $$(lst_name)
obj.getList().parse(values)
}
function set_value(control, value){
obj = $$(control)
obj.blockEvent()
obj.setValue(value)
obj.unblockEvent()
}
function grid_parse(grid_name, values){
obj = $$(grid_name)
obj.clearAll()
obj.parse(values)
}
function activate_tab(parent, name){
$$(parent).getTabbar().setValue(name)
}
var opt_global_periodicidad = [
{id: '01', value: '[01] Diario'},
{id: '02', value: '[02] Semanal'},
{id: '03', value: '[03] Quincenal'},
{id: '04', value: '[04] Mensual'},
{id: '05', value: '[05] Bimestral'},
]
var opt_global_months = [
{id: '01', value: '[01] Enero'},
{id: '02', value: '[02] Febrero'},
{id: '03', value: '[03] Marzo'},
{id: '04', value: '[04] Abril'},
{id: '05', value: '[05] Mayo'},
{id: '06', value: '[06] Junio'},
{id: '07', value: '[07] Julio'},
{id: '08', value: '[08] Agosto'},
{id: '09', value: '[09] Septiembre'},
{id: '10', value: '[10] Octubre'},
{id: '11', value: '[11] Noviembre'},
{id: '12', value: '[12] Diciembre'},
{id: '13', value: '[13] Enero-Febrero'},
{id: '14', value: '[14] Marzo-Abril'},
{id: '15', value: '[15] Mayo-Junio'},
{id: '16', value: '[16] Julio-Agosto'},
{id: '17', value: '[17] Septiembre-Octubre'},
{id: '18', value: '[18] Noviembre-Diciembre'},
]

View File

@ -29,10 +29,6 @@ webix.i18n.locales["es-ES"] = {
today: "Hoy" today: "Hoy"
}, },
controls:{
select:"Seleccione",
invalidMessage: "Valor de entrada no válido"
},
dataExport:{ dataExport:{
page:"Página", page:"Página",
of:"de" of:"de"
@ -81,7 +77,12 @@ webix.i18n.locales["es-ES"] = {
italic: "Itálico" italic: "Itálico"
}, },
combo:{ combo:{
select:"Seleccionar",
selectAll:"Seleccionar todo", selectAll:"Seleccionar todo",
unselectAll:"Deselecciona todo" unselectAll:"Deselecciona todo"
},
message:{
ok:"OK",
cancel:"Cancelar"
} }
}; };

File diff suppressed because one or more lines are too long

View File

@ -1,275 +0,0 @@
webix.protoUI({
name: "sidebar",
defaults:{
titleHeight: 40,
type: "sideBar",
activeTitle: true,
select: true,
scroll: false,
collapsed: false,
collapsedWidth: 41,
position: "left",
width: 250,
mouseEventDelay: 10
},
$init: function(config){
this.$ready.push(this._initSidebar);
this.$ready.push(this._initContextMenu);
},
on_context:{},
on_mouse_move:{},
_initSidebar: function(){
this._fullWidth = this.config.width;
this.attachEvent("onBeforeOpen", function(id){
if(!this.config.multipleOpen)
this.closeAll();
return !this.config.collapsed;
});
this.attachEvent("onItemClick", function(id, ev, node){
if(this.getPopup() && !this.getPopup().config.hidden)
ev.showpopup = true;
if(webix.env.touch)
this._showPopup(id, node);
});
this.attachEvent("onBeforeSelect", function(id){
if(!this.getItem(id).$count){
var selected = this.getSelectedId();
if(selected && id!= selected){
var parentId = this.getParentId(selected);
this.removeCss(parentId, "webix_sidebar_selected");
}
return true;
}
return false;
});
this.attachEvent("onAfterSelect", function(id){
var parentId = this.getParentId(id);
this.addCss(parentId, "webix_sidebar_selected");
var title = this.getPopupTitle();
title.callEvent("onMasterSelect",[id]);
});
this.attachEvent("onMouseMove", function(id, ev, node){
this._showPopup(id, node);
});
if(this.config.collapsed)
this.collapse();
},
_showPopup: function(id, node){
if(this.config.collapsed){
var popup = this.getPopup();
if(popup){
var title = this.getPopupTitle();
if(title){
this._updateTitle(id);
}
var list = this.getPopupList();
if(list){
this._updateList(id);
}
var x = (this.config.position == "left"?this.config.collapsedWidth:-popup.config.width);
popup.show(node, {x: x , y:-1});
}
}
},
_updateTitle: function(id){
var title = this.getPopupTitle();
title.masterId = id;
title.parse(this.getItem(id));
var selectedId = this.getSelectedId();
if(selectedId && this.getParentId(selectedId) == id){
webix.html.addCss(title.$view, "webix_sidebar_selected", true);
}
else{
webix.html.removeCss(title.$view, "webix_sidebar_selected");
}
if(selectedId == id){
webix.html.addCss(title.$view, "webix_selected", true);
}
else{
webix.html.removeCss(title.$view, "webix_selected");
}
},
_updateList: function(id){
var list = this.getPopupList();
list.masterId = id;
var selectedId = this.getSelectedId();
var data = [].concat(webix.copy(this.data.getBranch(id)));
list.unselect();
if(data.length){
list.show();
list.data.importData(data);
if(list.exists(selectedId))
list.select(selectedId);
}
else
list.hide();
},
_initContextMenu: function(){
var config = this.config,
popup;
if(config.popup){
popup = webix.$$(config.popup);
}
if(!popup){
var dirClassName = (config.position=="left"?"webix_sidebar_popup_left":"webix_sidebar_popup_right");
var popupConfig = {
view:"popup",
css: "webix_sidebar_popup "+dirClassName,
autofit: false,
width: this._fullWidth - this.config.collapsedWidth,
borderless: true,
padding:0,
body:{
rows:[
{
view: "template", borderless: true, css: "webix_sidebar_popup_title",
template: "#value#", height: this.config.titleHeight+2,
on:{
onMasterSelect: function(id){
var master = this.getTopParentView().master;
if( master && master.getParentId(id) == this.masterId){
webix.html.addCss(this.$view, "webix_sidebar_selected", true);
}
if(master.config.collapsed && master.getItem(id).$level ==1){
webix.html.addCss(this.$view, "webix_selected", true);
}
}
},
onClick:{
webix_template: function(){
var id = this.masterId;
var master = this.getTopParentView().master;
if(!master.getItem(id).$count)
master.select(id);
}
}
},
{ view: "list", select: true, borderless: true, css: "webix_sidebar_popup_list", autoheight: true,
on:{
onAfterSelect: function(id){
this.getTopParentView().master.select(id);
}
}
}
]
}
};
webix.extend(popupConfig, config.popup||{}, true);
popup = webix.ui(popupConfig);
popup.master = this;
}
popup.attachEvent("onBeforeShow",function(){
return config.collapsed;
});
var master = this;
var h = webix.event(document.body,"mousemove", function(e){
var trg = e.target || e.srcElement;
if(!popup.config.hidden && !popup.$view.contains(trg) && !master.$view.firstChild.contains(trg)){
popup.hide();
}
});
this.attachEvent("onDestruct", function(){
if(webix.removeEvent)
webix.removeEvent(h);
if(popup)
popup.destructor();
});
config.popupId = popup.config.id;
},
getPopup: function(){
return webix.$$(this.config.popupId);
},
getPopupTitle: function(){
var popup = this.getPopup();
return popup.getBody().getChildViews()[0];
},
getPopupList: function(){
var popup = this.getPopup();
return popup.getBody().getChildViews()[1];
},
position_setter:function(value){
var newPos = value;
var oldPos = value=="left"?"right":"left";
webix.html.removeCss(this.$view, "webix_sidebar_"+oldPos);
webix.html.addCss(this.$view, "webix_sidebar_"+newPos, true);
var popup = this.getPopup();
if(popup){
var popupEl = popup.$view;
webix.html.removeCss(popupEl, "webix_sidebar_popup_"+oldPos);
webix.html.addCss(popupEl, "webix_sidebar_popup_"+newPos, true);
}
return value;
},
collapse: function(){
this.define("collapsed", true);
},
expand: function(){
this.define("collapsed", false);
},
toggle: function(){
var collapsed = !this.config.collapsed;
this.define("collapsed", collapsed);
},
collapsed_setter: function(value){
var width;
if(!value){
width = this._fullWidth;
}
else{
width = this.config.collapsedWidth;
this.closeAll();
}
if(!value){
this.type.collapsed = false;
webix.html.addCss(this.$view, "webix_sidebar_expanded", true);
}
else{
this.type.collapsed = true;
webix.html.removeCss(this.$view, "webix_sidebar_expanded");
}
this.define("width",width);
this.resize();
return value;
}
}, webix.ui.tree);
webix.type(webix.ui.tree, {
name:"sideBar",
height: "auto",
css: "webix_sidebar",
template: function(obj, common){
if(common.collapsed)
return common.icon(obj, common);
return common.arrow(obj, common)+common.icon(obj, common) +"<span>"+obj.value+"</span>";
},
arrow: function(obj, common){
var html = "";
var open = "";
for (var i=1; i<=obj.$level; i++){
if (i==obj.$level && obj.$count){
var className = "webix_sidebar_dir_icon webix_icon fa-angle-"+(obj.open?"down":"left");
html+="<span class='"+className+"'></span>";
}
}
return html;
},
icon:function(obj, common){
if(obj.icon)
return "<span class='webix_icon webix_sidebar_icon fa-"+obj.icon+"'></span>";
return "";
}
});

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,688 @@
//~ Empresa Libre
//~ Copyright (C) 2016-2018 Mauricio Baeza Servin (web@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
//~ the Free Software Foundation, either version 3 of the License, or
//~ (at your option) any later version.
//~
//~ This program is distributed in the hope that it will be useful,
//~ but WITHOUT ANY WARRANTY; without even the implied warranty of
//~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
//~ GNU General Public License for more details.
//~
//~ You should have received a copy of the GNU General Public License
//~ along with this program. If not, see <http://www.gnu.org/licenses/>.
var toolbar_banco = [
{view: 'richselect', id: 'lst_cuentas_banco', label: 'Cuenta',
labelWidth: 100, options: []},
{view: 'text', id: 'txt_cuenta_moneda', label: 'Moneda', readonly: true},
{view: 'currency2', id: 'txt_cuenta_saldo', label: 'Saldo', readonly: true,
inputAlign: 'right', value: 0}
]
var toolbar_filtro_cuenta = [
{view: 'richselect', id: 'filter_cuenta_year', label: 'Año',
labelAlign: 'right', labelWidth: 50, width: 150, options: []},
{view: 'richselect', id: 'filter_cuenta_month', label: 'Mes',
labelAlign: 'right', labelWidth: 50, width: 200, options: months},
{view: 'daterangepicker', id: 'filter_cuenta_dates', label: 'Fechas',
labelAlign: 'right', width: 300},
{},
]
var toolbar_movimientos_banco = [
{view: 'button', id: 'cmd_agregar_retiro', label: 'Retiro',
type: 'iconButton', autowidth: true, icon: 'minus'},
{view: 'button', id: 'cmd_agregar_deposito', label: 'Depósito',
type: 'iconButton', autowidth: true, icon: 'plus'},
{},
{view: 'button', id: 'cmd_complemento_pago', label: 'Generar Factura de Pago',
type: 'iconButton', autowidth: true, icon: 'file-code-o'},
{view: 'button', id: 'cmd_show_invoice_pay', label: 'Ver Facturas de Pago',
type: 'iconButton', autowidth: true, icon: 'table'},
{},
{view: 'button', id: 'cmd_cancelar_movimiento', label: 'Cancelar',
type: 'iconButton', autowidth: true, icon: 'ban'},
]
var grid_cuentabanco_cols = [
{id: 'id', header:'ID', hidden: true},
{id: 'fecha', header: 'Fecha', width: 150},
{id: 'numero_operacion', header: 'Referencia'},
{id: 'way_payment', header: 'Forma de Pago', hidden: true},
{id: 'descripcion', header: ['Descripción', {content: 'textFilter'}],
fillspace:true},
{id: 'retiro', header: ['Retiro', {content: 'numberFilter'}],
width: 125, format: webix.i18n.priceFormat, css: 'right'},
{id: 'deposito', header: ['Depósito', {content: 'numberFilter'}],
width: 125, format: webix.i18n.priceFormat, css: 'right'},
{id: 'saldo', header: ['Saldo'],
width: 125, format: webix.i18n.priceFormat, css: 'right'},
{id: 'invoice', header: ['FP'], width: 40, css: 'center', hidden: true},
]
var grid_cuentabanco = {
view: 'datatable',
id: 'grid_cuentabanco',
select: 'row',
adjust: true,
footer: true,
resizeColumn: true,
headermenu: true,
columns: grid_cuentabanco_cols,
}
var grid_cfdi_por_pagar_cols = [
{id: 'index', header: '#', adjust: 'data', css: 'right'},
{id: 'id', header: 'ID', hidden: true},
{id: 'serie', header: 'Serie', adjust: 'data'},
{id: 'folio', header: ['Folio', {content: 'numberFilter'}], adjust: 'data', css: 'right'},
{id: 'uuid', header: 'UUID', width: 250, hidden: true},
{id: 'fecha', header: 'Fecha y Hora', width: 150, sort: 'date'},
{id: 'tipo_comprobante', header: 'Tipo', adjust: 'data'},
{id: 'estatus', header: 'Estatus', adjust: 'header'},
{id: 'cliente', header: ['Razón Social', {content: 'selectFilter'}],
fillspace:true, sort: 'string'},
{id: 'total', header: ['Total'], width: 125, sort: 'int',
format: webix.i18n.priceFormat, css: 'right', hidden: true},
{id: 'currency', header: ['Moneda', {content: 'selectFilter'}],
adjust: 'data', sort: 'string', hidden: true},
{id: 'total_mn', header: ['Total M.N'], width: 125, sort: 'int',
format: webix.i18n.priceFormat, css: 'right'},
{id: 'saldo', header: ['Saldo'], width: 125, sort: 'int',
format: webix.i18n.priceFormat, css: 'right'},
]
var grid_cfdi_este_deposito_cols = [
{id: 'index', header: '#', adjust: 'data', css: 'right'},
{id: 'id', header: 'ID', hidden: true},
{id: 'serie', header: 'Serie', adjust: 'data'},
{id: 'folio', header: 'Folio', adjust: 'data', css: 'right'},
{id: 'uuid', header: 'UUID', width: 250, hidden: true},
{id: 'fecha', header: 'Fecha y Hora', width: 150, sort: 'date'},
{id: 'tipo_comprobante', header: 'Tipo', adjust: 'data'},
{id: 'estatus', header: 'Estatus', adjust: 'header'},
{id: 'cliente', header: ['Razón Social'], fillspace: true},
{id: 'total', header: ['Total'], width: 125, sort: 'int',
format: webix.i18n.priceFormat, css: 'right', hidden: true},
{id: 'currency', header: ['Moneda', {content: 'selectFilter'}],
adjust: 'data', sort: 'string', hidden: true},
{id: 'total_mn', header: ['Total M.N'], width: 125, sort: 'int',
format: webix.i18n.priceFormat, css: 'right'},
{id: 'saldo', header: ['Saldo'], width: 125, sort: 'int',
format: webix.i18n.priceFormat, css: 'right'},
{id: 'this_pay', header: ['Este pago'], width: 125, sort: 'int', hidden: true,
format: webix.i18n.priceFormat, css: 'right', editor: 'text'},
{id: 'importe', header: ['Este pago'], width: 125, sort: 'int',
format: webix.i18n.priceFormat, css: 'right', editor: 'text'},
{id: 'type_change', header: ['T.C.'], width: 75, hidden: true,
format: format_currency4, css: 'right'},
]
var grid_cfdi_pay_cols = [
{id: 'index', header: '#', adjust: 'data', css: 'right',
footer: {content: 'countRows', colspan: 3, css: 'right'}},
{id: "id", header:"ID", hidden:true},
{id: 'serie', header: ["Serie"], adjust: "data", sort: 'string'},
{id: 'folio', header: ['Folio'], adjust: 'data',
sort: 'int', css: 'right', footer: {text: 'Facturas', colspan: 3}},
{id: "uuid", header: ["UUID"], adjust: "data",
sort:"string", hidden:true},
{id: "fecha", header: ["Fecha y Hora"],
adjust: "data", sort: "string"},
{id: "tipo_comprobante", header: ["Tipo"],
adjust: 'header', sort: 'string'},
{id: "estatus", header: ["Estatus"],
adjust: "data", sort:"string"},
{id: 'cliente', header: ['Razón Social'], fillspace: true},
{id: 'xml', header: 'XML', adjust: 'data', template: get_icon('xml')},
{id: 'pdf', header: 'PDF', adjust: 'data', template: get_icon('pdf')},
{id: 'email', header: '@', adjust: 'data', template: get_icon('email')}
]
var grid_pay_related_cols = [
{id: 'index', header: '#', adjust: 'data', css: 'right'},
{id: 'id', header: 'ID', hidden: true},
{id: 'serie', header: 'Serie', adjust: 'data'},
{id: 'folio', header: 'Folio', adjust: 'data', css: 'right'},
{id: 'uuid', header: 'UUID', width: 250, hidden: true},
{id: 'fecha', header: 'Fecha y Hora', width: 150, sort: 'date'},
{id: 'tipo_comprobante', header: 'Tipo', adjust: 'data'},
{id: 'estatus', header: 'Estatus', adjust: 'header'},
{id: 'cliente', header: ['Razón Social'], fillspace: true},
{id: 'total', header: ['Total'], width: 125, sort: 'int',
format: webix.i18n.priceFormat, css: 'right'},
{id: 'saldo', header: ['Saldo'], width: 125, sort: 'int',
format: webix.i18n.priceFormat, css: 'right', css: 'right',
footer: 'Importe Depósito'},
{id: 'importe', header: ['Este pago'], width: 125, sort: 'int',
format: webix.i18n.priceFormat, css: 'right',
footer: {content: 'summColumn', css: 'right'}},
]
var grid_cfdi_por_pagar = {
view: 'datatable',
id: 'grid_cfdi_por_pagar',
select: 'row',
multiselect: true,
autoConfig: false,
adjust: true,
height: 250,
resizeColumn: true,
headermenu: true,
drag: true,
columns: grid_cfdi_por_pagar_cols,
on:{
'data->onStoreUpdated':function(){
this.data.each(function(obj, i){
obj.index = i + 1
})
}
},
}
var grid_cfdi_este_deposito = {
view: 'datatable',
id: 'grid_cfdi_este_deposito',
select: 'row',
multiselect: true,
autoConfig: false,
adjust: true,
height: 200,
resizeColumn: true,
headermenu: true,
drag: true,
editable: true,
columns: grid_cfdi_este_deposito_cols,
on:{
'data->onStoreUpdated':function(){
this.data.each(function(obj, i){
obj.index = i + 1
})
}
},
}
var grid_cfdi_pay = {
view: 'datatable',
id: 'grid_cfdi_pay',
select: 'row',
autoConfig: false,
adjust: true,
autoheight: true,
resizeColumn: true,
headermenu: true,
columns: grid_cfdi_pay_cols,
on:{
'data->onStoreUpdated':function(){
this.data.each(function(obj, i){
obj.index = i + 1
})
}
},
}
var grid_pay_related = {
view: 'datatable',
id: 'grid_pay_related',
select: 'row',
autoConfig: false,
adjust: true,
autoheight: true,
resizeColumn: true,
headermenu: true,
footer: true,
columns: grid_pay_related_cols,
on:{
'data->onStoreUpdated':function(){
this.data.each(function(obj, i){
obj.index = i + 1
})
}
},
}
var grid_bank_invoice_pay_cols = [
{id: 'index', header: '#', adjust: 'data', css: 'right',
footer: {content: 'countRows', colspan: 3, css: 'right'}},
{id: "id", header:"ID", hidden:true},
{id: 'serie', header: ["Serie"], adjust: "data", sort: 'string',
template: '{common.subrow()} #serie#'},
{id: 'folio', header: ['Folio', {content: 'numberFilter'}], adjust: 'header',
sort: 'int', css: 'right', footer: {text: 'Facturas', colspan: 3}},
{id: "uuid", header: ["UUID", {content: "textFilter"}], adjust: "data",
sort:"string", hidden:true},
{id: "fecha", header: ["Fecha y Hora"], width: 135,
sort: "string"},
{id: "tipo_comprobante", header: ["Tipo"], adjust: 'data',
sort: 'string'},
{id: "estatus", header: ["Estatus", {content: "selectFilter"}],
adjust: "header", sort:"string"},
{id: 'movimiento', header: ['Movimiento', {content: "selectFilter"}],
adjust: 'data', sort: 'string'},
{id: 'total', header: ['Total', {content: 'numberFilter'}],
width: 125, sort: 'int', format: webix.i18n.priceFormat, css: 'right',
hidden: true},
{id: 'currency', header: ['Moneda', {content: 'selectFilter'}],
adjust: 'data', sort: 'string', hidden: true},
{id: 'total_mn', header: ['Total M.N.', {content: 'numberFilter'}],
width: 150, sort: 'int', format: webix.i18n.priceFormat, css: 'right',
footer: {content: 'summTimbrada', css: 'right'}},
{id: 'cliente', header: ['Razón Social', {content: 'selectFilter'}],
fillspace: true, sort: 'string'},
{id: 'xml', header: 'XML', adjust: 'data', template: get_icon('xml')},
{id: 'pdf', header: 'PDF', adjust: 'data', template: get_icon('pdf')},
//~ {id: 'ods', header: 'ODS', adjust: 'data', template: get_icon('table')},
//~ {id: 'zip', header: 'ZIP', adjust: 'data', template: get_icon('zip')},
{id: 'email', header: '@', adjust: 'data', template: get_icon('email')}
]
var grid_bank_invoice_pay = {
view: 'datatable',
id: 'grid_bank_invoice_pay',
select: 'row',
adjust: true,
footer: true,
resizeColumn: true,
headermenu: true,
columns: grid_bank_invoice_pay_cols,
scheme:{
$change:function(item){
if (item.estatus == 'Cancelada'){
item.$css = 'cancel'
}
}
},
on:{
'data->onStoreUpdated':function(){
this.data.each(function(obj, i){
obj.index = i + 1
})
}
},
}
var toolbar_banco_retiro = [
{view: 'label', label: 'Agregar retiro de banco', id: 'title_bank_retiro'},
{view: 'button', id: 'cmd_guardar_retiro', label: 'Guardar Retiro',
type: 'iconButton', autowidth: true, icon: 'minus'},
{view: 'icon', click: '$$("multi_bancos").setValue("banco_home")',
icon: 'times-circle'}
]
var toolbar_banco_deposito = [
{view: 'label', label: 'Agregar depósito de banco', id: 'title_bank_deposit'},
{view: 'button', id: 'cmd_guardar_deposito', label: 'Guardar Depósito',
type: 'iconButton', autowidth: true, icon: 'plus'},
{view: 'icon', click: '$$("multi_bancos").setValue("banco_home")',
icon: 'times-circle'}
]
var toolbar_bank_pay = [
{view: 'label', label: 'Factura de pago'},
{},
{view: 'button', id: 'cmd_pay_stamp', label: 'Timbrar',
type: 'iconButton', autowidth: true, icon: 'ticket'},
{view: 'button', id: 'cmd_pay_cancel', label: 'Cancelar',
type: 'iconButton', autowidth: true, icon: 'minus'},
{view: 'button', id: 'cmd_pay_delete', label: 'Eliminar',
type: 'iconButton', autowidth: true, icon: 'ban'},
{view: 'checkbox', id: 'chk_pay_close_when_stamp',
label: 'Cerrar al timbrar', tooltip: 'Cerrar al timbrar'},
{},
{view: 'icon', click: '$$("multi_bancos").setValue("banco_home")',
icon: 'times-circle'}
]
var toolbar_bank_invoice_pay = [
{view: 'label', label: 'Administración de Facturas de Pago'},
{},
{view: 'icon', click: '$$("multi_bancos").setValue("banco_home")',
icon: 'times-circle'}
]
var toolbar_bank_invoice_pay_filter = [
{view: 'richselect', id: 'filter_invoice_pay_year', label: 'Año',
labelAlign: 'right', labelWidth: 50, width: 150, options: []},
{view: 'richselect', id: 'filter_invoice_pay_month', label: 'Mes',
labelAlign: 'right', labelWidth: 50, width: 175, options: months},
{view: 'daterangepicker', id: 'filter_invoice_pay_dates', label: 'Fechas',
labelAlign: 'right', width: 275},
{view: 'button', id: 'cmd_invoice_pay_sat', label: 'SAT',
type: 'iconButton', autowidth: true, icon: 'check-circle'},
{view: 'button', id: 'cmd_invoice_pay_cancel', label: 'Cancelar',
type: 'iconButton', autowidth: true, icon: 'ban'},
{},
]
var controls_banco_retiro = [
{view: 'toolbar', elements: toolbar_banco_retiro},
{cols: [
{view: 'datepicker', id: 'date_retiro', name: 'retiro_fecha',
label: 'Fecha', format: '%d-%M-%Y', labelAlign: 'right',
required: true, invalidMessage: 'Selecciona una fecha',
labelWidth: 125},
{view: 'search', id: 'time_retiro', name: 'retiro_hora',
label: 'Hora', icon: 'clock-o', labelAlign: 'right',
pattern:{mask: '##:##:##', allow:/[0-9]/g}, required: true,
invalidMessage: 'Captura una hora'},
{view: 'text', id: 'retiro_referencia', name: 'retiro_referencia',
label: 'Referencia', labelAlign: 'right'},
]},
{cols: [
{view: 'richselect', id: 'lst_retiro_forma_pago',
name: 'retiro_forma_pago', label: 'Forma de Pago', required: true,
options: [], labelWidth: 125, labelAlign: 'right'},
{view: 'currency2', type: 'text', id: 'txt_retiro_importe',
name: 'retiro_importe', label: 'Importe', labelAlign: 'right',
required: true, invalidMessage: 'Captura un valor númerico',
inputAlign: 'right', value: ''}
]},
{cols: [
{view: 'text', id: 'retiro_descripcion', name: 'retiro_descripcion',
label: 'Descripción', labelAlign: 'right', required: true,
labelWidth: 125},
]},
]
var controls_banco_deposito = [
{view: 'toolbar', elements: toolbar_banco_deposito},
{cols: [
{view: 'datepicker', id: 'date_deposito', name: 'deposito_fecha',
label: 'Fecha', format: '%d-%M-%Y', labelAlign: 'right',
required: true, invalidMessage: 'Selecciona una fecha',
labelWidth: 125},
{view: 'search', id: 'time_deposito', name: 'deposito_hora',
label: 'Hora', icon: 'clock-o', labelAlign: 'right',
pattern:{mask: '##:##:##', allow:/[0-9]/g}, required: true,
invalidMessage: 'Captura una hora'},
{view: 'text', id: 'deposito_referencia', name: 'deposito_referencia',
label: 'Referencia', labelAlign: 'right'},
]},
{cols: [
{view: 'richselect', id: 'lst_deposito_forma_pago',
name: 'deposito_forma_pago', label: 'Forma de Pago', required: true,
options: [], labelWidth: 125, labelAlign: 'right'},
{view: 'currency2', type: 'text', id: 'txt_deposito_importe',
name: 'deposito_importe', label: 'Importe', labelAlign: 'right',
required: true, invalidMessage: 'Captura un valor númerico',
inputAlign: 'right', value: ''},
{view: 'currency4', type: 'text', id: 'deposit_type_change',
name: 'deposit_type_change', label: 'T.C.', labelAlign: 'right',
required: false, invalidMessage: 'Captura un valor númerico',
inputAlign: 'right', value: '1.0000', width: 175, hidden: true}
]},
{cols: [
{view: 'textarea', id: 'deposito_descripcion', label: 'Descripción',
name: 'deposito_descripcion', labelAlign: 'right', required: true,
labelWidth: 125, height: 70},
]},
{cols: [
{view: 'label', label: '<b>Facturas por pagar: </b>'},
{view: 'richselect', id: 'lst_partner_account_bank', hidden: true,
name: 'partner_account_bank', label: 'Cuenta Origen', required: false,
options: [], labelWidth: 125, labelAlign: 'right'},
{},
{view: 'button', id: 'cmd_invoice_payed', label: 'Solo marcar pagada',
type: 'iconButton', autowidth: true, icon: 'check-circle',
tooltip: 'No afecta a saldos'},
]},
grid_cfdi_por_pagar,
{view: 'label', label: '<b>Facturas a pagar en este depósito: </b>'},
grid_cfdi_este_deposito,
]
var controls_bank_pay = [
{view: 'toolbar', elements: toolbar_bank_pay},
{view: 'label', label: '<b>Este depósito: </b>'},
{cols: [
{view: 'datepicker', id: 'pay_date', name: 'pay_date',
label: 'Fecha', format: '%d-%M-%Y', labelAlign: 'right',
required: true, invalidMessage: 'Selecciona una fecha',
labelWidth: 125, readonly: true},
{view: 'search', id: 'pay_time', name: 'pay_time', label: 'Hora',
icon: 'clock-o', labelAlign: 'right', required: true,
readonly: true,
invalidMessage: 'Captura una hora'},
{view: 'text', id: 'pay_reference', name: 'pay_reference',
label: 'Referencia', labelAlign: 'right', readonly: true},
]},
{cols: [
{view: 'richselect', id: 'pay_way_payment', readonly: true,
name: 'pay_way_payment', label: 'Forma de Pago', required: true,
options: [], labelWidth: 125, labelAlign: 'right'},
{view: 'currency', type: 'text', id: 'pay_import', name: 'pay_import',
label: 'Importe', labelAlign: 'right', required: true, readonly: true,
invalidMessage: 'Captura un valor númerico', inputAlign: 'right'}
]},
{cols: [
{view: 'textarea', id: 'pay_description', label: 'Descripción',
name: 'pay_description', labelAlign: 'right', required: true,
labelWidth: 125, height: 70, readonly: true},
]},
{view: 'label', label: '<b>Facturas de pago de este depósito: </b>'},
grid_cfdi_pay,
{view: 'label', label: '<b>Facturas relacionadas en este pago: </b>'},
grid_pay_related
]
var controls_bank_invoice_pay = [
{view: 'toolbar', elements: toolbar_bank_invoice_pay},
{view: 'toolbar', elements: toolbar_bank_invoice_pay_filter},
grid_bank_invoice_pay
]
var form_banco_retiro = {
type: 'space',
responsive: true,
cols: [{
view: 'form',
id: 'form_banco_retiro',
complexData: true,
scroll: true,
elements: controls_banco_retiro,
}],
}
var form_banco_deposito = {
type: 'space',
responsive: true,
cols: [{
view: 'form',
id: 'form_banco_deposito',
complexData: true,
scroll: true,
elements: controls_banco_deposito,
}],
}
var form_bank_pay = {
type: 'space',
responsive: true,
cols: [{
view: 'form',
id: 'form_bank_pay',
complexData: true,
scroll: true,
elements: controls_bank_pay,
}],
}
var form_bank_invoice_pay = {
type: 'space',
responsive: true,
cols: [{
view: 'form',
id: 'form_bank_invoice_pay',
complexData: true,
scroll: true,
elements: controls_bank_invoice_pay,
}],
}
var multi_bancos = {
id: 'multi_bancos',
animate: true,
cells:[
{id: 'banco_home', rows:[
{view: 'toolbar', elements: toolbar_banco},
{view: 'toolbar', elements: toolbar_filtro_cuenta},
{view: 'toolbar', elements: toolbar_movimientos_banco},
grid_cuentabanco,
]},
{id: 'banco_retiro', rows: [form_banco_retiro]},
{id: 'banco_deposito', rows: [form_banco_deposito]},
{id: 'bank_pay', rows: [form_bank_pay]},
{id: 'bank_invoice_pay', rows: [form_bank_invoice_pay]}
],
}
var title_partners = 'Administración de Bancos'
var app_bancos = {
id: 'app_bancos',
rows:[
{view: 'template', id: 'th_bancos', type: 'header',
template: title_partners},
multi_bancos
]
}
var win_body_mov_description = {rows: [
{minHeight: 5, maxHeight: 5},
{view: 'textarea', id: 'mov_description', name: 'mov_description',
height: 300},
{minHeight: 5, maxHeight: 5},
{cols: [{},
{view: 'button', id: 'cmd_save_mov_description', autowidth: true,
label: 'Guardar', type: 'iconButton', icon: 'save', hotkey: 'Ctrl+enter'},
{maxWidth: 50},
{view: 'button', id: 'cmd_close_mov_description', autowidth: true,
label: 'Cerrar', type: 'iconButton', icon: 'times-circle', hotkey: 'esc'},
{}]},
{minHeight: 5, maxHeight: 5},
],}
var win_mov_description = {
init: function(){
webix.ui({
view: 'window',
id: 'win_mov_description',
height: 400,
width: 600,
modal: true,
position: 'center',
head: 'Descripción del movimiento',
body: win_body_mov_description,
})
$$('cmd_save_mov_description').attachEvent('onItemClick', cmd_save_mov_description_click)
$$('cmd_close_mov_description').attachEvent('onItemClick', cmd_close_mov_description_click)
}
}
var opt_reasons_cancel_pay = [
{id: '', value: ''},
{id: '01', value: '[01] Comprobante emitido con errores con relación'},
{id: '02', value: '[02] Comprobante emitido con errores sin relación'},
{id: '03', value: '[03] No se llevó acabo la operación'},
{id: '04', value: '[04] Operación nominativa relacionada en una factura global'},
]
var body_invoice_cancel_pay = {rows: [{minHeight: 15},
{view: 'richselect', id: 'lst_reasons_cancel', labelPosition: 'top', label: 'Razón de cancelación', options: opt_reasons_cancel_pay, value: '', width: 400},
{view: 'text', id: 'txt_cancel_uuid', labelPosition: 'top', label: 'UUID que sustituye', width: 400},
{view: 'label', label: 'Esta acción no se puede deshacer', autowidth: true, align: 'center'},
{view: 'label', label: '¿Estás seguro de continuar?', autowidth: true, align: 'center'},
{cols: [{},
{view: 'button', id: 'cmd_invoice_cancel_pay', width: 100, label: 'Cancelar', type: 'danger', icon: 'ban'},
{maxWidth: 25},
{view: 'button', id: 'cmd_win_cancel_pay_close', width: 100, label: 'Cerrar'},
{}
]},
{minHeight: 20},
]}
var win_invoice_cancel_pay = {
init: function(){
webix.ui({
view: 'window',
id: 'win_invoice_cancel_pay',
modal: true,
width: 400,
position: 'center',
head: 'Cancelar CFDI',
body: body_invoice_cancel_pay,
})
$$('cmd_invoice_cancel_pay').attachEvent('onItemClick', cmd_invoice_cancel_pay_click)
$$('cmd_win_cancel_pay_close').attachEvent('onItemClick', cmd_win_cancel_pay_close_click)
}
}
var body_invoice_cancel_pay2 = {rows: [{minHeight: 15},
{view: 'richselect', id: 'lst_reasons_cancel2', labelPosition: 'top', label: 'Razón de cancelación', options: opt_reasons_cancel_pay, value: '', width: 400},
{view: 'text', id: 'txt_cancel_uuid2', labelPosition: 'top', label: 'UUID que sustituye', width: 400},
{view: 'label', label: 'Esta acción no se puede deshacer', autowidth: true, align: 'center'},
{view: 'label', label: '¿Estás seguro de continuar?', autowidth: true, align: 'center'},
{cols: [{},
{view: 'button', id: 'cmd_invoice_cancel_pay2', width: 100, label: 'Cancelar', type: 'danger', icon: 'ban'},
{maxWidth: 25},
{view: 'button', id: 'cmd_win_cancel_pay_close2', width: 100, label: 'Cerrar'},
{}
]},
{minHeight: 20},
]}
var win_invoice_cancel_pay2 = {
init: function(){
webix.ui({
view: 'window',
id: 'win_invoice_cancel_pay2',
modal: true,
width: 400,
position: 'center',
head: 'Cancelar CFDI de Pago',
body: body_invoice_cancel_pay2,
})
$$('cmd_invoice_cancel_pay2').attachEvent('onItemClick', cmd_invoice_cancel_pay2_click)
$$('cmd_win_cancel_pay_close2').attachEvent('onItemClick', cmd_win_cancel_pay_close2_click)
}
}

View File

@ -0,0 +1,69 @@
var msg_rfc = 'El RFC es requerido'
var form_controls_empresa = [
{view: 'text', label: 'RFC', id: 'txt_alta_rfc', name: 'alta_rfc',
labelPosition: 'top', required: true, invalidMessage: msg_rfc},
{margin: 10, cols:[{}, {view: 'button', value: 'Agregar RFC',
click: 'validate_nuevo_rfc', hotkey: 'enter'}, {}]}
]
var msg_header = '<font color="#610B0B">Bienvenido a Empresa Libre</font>'
var header = [
{view: 'label', label: '<b><font color="#610B0B">Alta de Emisor</font></b>'},
{},
{view: 'button', type: 'icon', width: 40, css: 'app_button',
icon: 'home', click: 'window.location = "/"'},
]
var footer = [
{},
{view: 'button', value: 'Respaldar BD', click: 'cmd_respaldar_bd'},
{},
]
var grid_empresas_cols = [
{id: 'delete', header: '', width: 30, css: 'delete'},
{id: 'rfc', header: 'RFC Emisor', fillspace: true,
footer: {content: 'countRows', css: 'right'}},
]
var grid_empresas = {
view: 'datatable',
id: 'grid_empresas',
select: 'row',
url: '/values/empresas',
adjust: true,
autoheight: true,
headermenu: true,
footer: true,
columns: grid_empresas_cols,
}
var ui_empresas = {
rows: [
{maxHeight: 50},
{view: 'template', template: msg_header, maxHeight: 50, css: 'login_header'},
{maxHeight: 50},
{cols: [{}, {type: 'space', padding: 5,
rows: [
{view: 'toolbar', elements: header},
{
container: 'form_empresas',
view: 'form',
id: 'form_empresas',
width: 400,
elements: form_controls_empresa,
rules:{
alta_rfc:function(value){ return value.trim() != '';},
}
},
grid_empresas,
{view: 'toolbar', elements: footer},
]}, {}, ]
},
]
}

File diff suppressed because it is too large Load Diff

View File

@ -16,16 +16,16 @@ var form_controls = [
] ]
var msg_header = 'Bienvenido a Empresa Libre'
var ui_login = { var ui_login = {
rows: [ rows: [
{maxHeight: 50}, {maxHeight: 50},
{view: 'template', template: msg_header, maxHeight: 50, css: 'login_header'}, {view: 'template', id: 'title_login', template: '', maxHeight: 50,
css: 'login_header'},
{maxHeight: 50}, {maxHeight: 50},
{cols: [{}, {type: 'space', padding: 5, {cols: [{}, {type: 'space', padding: 5,
rows: [ rows: [
{view: 'template', template: 'Acceso al sistema', type: 'header'}, {view: 'template', type: 'header',
template: '<font color="#610B0B">Acceso al sistema</font>'},
{ {
container: 'form_login', container: 'form_login',
view: 'form', view: 'form',

View File

@ -1,14 +1,17 @@
var menu_data = [ var menu_data = [
{id: 'app_home', icon: 'dashboard', value: 'Inicio'}, {id: 'app_home', icon: 'dashboard', value: 'Inicio'},
{id: 'app_partners', icon: 'users', value: 'Clientes y Proveedores'}, {id: 'app_partners', icon: 'address-book-o', value: 'Clientes y Proveedores'},
{id: 'app_products', icon: 'server', value: 'Productos y Servicios'}, {id: 'app_products', icon: 'server', value: 'Productos y Servicios'},
{id: 'app_invoices', icon: 'cart-plus', value: 'Facturas'}, {id: 'app_bancos', icon: 'university', value: 'Bancos'},
]; {id: 'app_invoices', icon: 'file-code-o', value: 'Facturas'},
]
var sidebar = { var sidebar = {
view: 'sidebar', view: 'sidebar',
id: 'main_sidebar',
data: menu_data, data: menu_data,
ready: function(){ ready: function(){
this.select('app_home'); this.select('app_home');
@ -18,8 +21,8 @@ var sidebar = {
onAfterSelect: function(id){ onAfterSelect: function(id){
$$('multi').setValue(id) $$('multi').setValue(id)
} }
} },
}; }
var multi_main = { var multi_main = {
@ -33,9 +36,13 @@ var multi_main = {
}, },
app_partners, app_partners,
app_products, app_products,
app_bancos,
app_school,
app_nomina,
app_tickets,
app_invoices, app_invoices,
] ],
}; }
var menu_user = { var menu_user = {
@ -49,20 +56,33 @@ var menu_user = {
type: { type: {
subsign: true, subsign: true,
}, },
}; }
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 = { var ui_main = {
rows: [ rows: [
{view: 'toolbar', padding: 3, elements: [ {view: 'toolbar', padding: 3, elements: [
{view: 'button', type: 'icon', icon: 'bars', {view: 'button', type: 'icon', icon: 'bars',
width: 37, align: 'left', css: 'app_button', click: function(){ width: 37, align: 'left', css: 'app_button', click: function(){
$$('$sidebar1').toggle() $$('main_sidebar').toggle()
} }
}, },
{view: 'label', label: 'Empresa Libre'}, {view: 'label', id: 'lbl_title_main', label: '<b>Empresa Libre</b>'},
{}, {},
{view: 'label', id: 'lbl_blog', label: link_blog, align: 'right', width: 30},
{view: 'label', id: 'lbl_forum', label: link_forum, align: 'right', width: 30},
{view: 'label', id: 'lbl_doc', label: link_doc, align: 'right', width: 25},
menu_user, menu_user,
{view: 'button', type: 'icon', width: 45, css: 'app_button', icon: 'bell-o', badge: 1} {view: 'button', id: 'cmd_update_timbres', type: 'icon', width: 45,
css: 'app_button', icon: 'bell-o', badge: 0},
{view: 'button', type: 'icon', width: 30, css: 'app_button',
icon: 'cogs', id: 'cmd_ir_al_admin', hidden: true,
click: 'cmd_ir_al_admin_click'}
] ]
}, },
{ {

View File

@ -0,0 +1,288 @@
var toolbar_nomina = [
{view: 'button', id: 'cmd_empleados', label: 'Empleados', type: 'iconButton',
autowidth: true, icon: 'users'},
{},
{view: 'button', id: 'cmd_nomina_report', label: 'Reporte', type: 'iconButton',
autowidth: true, icon: 'table'},
{},
{view: 'button', id: 'cmd_nomina_delete', label: 'Eliminar',
type: 'iconButton', autowidth: true, icon: 'minus'},
]
var toolbar_nomina_util = [
{view: 'button', id: 'cmd_nomina_import', label: 'Importar',
type: 'iconButton', autowidth: true, icon: 'upload'},
{view: 'button', id: 'cmd_nomina_timbrar', label: 'Timbrar',
type: 'iconButton', autowidth: true, icon: 'ticket'},
{view: 'button', id: 'cmd_nomina_sat', label: 'SAT',
type: 'iconButton', autowidth: true, icon: 'check-circle'},
{view: 'button', id: 'cmd_nomina_log', label: 'Log',
type: 'iconButton', autowidth: true, icon: 'download'},
{view: 'button', id: 'cmd_nomina_download', label: 'Descargar',
type: 'iconButton', autowidth: true, icon: 'download'},
{},
{view: 'button', id: 'cmd_nomina_cancel', label: 'Cancelar',
type: 'iconButton', autowidth: true, icon: 'ban'},
]
var toolbar_nomina_filter = [
{view: 'richselect', id: 'filter_year_nomina', label: 'Año',
labelAlign: 'right', labelWidth: 50, width: 150, options: []},
{view: 'richselect', id: 'filter_month_nomina', label: 'Mes',
labelAlign: 'right', labelWidth: 50, width: 200, options: months},
{view: 'daterangepicker', id: 'filter_dates_nomina', label: 'Fechas',
labelAlign: 'right', width: 300},
{},
{view: 'button', id: 'cmd_nomina_without_stamp', label: 'Sin Timbrar',
type: 'iconButton', autowidth: true, icon: 'filter'},
]
var grid_cols_nomina = [
{id: 'index', header: '#', adjust: 'data', css: 'right',
footer: {content: 'countRows', colspan: 3, css: 'right'}},
{id: "id", header:"ID", hidden:true},
{id: "serie", header: ["Serie"], adjust: "header"},
{id: 'folio', header: ['Folio', {content: 'numberFilter'}], adjust: 'header',
sort: 'int', css: 'right', footer: {text: 'Recibos', colspan: 3}},
{id: "uuid", header: ["UUID", {content: "textFilter"}], adjust: "data",
sort:"string", hidden:true},
{id: "fecha", header: ["Fecha y Hora"], adjust: "data", sort: "string"},
{id: "estatus", header: ["Estatus", {content: "selectFilter"}],
adjust: "data", sort:"string"},
{id: 'fecha_pago', header: ['Fecha de Pago', {content: 'selectFilter'}],
adjust: 'data', sort: 'string'},
{id: 'total', header: ['Total', {content: 'numberFilter'}], width: 150,
sort: 'int', format: webix.i18n.priceFormat, css: 'right',
footer: {content: 'summTimbradaN', css: 'right'}},
{id: "empleado", header: ["Empleado", {content: "selectFilter"}],
fillspace:true, sort:"string", footer: {text: '$ 0.00'}},
{id: 'xml', header: 'XML', adjust: 'data', template: get_icon('xml')},
{id: 'pdf', header: 'PDF', adjust: 'data', template: get_icon('pdf')},
{id: 'email', header: '@', adjust: 'data', template: get_icon('email')}
]
var grid_nomina = {
view: 'datatable',
id: 'grid_nomina',
select: 'row',
multiselect: true,
scrollY: true,
adjust: true,
footer: true,
resizeColumn: true,
headermenu: true,
columns: grid_cols_nomina,
scheme:{
$change:function(item){
if (item.estatus == 'Cancelado'){
item.$css = 'cancel'
}
}
},
on:{
'data->onStoreUpdated':function(){
this.data.each(function(obj, i){
obj.index = i + 1
})
}
},
}
var rows_nomina_home = [
{view: 'toolbar', elements: toolbar_nomina},
{view: 'toolbar', elements: toolbar_nomina_util},
{view: 'toolbar', elements: toolbar_nomina_filter},
grid_nomina,
]
var toolbar_nomina_empleados = [
{view: 'button', id: 'cmd_new_empleado', label: 'Nuevo', type: 'iconButton',
autowidth: true, icon: 'user-plus'},
{view: 'button', id: 'cmd_edit_empleado', label: 'Editar', type: 'iconButton',
autowidth: true, icon: 'user'},
{view: 'button', id: 'cmd_delete_empleado', label: 'Eliminar', type: 'iconButton',
autowidth: true, icon: 'user-times'},
{},
{view: 'button', id: 'cmd_import_empleados', label: 'Importar',
type: 'iconButton', autowidth: true, icon: 'upload'},
{},
{view: 'button', id: 'cmd_close_empleados', label: 'Cerrar', type: 'iconButton',
autowidth: true, icon: 'times-circle-o'},
]
var grid_cols_empleados = [
{id: 'index', header: '#', adjust: 'data', css: 'right',
footer: {content: 'countRows', colspan: 3, css: 'right'}},
{id: "id", header:"ID", hidden:true},
{id: "num_empleado", header: ["No Empleado"], adjust: "data"},
{id: "rfc", header: ["RFC", {content: 'textFilter'}], adjust: "data", sort: "string"},
{id: "curp", header: ["CURP"], adjust: "data", hidden:true, sort: "string"},
{id: "nombre_completo", header: ["Empleado", {content: 'textFilter'}],
adjust: "data", fillspace: true, sort: "string"},
{id: 'imss', header: ['IMSS'], adjust: 'data'},
{id: 'salario_base', header: ['Salario Base'], adjust: 'header',
format: webix.i18n.priceFormat, css: 'right'},
{id: 'salario_diario', header: ['Salario Diario'], adjust: 'header',
format: webix.i18n.priceFormat, css: 'right'},
{id: "fecha_ingreso", header: ["Fecha de Ingreso"], adjust: "header",
sort: "string"},
]
var grid_empleados = {
view: 'datatable',
id: 'grid_employees',
select: 'row',
scrollY: true,
adjust: true,
footer: true,
resizeColumn: true,
headermenu: true,
columns: grid_cols_empleados,
on:{
'data->onStoreUpdated':function(){
this.data.each(function(obj, i){
obj.index = i + 1
})
}
},
}
var rows_nomina_empleados = [
{view: 'toolbar', elements: toolbar_nomina_empleados},
grid_empleados,
]
var multi_nomina = {
id: 'multi_nomina',
view: 'multiview',
animate: true,
cells:[
{id: 'nomina_home', rows: rows_nomina_home},
{id: 'nomina_empleados', rows: rows_nomina_empleados},
],
}
var app_nomina = {
id: 'app_nomina',
rows:[
{view: 'template', type: 'header', template: 'Timbrado de Nómina'},
multi_nomina
],
}
var body_import_employees = {rows: [
{view: 'form', id: 'form_upload_employees', rows: [
{cols: [{},
{view: 'uploader', id: 'up_employees', autosend: false,
link: 'lst_upload_employees', value: 'Seleccionar Plantilla',
upload: '/files/employees'}, {}]},
{cols: [
{view: 'list', id: 'lst_upload_employees', name: 'lst_employees',
type: 'uploader', autoheight: true, borderless: true}]},
{cols: [{}, {view: 'button', id: 'cmd_import_employees',
label: 'Importar Empleados'}, {}]},
]},
],}
var win_import_employees = {
init: function(){
webix.ui({
view: 'window',
id: 'win_import_employees',
width: 400,
modal: true,
position: 'center',
head: 'Importar Empleados',
body: body_import_employees,
})
$$('cmd_import_employees').attachEvent('onItemClick', cmd_import_employees_click)
$$('up_employees').attachEvent('onUploadComplete', up_employees_upload_complete)
}
}
var body_import_nomina = {rows: [
{view: 'form', id: 'form_upload_nomina', rows: [
{cols: [{},
{view: 'uploader', id: 'up_nomina', autosend: false,
link: 'lst_upload_nomina', value: 'Seleccionar Plantilla',
upload: '/files/nomina'}, {}]},
{cols: [
{view: 'list', id: 'lst_upload_nomina', name: 'lst_nomina',
type: 'uploader', autoheight: true, borderless: true}]},
{cols: [{}, {view: 'button', id: 'cmd_import_template_nomina',
label: 'Importar Nómina'}, {}]},
]},
],}
var win_import_nomina = {
init: function(){
webix.ui({
view: 'window',
id: 'win_import_nomina',
width: 400,
modal: true,
position: 'center',
head: 'Importar Nómina',
body: body_import_nomina,
})
$$('cmd_import_template_nomina').attachEvent('onItemClick', cmd_import_template_nomina_click)
$$('up_nomina').attachEvent('onUploadComplete', up_nomina_upload_complete)
}
}
var opt_reasons_cancel_nomina = [
{id: '', value: ''},
{id: '01', value: '[01] Comprobante emitido con errores con relación'},
{id: '02', value: '[02] Comprobante emitido con errores sin relación'},
{id: '03', value: '[03] No se llevó acabo la operación'},
{id: '04', value: '[04] Operación nominativa relacionada en una factura global'},
]
var body_invoice_cancel_nomina = {rows: [{minHeight: 15},
{view: 'richselect', id: 'lst_reasons_cancel', labelPosition: 'top', label: 'Razón de cancelación', options: opt_reasons_cancel_pay, value: '', width: 400},
{view: 'text', id: 'txt_cancel_uuid', labelPosition: 'top', label: 'UUID que sustituye', width: 400},
{view: 'label', label: 'Esta acción no se puede deshacer', autowidth: true, align: 'center'},
{view: 'label', label: '¿Estás segura de continuar?', autowidth: true, align: 'center'},
{cols: [{},
{view: 'button', id: 'cmd_invoice_cancel_nomina', width: 100, label: 'Cancelar', type: 'danger', icon: 'ban'},
{maxWidth: 25},
{view: 'button', id: 'cmd_win_cancel_nomina_close', width: 100, label: 'Cerrar'},
{}
]},
{minHeight: 20},
]}
var win_invoice_cancel_nomina = {
init: function(){
webix.ui({
view: 'window',
id: 'win_invoice_cancel_nomina',
modal: true,
width: 400,
position: 'center',
head: 'Cancelar CFDI',
body: body_invoice_cancel_nomina,
})
$$('cmd_invoice_cancel_nomina').attachEvent('onItemClick', cmd_invoice_cancel_nomina_click)
$$('cmd_win_cancel_nomina_close').attachEvent('onItemClick', cmd_win_cancel_nomina_close_click)
}
}

View File

@ -1,3 +1,18 @@
//~ Empresa Libre
//~ Copyright (C) 2016-2018 Mauricio Baeza Servin (web@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
//~ the Free Software Foundation, either version 3 of the License, or
//~ (at your option) any later version.
//~
//~ This program is distributed in the hope that it will be useful,
//~ but WITHOUT ANY WARRANTY; without even the implied warranty of
//~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
//~ GNU General Public License for more details.
//~
//~ You should have received a copy of the GNU General Public License
//~ along with this program. If not, see <http://www.gnu.org/licenses/>.
var toolbar_partners = [ var toolbar_partners = [
@ -7,17 +22,22 @@ var toolbar_partners = [
autowidth: true, icon: 'user'}, autowidth: true, icon: 'user'},
{view: 'button', id: 'cmd_delete_partner', label: 'Eliminar', type: 'iconButton', {view: 'button', id: 'cmd_delete_partner', label: 'Eliminar', type: 'iconButton',
autowidth: true, icon: 'user-times'}, autowidth: true, icon: 'user-times'},
{},
//~ {view: 'button', id: 'cmd_partner_zero', label: 'Saldo 0', type: 'iconButton',
//~ autowidth: true, icon: 'power-off', disabled: true},
] ]
var grid_partners_cols = [ var grid_partners_cols = [
{id: 'index', header:'#', adjust:'data', css: 'right', {id: 'index', header:'#', css: 'right',
footer: {content: 'rowCount', colspan: 2, css: 'right'}}, footer: {content: 'countRows', colspan: 2, css: 'right'}},
{id: 'id', header: 'Clave', adjust:'data', sort: 'int', css: 'right'}, {id: 'id', header: 'Clave', sort: 'int', css: 'right'},
{id: 'rfc', header: ['RFC', {content: 'textFilter'}], adjust:'data', {id: 'rfc', header: ['RFC', {content: 'textFilter'}], adjust: 'data',
sort: 'string', footer: {text: 'Clientes y Proveedores', colspan: 2}}, sort: 'string', footer: {text: 'Clientes y Proveedores', colspan: 2}},
{id: 'nombre', header: ['Razón Social', {content: 'textFilter'}], {id: 'nombre', header: ['Razón Social', {content: 'textFilter'}],
fillspace:true, sort: 'string'}, fillspace:true, sort: 'string'},
{id: 'saldo_cliente', header: ['Saldo Cliente', {content: 'numberFilter'}],
width: 150, sort: 'int', format: webix.i18n.priceFormat, css: 'right'},
] ]
@ -30,6 +50,15 @@ var grid_partners = {
resizeColumn: true, resizeColumn: true,
headermenu: true, headermenu: true,
columns: grid_partners_cols, columns: grid_partners_cols,
pager: 'pager_clientes',
//~ datafetch: 100,
//~ loadahead: 100,
//~ url: '/partners',
ready:function(){
this.adjustColumn('index');
this.adjustColumn('id');
this.adjustColumn('rfc');
},
on:{ on:{
'data->onStoreUpdated':function(){ 'data->onStoreUpdated':function(){
this.data.each(function(obj, i){ this.data.each(function(obj, i){
@ -55,7 +84,10 @@ var controls_fiscales = [
invalidMessage: 'El Tipo de Persona es requerido'}, invalidMessage: 'El Tipo de Persona es requerido'},
{template: 'Dirección Fiscal', type: 'section'}, {template: 'Dirección Fiscal', type: 'section'},
{cols: [{view: 'text', id: 'rfc', name: 'rfc', label: 'RFC: ', width: 300, {cols: [{view: 'text', id: 'rfc', name: 'rfc', label: 'RFC: ', width: 300,
required: true, invalidMessage: 'RFC inválido', attributes: {maxlength: 13}},{}]}, required: true, invalidMessage: 'RFC inválido', attributes: {maxlength: 13}},
{view: 'text', id: 'id_fiscal', name: 'id_fiscal', label: 'ID Fiscal: ',
width: 400, required: false, attributes: {maxlength: 40}, hidden: true},
{}]},
{view: 'text', id: 'nombre', name: 'nombre', label: 'Razón Social: ', required: true, {view: 'text', id: 'nombre', name: 'nombre', label: 'Razón Social: ', required: true,
invalidMessage: 'La Razón Social es requerida'}, invalidMessage: 'La Razón Social es requerida'},
{view: 'text', id: 'calle', name: 'calle', label: 'Calle: '}, {view: 'text', id: 'calle', name: 'calle', label: 'Calle: '},
@ -64,19 +96,19 @@ var controls_fiscales = [
{cols: [{view: 'text', id: 'no_interior', name: 'no_interior', width: 300, {cols: [{view: 'text', id: 'no_interior', name: 'no_interior', width: 300,
label: 'No Interior: '},{}]}, label: 'No Interior: '},{}]},
{cols: [{view: 'search', id: 'codigo_postal', name: 'codigo_postal', {cols: [{view: 'search', id: 'codigo_postal', name: 'codigo_postal',
width: 300, label: 'C.P.: ', attributes: {maxlength: 5}},{}]}, width: 300, label: 'C.P.: ', attributes: {maxlength: 5}, required: true},{}]},
{view: 'text', id: 'colonia', name: 'colonia', label: 'Colonia: '}, {view: 'text', id: 'colonia', name: 'colonia', label: 'Colonia: '},
{view: 'text', id: 'municipio', name: 'municipio', label: 'Municipio: '}, {view: 'text', id: 'municipio', name: 'municipio', label: 'Municipio: '},
{view: 'text', id: 'estado', name: 'estado', label: 'Estado: '}, {view: 'text', id: 'estado', name: 'estado', label: 'Estado: '},
{view: 'text', id: 'pais', name: 'pais', label: 'País: ', {view: 'text', id: 'pais', name: 'pais', label: 'País: ', value: 'México',
value: 'México', readonly: true}, readonly: true, placeholder: 'Usa solo tres letras de acuerdo al catalogo del SAT'},
{template: 'Condiciones Comerciales', type: 'section'}, {template: 'Condiciones Comerciales', type: 'section'},
{cols: [ {cols: [
{view: 'richselect', id: 'forma_pago', name: 'forma_pago', {view: 'richselect', id: 'forma_pago', name: 'forma_pago',
label: 'Forma de Pago: ', required: true, options: [], label: 'Forma de Pago: ', required: true, options: [],
invalidMessage: 'La Forma de pago es requerida'}, invalidMessage: 'La Forma de pago es requerida'},
{view: 'text', id: 'condicion_pago', name: 'condicion_pago', {view: 'text', id: 'condicion_pago', name: 'condicion_pago',
label: 'Condiciones de Pago: '}, label: 'Condiciones de Pago: ', suggest: []},
]}, ]},
{cols: [ {cols: [
{view: 'counter', id: 'dias_pago', name: 'dias_pago', {view: 'counter', id: 'dias_pago', name: 'dias_pago',
@ -90,25 +122,41 @@ var controls_fiscales = [
{view: 'richselect', id: 'lst_uso_cfdi_socio', name: 'uso_cfdi_socio', {view: 'richselect', id: 'lst_uso_cfdi_socio', name: 'uso_cfdi_socio',
label: 'Uso del CFDI', options: []}, label: 'Uso del CFDI', options: []},
{}, {},
]},
{template: 'Regimenes Fiscales', type: 'section'},
{cols: [
{view: 'list', id: 'lst_receptor_regimenes_fiscales', data: [],
select: 'multiselect', width: 600, height: 125, required: true},
{},
]} ]}
] ]
var controls_others = [ var controls_others = [
{view: 'checkbox', id: 'es_activo', name: 'es_activo', label: 'Activo: ', {view: 'checkbox', id: 'es_activo', name: 'es_activo', label: 'Activo: ',
value: true, bottomLabel: '&nbsp;&nbsp;&nbsp;Se recomienda solo desactivar y no eliminar'}, value: true, bottomLabel: '&nbsp;&nbsp;Se recomienda solo desactivar y no eliminar'},
{view: 'text', id: 'commercial_name', name: 'nombre_comercial', {view: 'text', id: 'commercial_name', name: 'nombre_comercial',
label: 'Nombre Comercial: '}, label: 'Nombre Comercial: '},
{view: 'text', id: 'telefonos', name: 'telefonos', label: 'Teléfonos: '}, {view: 'text', id: 'telefonos', name: 'telefonos', label: 'Teléfonos: '},
{view: 'text', id: 'web', name: 'web', label: 'Página Web: '}, {view: 'text', id: 'web', name: 'web', label: 'Página Web: '},
{cols: [
{view: 'text', id: 'correo_facturas', name: 'correo_facturas', {view: 'text', id: 'correo_facturas', name: 'correo_facturas',
label: 'Correos para Facturas: ', tooltip: 'Separados por comas', label: 'Correos para Facturas: ', tooltip: 'Separados por comas',
bottomLabel: 'Uno o más correos electrónicos separados por comas'}, bottomLabel: 'Uno o más correos electrónicos separados por comas'},
{view: 'text', id: 'partner_email_fp', name: 'partner_email_fp',
label: 'Para Facturas de pago: ', tooltip: 'Separados por comas',
bottomLabel: 'Uno o más correos electrónicos separados por comas'},
]},
{cols: [ {cols: [
{view: 'checkbox', id: 'es_cliente', name: 'es_cliente', {view: 'checkbox', id: 'es_cliente', name: 'es_cliente',
label: 'Es Cliente: ', value: true, width: 180}, label: 'Es Cliente: ', value: true, width: 180},
{view: 'text', id: 'cuenta_cliente', name: 'cuenta_cliente', {view: 'text', id: 'cuenta_cliente', name: 'cuenta_cliente',
label: 'Cuenta Cliente: ', disabled: true}, {}] label: 'Cuenta Cliente: ', disabled: true},
{view: "currency", type: "text", id: 'partner_balance', width: 300,
name: 'partner_balance', label: 'Saldo', labelWidth: 100,
labelAlign: "right", required: true, inputAlign: "right",
invalidMessage: "Captura un valor númerico", readonly: true},
]
}, },
{cols: [ {cols: [
{view: 'checkbox', id: 'es_proveedor', name: 'es_proveedor', {view: 'checkbox', id: 'es_proveedor', name: 'es_proveedor',
@ -117,12 +165,56 @@ var controls_others = [
label: 'Cuenta Proveedor: ', disabled: true}, {}] label: 'Cuenta Proveedor: ', disabled: true}, {}]
}, },
{view: 'checkbox', name: 'es_ong', label: 'Es ONG: ', value: false}, {view: 'checkbox', name: 'es_ong', label: 'Es ONG: ', value: false},
{view: 'text', name: 'tags', label: 'Etiquetas', {view: 'text', name: 'tags', label: 'Etiquetas', disabled: true,
tooltip: 'Utiles para filtrados rápidos. Separa por comas.'}, tooltip: 'Utiles para filtrados rápidos. Separa por comas.'},
{view: 'textarea' , height: 200, name: 'notas', label: 'Notas'}, {view: 'textarea' , height: 200, name: 'notas', label: 'Notas'},
] ]
var grid_partner_account_bank_cols = [
{id: 'id', header: 'ID', hidden: true},
{id: 'delete', header: '', width: 30, css: 'delete'},
{id: 'banco', header: 'Banco', fillspace: 1},
{id: 'cuenta', header: 'Cuenta', fillspace: 1},
{id: 'clabe', header: 'CLABE', fillspace: 1},
//~ {id: 'moneda', header: 'Moneda', fillspace: 1},
]
var grid_partner_account_bank = {
view: 'datatable',
id: 'grid_partner_account_bank',
select: 'row',
adjust: true,
autoheight: true,
columns: grid_partner_account_bank_cols,
}
var controls_partner_bank = [
{template: 'Agregar cuenta de banco', type: 'section'},
{view: 'form', id: 'form_partner_account_bank', rows: [
{cols: [
{view: 'richselect', id: 'lst_partner_bank', name: 'partner_bank',
label: 'Banco: ', required: true, options: []},
{view: 'text', id: 'partner_account', name: 'partner_account',
label: 'Cuenta: ', required: true},
{view: 'text', id: 'partner_clabe', name: 'partner_clabe',
label: 'CLABE: ', required: true},
]},
{minHeight: 10},
{cols: [{},
{view: 'button', id: 'cmd_partner_add_account_bank', maxWidth: 200,
label: 'Agregar cuenta'},
{}]},
],
},
{minHeight: 20, maxHeight: 20},
{template: 'Cuentas de banco existentes', type: 'section'},
grid_partner_account_bank,
{minHeight: 50},
]
var toolbar_contacts = [ var toolbar_contacts = [
{view: 'button', id: 'cmd_new_contact', label: 'Nuevo', type: 'iconButton', {view: 'button', id: 'cmd_new_contact', label: 'Nuevo', type: 'iconButton',
@ -132,7 +224,7 @@ var toolbar_contacts = [
var grid_contacts_cols = [ var grid_contacts_cols = [
{id: 'index', header: '#', adjust:'data', css:'right', {id: 'index', header: '#', adjust:'data', css:'right',
footer: {content: 'rowCount'}}, footer: {content: 'countRows'}},
{id: 'id', header: '', hidden: true}, {id: 'id', header: '', hidden: true},
{id: 'title', header: 'Título', adjust:'data', sort: 'string', {id: 'title', header: 'Título', adjust:'data', sort: 'string',
footer: 'Contactos'}, footer: 'Contactos'},
@ -260,11 +352,12 @@ var controls_partner = [
{ {
view: 'tabview', view: 'tabview',
id: 'tab_partner', id: 'tab_partner',
tabbar: {options: ['Datos Fiscales', 'Otros Datos', 'Contactos']}, animate: true, tabbar: {ptions: ['Datos Fiscales', 'Otros Datos', 'Cuentas de Banco']},
animate: true,
cells: [ cells: [
{id: 'Datos Fiscales', rows: controls_fiscales}, {id: 'Datos Fiscales', rows: controls_fiscales},
{id: 'Otros Datos', rows: controls_others}, {id: 'Otros Datos', rows: controls_others},
{id: 'Contactos', rows: [multi_contacts]}, {id: 'Cuentas de Banco', rows: controls_partner_bank}
] ]
}, },
{rows: [ {rows: [
@ -284,12 +377,12 @@ var form_partner = {
view: 'form', view: 'form',
id: 'form_partner', id: 'form_partner',
complexData: true, complexData: true,
scroll: true,
elements: controls_partner, elements: controls_partner,
elementsConfig: { elementsConfig: {
labelWidth: 150, labelWidth: 150,
labelAlign: 'right' labelAlign: 'right'
}, },
autoheight: true,
rules: { rules: {
nombre: function(value){ return value.trim() != '';}, nombre: function(value){ return value.trim() != '';},
rfc: validate_rfc, rfc: validate_rfc,
@ -298,6 +391,15 @@ var form_partner = {
} }
var pager_clientes = {
view: "pager",
id: "pager_clientes",
template: "{common.prev()} {common.pages()} {common.next()}",
size: 100,
group: 10,
}
var multi_partners = { var multi_partners = {
id: 'multi_partners', id: 'multi_partners',
animate: true, animate: true,
@ -305,8 +407,9 @@ var multi_partners = {
{id: 'partners_home', rows:[ {id: 'partners_home', rows:[
{view: 'toolbar', elements: toolbar_partners}, {view: 'toolbar', elements: toolbar_partners},
grid_partners, grid_partners,
pager_clientes,
]}, ]},
{id: 'partners_new', rows:[form_partner, {}]} {id: 'partners_new', rows:[form_partner]}
] ]
} }

View File

@ -7,38 +7,60 @@ var toolbar_products = [
autowidth: true, icon: "pencil"}, autowidth: true, icon: "pencil"},
{view: "button", id: "cmd_delete_product", label: "Eliminar", {view: "button", id: "cmd_delete_product", label: "Eliminar",
type: "iconButton", autowidth: true, icon: "minus"}, type: "iconButton", autowidth: true, icon: "minus"},
{},
{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'},
] ]
var grid_products_cols = [ var grid_products_cols = [
{ id: "id", header: "ID", width: 75}, { id: "id", header: "ID", width: 75, hidden: true},
{ id: 'es_activo', header: 'Activo', hidden: true},
{ id: "clave_sat", header: ["Clave SAT"], width: 100,},
{ id: "clave", header: ["Clave", {content: "textFilter"}], width: 100, { id: "clave", header: ["Clave", {content: "textFilter"}], width: 100,
sort:"string" }, sort: 'string', footer: {content: 'countRows', css: 'right'}},
{ id: "descripcion", header: ["Descripción", {content: "textFilter"}], { id: "descripcion", header: ["Descripción", {content: "textFilter"}],
fillspace:true, sort:"string" }, fillspace:true, sort: 'string', footer: 'Productos y Servicios'},
{ id: "unidad", header: ["Unidad", {content: "selectFilter"}], width: 150, { id: "unidad", header: ["Unidad", {content: "selectFilter"}], width: 150,
sort:"string" }, sort:"string" },
{ id: "valor_unitario", header: ["Precio", {content: "numberFilter"}], width: 150, { id: "valor_unitario", header: ["Precio", {content: "numberFilter"}],
sort:"int", format: webix.i18n.priceFormat, css: "right" }, width: 150, sort: 'int', format: format_currency, css: "right" },
{ id: 'existencia', header: ['Existencia', {content: 'numberFilter'}],
width: 100, sort: 'int', format: webix.i18n.numberFormat,
hidden: true, css: 'right' },
] ]
var grid_products = { var grid_products = {
view: "datatable", view: 'datatable',
id: "grid_products", id: 'grid_products',
select: "row", select: 'row',
adjust: true, adjust: true,
footer: true, footer: true,
resizeColumn: true, resizeColumn: true,
headermenu: true, headermenu: true,
columns: grid_products_cols, columns: grid_products_cols,
scheme:{
$change:function(item){
if(!item.es_activo){
item.$css = 'cancel'
}
}
},
} }
var suggest_categories = { var suggest_categories = {
view: "datasuggest", view: 'datasuggest',
type: "tree", type: 'tree',
width: 400, width: 400,
body: { data: [] }, body: {data: []},
} }
@ -63,19 +85,55 @@ var grid_product_taxes = {
} }
var suggest_sat_producto = {
view: 'gridsuggest',
id: 'grid_producto_found',
name: 'grid_producto_found',
body: {
autoConfig: false,
scroll: true,
autoheight: false,
header: true,
yCount: 10,
columns: [
{id: 'id', hidden: true},
{id: 'key', adjust: 'data', header: 'Clave SAT'},
{id: 'name', header: 'Producto', width: 750},
],
dataFeed:function(text){
if (text.length > 3){
this.load('/values/satproductos?key=' + text)
}else{
this.hide()
}
}
},
}
var opt_tax_object = [
{id: '01', value: '[01] No objeto de impuesto.'},
{id: '02', value: '[02] Sí objeto de impuesto.'},
{id: '03', value: '[03] Sí objeto del impuesto y no obligado al desglose.'},
{id: '04', value: '[04] Sí objeto del impuesto y no causa impuesto.'},
{id: '05', value: '[05] Sí objeto del impuesto, IVA crédito PODEBI.'},
]
var controls_generals = [ var controls_generals = [
{view: 'checkbox', id: 'es_activo_producto', name: 'es_activo_producto', {view: 'checkbox', id: 'es_activo_producto', name: 'es_activo_producto',
label: 'Activo: ', value: true, label: 'Activo: ', value: true,
bottomLabel: 'Se recomienda solo desactivar y no eliminar'}, bottomLabel: 'Se recomienda solo desactivar y no eliminar'},
{cols: [ {cols: [
{view: 'combo', id: 'categoria', name: 'categoria', label: 'Categoría', {view: 'combo', id: 'categoria', name: 'categoria', label: 'Categoría',
labelPosition: 'top', options: suggest_categories}, labelPosition: 'top', suggest: suggest_categories},
{view: 'text', id: 'clave', name: 'clave', label: 'Clave', {view: 'text', id: 'clave', name: 'clave', label: 'Clave',
labelPosition: 'top', readonly: true, required: true}, labelPosition: 'top', readonly: true, required: true},
{view: 'checkbox', id: 'chk_automatica', label: 'Automática', {view: 'checkbox', id: 'chk_automatica', label: 'Automática',
labelPosition: 'top', value: true, maxWidth: 80}, labelPosition: 'top', value: true, maxWidth: 80},
{view: 'search', id: 'clave_sat', name: 'clave_sat', label: 'Clave SAT', {view: 'search', id: 'clave_sat', name: 'clave_sat', label: 'Clave SAT',
labelPosition: 'top', required: true, placeholder: 'Buscar clave...'}, labelPosition: 'top', required: true, suggest: suggest_sat_producto,
placeholder: 'Al menos 4 caracteres...'},
]}, ]},
{cols: [ {cols: [
{view: 'text', id: 'codigo_barras', name: 'codigo_barras', {view: 'text', id: 'codigo_barras', name: 'codigo_barras',
@ -85,28 +143,37 @@ var controls_generals = [
{id: 'txt_col1'}]}, {id: 'txt_col1'}]},
{view: "textarea", id: "descripcion", name: "descripcion", height: 200, {view: "textarea", id: "descripcion", name: "descripcion", height: 200,
label: "Descripción", required: true, labelPosition: "top", label: "Descripción", required: true, labelPosition: "top",
invalidMessage: "La Descripción es requerida" }, invalidMessage: "La Descripción es requerida",
placeholder: 'El SAT solo permite 1000 caracteres en este campo'},
{minHeight: 5}, {minHeight: 5},
{cols: [ {cols: [
{view: "richselect", id: "unidad", name: "unidad", label: "Unidad", {view: "richselect", id: "unidad", name: "unidad", label: "Unidad",
width: 300, labelWidth: 130, labelAlign: "right", required: true, width: 300, labelWidth: 130, labelAlign: "right", required: true,
invalidMessage: "La Unidad es requerida", options: []}, invalidMessage: "La Unidad es requerida", options: []},
{view: 'text', id: 'tags_producto', name: 'tags_producto', {view: 'richselect', id: 'objeto_impuesto', name: 'objeto_impuesto', label: 'Objeto de Impuestos',
labelAlign: 'right', label: 'Etiquetas', width: 500, labelWidth: 150, labelAlign: "right", required: true,
placeholder: 'Separadas por comas'} invalidMessage: 'Este campo es requerido', options: opt_tax_object},
{},
]}, ]},
{cols: [{view: "currency", type: "text", id: "valor_unitario",
name: "valor_unitario", label: "Valor Unitario", width: 300,
labelWidth: 130, labelAlign: "right", required: true,
invalidMessage: "Captura un valor númerico", inputAlign: "right" },{}]},
{cols: [ {cols: [
{view: 'checkbox', id: 'inventario', name: 'inventario', hidden: true, {view: "currency", type: "text", id: "valor_unitario",
name: "valor_unitario", label: "Valor Unitario", width: 300,
labelWidth: 130, labelAlign: "right", required: true,
invalidMessage: "Captura un valor númerico", inputAlign: "right"},
{view: 'currency', type: 'text', id: 'precio_con_impuestos',
name: 'precio_con_impuestos', label: 'Con Impuestos', width: 300,
labelWidth: 115, labelAlign: 'right', required: false,
invalidMessage: 'Captura un valor númerico', inputAlign: 'right'},
{},]},
{cols: [
{view: 'checkbox', id: 'chk_inventario', name: 'inventario', hidden: true,
label: 'Inventario', labelAlign: 'right', labelWidth: 130}, label: 'Inventario', labelAlign: 'right', labelWidth: 130},
{view: 'counter', id: 'existencia', name: 'existencia', hidden: true, {view: 'counter', id: 'txt_existencia', name: 'existencia',
label: 'Existencia', step: 5, value: 0, min: 0, disabled: true}, hidden: true, label: 'Existencia', step: 5, value: 0, min: 0,
{view: 'counter', id: 'minimo', name: 'minimo', hidden: 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}, label: 'Mínimo', step: 5, value: 0, min: 0, disabled: true},
{id: 'txt_col2'}]}, {},]},
{cols:[{view:'label', label:'Impuestos', width: 300, align:'center'}, {}]}, {cols:[{view:'label', label:'Impuestos', width: 300, align:'center'}, {}]},
{cols:[grid_product_taxes, {}]} {cols:[grid_product_taxes, {}]}
] ]
@ -121,8 +188,8 @@ var controls_products = [
], ],
}, },
{rows: [ {rows: [
{ template:"", type: "section" }, {template: "", type: "section"},
{ margin: 10, cols: [{}, {margin: 10, cols: [{},
{view: "button", id: "cmd_save_product", label: "Guardar" , type: "form", autowidth: true, align:"center"}, {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"}, {view: "button", id: "cmd_cancel_product", label: "Cancelar" , type: "danger", autowidth: true, align:"center"},
{}] {}]
@ -136,7 +203,7 @@ var form_product = {
cols: [{ cols: [{
view: "form", view: "form",
id: "form_product", id: "form_product",
//~ width: 600, scroll: true,
complexData: true, complexData: true,
elements: controls_products, elements: controls_products,
rules: { rules: {
@ -147,6 +214,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 = { var multi_products = {
id: "multi_products", id: "multi_products",
animate: true, animate: true,
@ -155,17 +345,197 @@ var multi_products = {
{view:"toolbar", elements: toolbar_products}, {view:"toolbar", elements: toolbar_products},
grid_products, grid_products,
]}, ]},
{id: "product_new", rows:[form_product, {}]} {id: "product_new", rows:[form_product]},
{id: "product_add", rows:[form_products_add]}
], ],
} }
var app_products = { var app_products = {
id: "app_products", id: 'app_products',
rows:[ rows:[
{view: "template", id: "th_products", type: "header", template:"Administración de Productos" }, {view: 'template', id: 'th_products', type: 'header',
template: 'Administración de Productos y Servicios'},
multi_products multi_products
], ],
} }
var body_import_products = {rows: [
{view: 'form', id: 'form_upload_products', rows: [
{cols: [{},
{view: 'uploader', id: 'up_products', autosend: false,
link: 'lst_upload_products', value: 'Seleccionar Archivo',
upload: '/files/products'}, {}]},
{cols: [
{view: 'list', id: 'lst_upload_products', name: 'lst_upload_products',
type: 'uploader', autoheight: true, borderless: true}]},
{cols: [{}, {view: 'button', id: 'cmd_upload_products',
label: 'Importar Productos'}, {}]},
]},
]}
var win_import_products = {
init: function(){
webix.ui({
view: 'window',
id: 'win_import_products',
width: 400,
modal: true,
position: 'center',
head: 'Importar Productos',
body: body_import_products,
})
$$('cmd_upload_products').attachEvent('onItemClick', cmd_upload_products_click)
$$('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

@ -0,0 +1,146 @@
var toolbar_students = [
{view: 'button', id: 'cmd_new_student', label: 'Nuevo', type: 'iconButton',
autowidth: true, icon: 'user-plus'},
{view: 'button', id: 'cmd_edit_student', label: 'Editar', type: 'iconButton',
autowidth: true, icon: 'user'},
{view: 'button', id: 'cmd_delete_student', label: 'Eliminar', type: 'iconButton',
autowidth: true, icon: 'user-times'},
]
var grid_cols_students = [
{id: 'index', header:'#', css: 'right',
footer: {content: 'countRows', colspan: 2, css: 'right'}},
{id: 'id', header: 'Clave', sort: 'int', css: 'right'},
{id: 'nombre', header: ['Nombre', {content: 'textFilter'}],
sort: 'string'},
{id: 'paterno', header: ['A. Paterno', {content: 'textFilter'}],
sort: 'string'},
{id: 'materno', header: ['A. Materno', {content: 'textFilter'}],
sort: 'string'},
{id: 'rfc', header: ['RFC', {content: 'textFilter'}], adjust: 'data',
sort: 'string'},
{id: 'curp', header: ['CURP'], sort: 'string', adjust: 'data'},
]
var grid_students = {
view: 'datatable',
id: 'grid_students',
select: 'row',
adjust: true,
footer: true,
resizeColumn: true,
headermenu: true,
columns: grid_cols_students,
ready:function(){
this.adjustColumn('index');
this.adjustColumn('id');
this.adjustColumn('nombre');
this.adjustColumn('rfc');
this.adjustColumn('curp');
},
on:{
'data->onStoreUpdated':function(){
this.data.each(function(obj, i){
obj.index = i+1;
})
}
},
}
var rows_school_home = [
{view: 'toolbar', elements: toolbar_students},
grid_students,
]
var student_controls_generales = [
{view: 'text', id: 'student_name', name: 'nombre', label: 'Nombre: ',
required: true, invalidMessage: 'El nombre es requerido'},
{view: 'text', id: 'student_paterno', name: 'paterno', label: 'Apellido Paterno: ',
required: true, invalidMessage: 'El apellido paterno es requerido'},
{view: 'text', id: 'student_materno', name: 'materno',
label: 'Apellido Materno: '},
{cols: [
{view: 'text', id: 'student_rfc', name: 'rfc', label: 'RFC: ',
invalidMessage: 'RFC inválido', adjust: 'data',
attributes: {maxlength: 13}},
{view: 'text', id: 'student_curp', name: 'curp', label: 'CURP: ',
required: true, invalidMessage: 'CURP inválido', adjust: 'data',
attributes: {maxlength: 20}},
{}]},
{cols: [
{view: 'richselect', id: 'student_grupo', name: 'grupo',
label: 'Nivel Educativo: ', required: true, options: [],
invalidMessage: 'El Nivel Educativo es requerido'},
{},
]},
]
var form_controls_student = [
{
view: 'tabview',
id: 'tab_student',
tabbar: {options: ['Datos Generales']}, animate: true,
cells: [
{id: 'Datos Generales', rows: student_controls_generales},
]
},
{rows: [
{ template:"", type: "section" },
{ margin: 10, cols: [{},
{view: "button", id: "cmd_save_student", label: "Guardar" ,
type: "form", autowidth: true, align: "center"},
{view: "button", id: "cmd_cancel_student", label: "Cancelar" ,
type: "danger", autowidth: true, align: "center"},
{}]
},
]}
]
var form_student = {
type: 'space',
cols: [{
view: 'form',
id: 'form_student',
complexData: true,
scroll: true,
elements: form_controls_student,
elementsConfig: {
labelWidth: 150,
labelAlign: 'right'
},
rules: {
nombre: function(value){ return value.trim() != '';},
curp: validate_curp,
}
}]
}
var multi_school = {
id: 'multi_school',
view: 'multiview',
animate: true,
cells:[
{id: 'school_home', rows: rows_school_home},
{id: 'school_groups', rows: []},
{id: 'new_student', rows: [form_student]},
],
}
var app_school = {
id: 'app_school',
rows:[
{view: 'template', id: 'th_school', type: 'header',
template: 'Administración de Escuela'},
multi_school
],
}

View File

@ -0,0 +1,492 @@
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'},
{},
{view: 'button', id: 'cmd_ticket_report_pdf', label: 'Reporte',
type: 'iconButton', autowidth: true, icon: 'file-pdf-o'},
{view: 'button', id: 'cmd_ticket_report_xls', label: 'Reporte',
type: 'iconButton', autowidth: true, icon: 'table'},
{},
{view: 'button', id: 'cmd_cancelar_ticket', label: 'Cancelar',
type: 'iconButton', autowidth: true, icon: 'ban'},
]
var toolbar_tickets_filter = [
{view: 'button', id: 'cmd_ticket_filter_today', label: 'Hoy', type: 'iconButton',
autowidth: true, icon: 'filter'},
{view: 'richselect', id: 'filter_year_ticket', label: 'Año',
labelAlign: 'right', labelWidth: 50, width: 150, options: []},
{view: 'richselect', id: 'filter_month_ticket', label: 'Mes',
labelAlign: 'right', labelWidth: 50, width: 200, options: months},
{view: 'daterangepicker', id: 'filter_dates_ticket', label: 'Fechas',
labelAlign: 'right', width: 300},
]
var grid_tickets_cols = [
{id: 'index', header: '#', adjust: 'data', css: 'right',
footer: {content: 'countRows', colspan: 3, css: 'right'}},
{id: "id", header:"ID", hidden:true},
{id: "serie", header: ["Serie", {content: "selectFilter"}], adjust: "header",
sort: "string"},
{id: 'folio', header: ['Folio', {content: 'numberFilter'}], adjust: 'header',
sort: 'int', css: 'right', footer: {text: 'Tickets', colspan: 3}},
{id: "fecha", header: ["Fecha y Hora"],
adjust: "data", sort: "date"},
{id: "estatus", header: ["Estatus", {content: "selectFilter"}],
adjust: "data", sort:"string"},
{id: 'total', header: ['Total', {content: 'numberFilter'}], width: 150,
sort: 'int', format: webix.i18n.priceFormat, css: 'right',
footer: {content: 'summActive', css: 'right'}},
{id: "cliente", header: ["Razón Social", {content: "selectFilter"}],
fillspace:true, sort:"string", hidden: true},
{id: 'pdf', header: 'PDF', adjust: 'data', template: get_icon('pdf')},
{id: 'print', header: '', adjust: 'data', template: get_icon('print')},
]
var grid_tickets = {
view: 'datatable',
id: 'grid_tickets',
select: 'row',
scrollY: true,
adjust: true,
footer: true,
resizeColumn: true,
headermenu: true,
columns: grid_tickets_cols,
scheme:{
$change:function(item){
if (item.estatus == 'Cancelado'){
item.$css = 'cancel'
}
}
},
on:{
'data->onStoreUpdated':function(){
this.data.each(function(obj, i){
obj.index = i + 1
})
}
},
}
var rows_tickets_home = [
{view: 'toolbar', elements: toolbar_tickets},
{view: 'toolbar', elements: toolbar_tickets_filter},
grid_tickets,
]
var ticket_suggest_products = {
view: 'gridsuggest',
id: 'gt_productos_found',
name: 'gt_productos_found',
body: {
autoConfig: false,
header: true,
columns: [
{id: 'id', hidden: true},
{id: 'clave', header: 'Clave', adjust: 'data'},
{id: 'descripcion', header: 'Descripción', width: 500},
{id: 'unidad', header: 'Unidad', adjust: 'data'},
{id: 'valor_unitario', header: 'Valor Unitario', adjust: 'data',
format: webix.i18n.priceFormat}
],
dataFeed:function(text){
if (text.length > 2){
this.load('/values/product?name=' + text)
}else{
this.hide()
}
}
},
}
var tbody_buscar_producto = {rows: [
{cols: [
{view: 'search', id: 'tsearch_product_key', name: 'tsearch_product_key',
label: 'por Clave', labelPosition:'top', maxWidth: 250,
placeholder: 'Presiona ENTER para buscar'},
{view: 'search', id: 'tsearch_product_name', name: 'tsearch_product_name',
label: 'por Descripción', labelPosition:'top',
suggest: ticket_suggest_products, placeholder: 'Captura al menos tres letras'},
]},
]}
var grid_tdetails_cols = [
{id: "id_product", header:"ID", hidden: true},
{id: 'delete', header: '', width: 30, css: 'delete'},
{id: "clave", header:{text: 'Clave', css: 'center'}, width: 100,
footer: {text: 'Artículos', css: 'right_footer3'}},
{id: "clave_sat", hidden: true},
{id: "descripcion", header:{text: 'Descripción', css: 'center'},
fillspace: true, footer: {content: 'countRows', css: 'footer3'}},
{id: "unidad", header:{text: 'Unidad', css: 'center'}, width: 100,
editor: 'select', options: 'values/unidades'},
{id: 'cantidad', header: {text: 'Cantidad', css: 'center'}, width: 100,
format: webix.i18n.numberFormat, css: 'right', editor: 'text',
footer: {content: 'summColumn', css: 'right_footer3'}},
{id: "valor_unitario", header:{text: 'Valor Unitario', css: 'center'},
width: 100, format: webix.i18n.priceFormat, css: 'right', editor: 'text',
footer: {text: 'Total ', css: 'right_footer2'}},
{id: 'descuento', header: {text: 'Descuento', css: 'center'}, hidden: true,
width: 80, format: webix.i18n.priceFormat, css: 'right', editor: 'text'},
{id: 'precio_final', hidden: true, header: 'precio_final', width: 80,
format: webix.i18n.priceFormat, css: 'right'},
{id: "importe", header:{text: 'Importe', css: 'center'}, width: 150,
format: webix.i18n.priceFormat, css: 'right',
footer: {content: 'summColumn', css: 'right_footer2'}},
{id: "inventario", hidden: true},
{id: "existencia", hidden: true},
]
var grid_tdetails = {
view: 'datatable',
id: 'grid_tdetails',
select: 'row',
adjust: true,
autoheight: true,
editable: true,
footer: true,
columns: grid_tdetails_cols,
on:{
'data->onStoreUpdated':function(){
this.data.each(function(obj, i){
obj.delete = '-'
})
}
},
data: [],
}
var body_ticket_informacion = {rows: [
{view: 'richselect', id: 'lst_ticket_forma_pago', name: 'forma_pago',
label: 'Forma de Pago', labelPosition: 'top', required: true,
options: []},
],}
var toolbar_new_ticket = {
view: 'toolbar', elements: [
{view: 'button', id: 'cmd_generar_ticket', label: 'Generar',
icon: 'ticket', type: 'iconButton', autowidth: true,
align: 'center', hotkey: 'Ctrl+g'}, {},
{view: 'button', id: 'cmd_ticket_notes', label: 'Notas',
autowidth: true, type: 'iconButton', icon: 'commenting-o'}, {},
{view: 'button', id: 'cmd_cerrar_ticket', label: 'Cerrar',
type: 'danger', autowidth: true, align: 'center'}
]}
var grid_ticket_total_up = {
view: 'datatable',
id: 'grid_ticket_total_up',
adjust: true,
autoheight: true,
width: 250,
header: false,
footer: true,
hidden: true,
rowHeight: 0,
columns: [
{id: 'id', hidden: true},
{id: 'title', width: 100,
footer: {text: 'Total ', css: 'right_footer2'}},
{id: 'total', fillspace: true, format: webix.i18n.priceFormat,
footer: {content: 'summColumn', css: 'right_footer2'}},
],
data: [{id:0, title: '', total: 0.00}]
}
var cells_new_ticket = [
{minHeight: 10, maxHeight: 10},
toolbar_new_ticket,
{minHeight: 10, maxHeight: 10},
{cols: [{rows: [
{view: 'fieldset', label: 'Buscar Producto', body: tbody_buscar_producto},
]},
{maxWidth: 10},
{maxWidth: 300, rows: [
{view: 'fieldset', label: 'Información', body: body_ticket_informacion},
]},
]},
{cols: [
{view: 'label', label: 'Detalle', height: 30, align: 'left'},
{},
grid_ticket_total_up,
]},
grid_tdetails,
]
var toolbar_ticket_invoice = {view: 'toolbar', elements: [{},
{view: 'checkbox', id: 'chk_is_invoice_day', labelWidth: 0, width: 150,
labelRight: 'Es Factura Global'},
{view: 'richselect', id: 'lst_global_periodicidad_2', labelWidth: 90, width: 225,
label: 'Periodicidad:', options: opt_global_periodicidad, value: '01', disabled: true},
{view: 'richselect', id: 'lst_global_months_2', labelWidth: 50, width: 250,
label: 'Mes:', options: opt_global_months, value: '01', disabled: true},
{},
{view: 'button', id: 'cmd_close_ticket_invoice', label: 'Cerrar',
type: 'danger', autowidth: true, align: 'center'}
]}
var ticket_suggest_partners = {
view: 'gridsuggest',
id: 'grid_ticket_clients_found',
name: 'grid_ticket_clients_found',
body: {
autoConfig: false,
header: false,
columns: [
{id: 'id', hidden: true},
{id: 'nombre', adjust: 'data'},
{id: 'rfc', adjust: 'data'},
{id: 'forma_pago', hidden: true},
{id: 'uso_cfdi', hidden: true},
],
dataFeed:function(text){
if (text.length > 2){
this.load('/values/client?name=' + text)
}else{
this.hide()
}
}
}
}
var ticket_search_client = {cols: [{rows: [
{view: 'fieldset', id: 'fs_ticket_search_client', label: 'Buscar Cliente', body: {rows: [
{cols: [
{view: 'search', id: 'tsearch_client_key', name: 'tsearch_client_key',
label: 'por Clave', labelPosition: 'top', maxWidth: 250,
placeholder:'Presiona ENTER para buscar'},
{view: 'search', id: 'tsearch_client_name',
name: 'tsearch_client_name', label: 'por Nombre o RFC',
labelPosition: 'top', suggest: ticket_suggest_partners,
placeholder: 'Captura al menos tres letras'},
]},
{cols: [
{view: 'label', id: 'lbl_tclient_title', autowidth:true,
name: "lbl_tclient_title", label: 'Seleccionado: ' },
{view: 'label', id: 'lbl_tclient', name: 'lbl_tclient',
label: 'Ninguno'},
]}
]}},
]},]}
var grid_tickets_active_cols = [
{id: 'index', header: '#', adjust: 'data', css: 'right'},
{id: "id", header:"ID", hidden:true},
{id: "serie", header: ["Serie", {content: "selectFilter"}], adjust: "data",
sort: "string"},
{id: 'folio', header: ['Folio', {content: 'numberFilter'}], adjust: 'header',
sort: 'int', css: 'right', footer: {content: 'countRows', css: 'right'}},
{id: "fecha", header: ["Fecha y Hora"], adjust: "data", sort: "string",
footer: 'Tickets'},
{id: 'total', header: 'Total', width: 150,sort: 'int', css: 'right',
format: webix.i18n.priceFormat, footer: {content: 'summColumn',
css: 'right'}},
]
var grid_tickets_active = {
view: 'datatable',
id: 'grid_tickets_active',
select: 'row',
multiselect: true,
adjust: true,
footer: true,
drag: true,
resizeColumn: true,
headermenu: true,
columns: grid_tickets_active_cols,
on:{
'data->onStoreUpdated':function(){
this.data.each(function(obj, i){
obj.index = i + 1
})
}
},
}
var grid_tickets_invoice_cols = [
{id: 'index', header: '#', adjust: 'data', css: 'right'},
{id: "id", header:"ID", hidden:true},
{id: "serie", header: ["Serie", {content: "selectFilter"}], adjust: "data",
sort: "string"},
{id: 'folio', header: ['Folio', {content: 'numberFilter'}], adjust: 'header',
sort: 'int', css: 'right', footer: {content: 'countRows', css: 'right'}},
{id: "fecha", header: ["Fecha y Hora"], adjust: "data", sort: "string",
footer: 'Tickets'},
{id: 'total', header: 'Total', width: 150,sort: 'int', css: 'right',
format: webix.i18n.priceFormat, footer: {content: 'summColumn',
css: 'right'}},
]
var grid_tickets_invoice = {
view: 'datatable',
id: 'grid_tickets_invoice',
select: 'row',
multiselect: true,
adjust: true,
footer: true,
drag: true,
resizeColumn: true,
headermenu: true,
columns: grid_tickets_invoice_cols,
on:{
'data->onStoreUpdated':function(){
this.data.each(function(obj, i){
obj.index = i + 1
})
}
},
}
var controls_ticket_to_invoice = [
{minHeight: 10, maxHeight: 10},
toolbar_ticket_invoice,
{minHeight: 10, maxHeight: 10},
ticket_search_client,
{minHeight: 5, maxHeight: 5},
{cols:[
{rows: [{view: 'label', label: 'Tickets sin facturar', height: 30,
align: 'left'},
grid_tickets_active]},
{rows:[{},
{view: 'button', id: 'cmd_move_tickets_right', label: '->', autowidth: true},
{view: 'button', id: 'cmd_move_tickets_left', label: '<-', autowidth: true},
{}]},
{rows: [{view: 'label', label: 'Tickets a facturar', height: 30,
align: 'left'},
grid_tickets_invoice]},
]},
{minHeight: 20, maxHeight: 20},
{margin: 20, cols: [{},
{view: 'button', id: 'cmd_new_invoice_from_ticket', label: 'Facturar',
icon: 'ticket', type: 'iconButton', autowidth: true, align: 'center'},
{}]
},
]
var controls_new_ticket = [
{
view: 'tabview',
id: 'tv_new_ticket',
animate: true,
cells: [
{id: 'Generar', rows: cells_new_ticket},
]
},
]
var controls_ticket_invoice = [
{
view: 'tabview',
id: 'tv_ticket_invoice',
animate: true,
cells: [
{id: 'Facturar Tickets', rows: controls_ticket_to_invoice},
]
},
]
var form_new_ticket = {
type: 'space',
responsive: true,
cols: [{
view: 'form',
id: 'form_new_ticket',
complexData: true,
scroll: true,
elements: controls_new_ticket,
}],
}
var form_ticket_invoice = {
type: 'space',
responsive: true,
cols: [{
view: 'form',
id: 'form_ticket_invoice',
complexData: true,
scroll: true,
elements: controls_ticket_invoice,
}],
}
var multi_tickets = {
id: 'multi_tickets',
view: 'multiview',
animate: true,
cells:[
{id: 'tickets_home', rows: rows_tickets_home},
{id: 'tickets_new', rows:[form_new_ticket]},
{id: 'tickets_invoice', rows:[form_ticket_invoice]}
],
}
var app_tickets = {
id: 'app_tickets',
rows:[
{view: 'template', id: 'th_ticckets', type: 'header',
template: 'Punto de venta - Tickets'},
multi_tickets
],
}
var body_ticket_notes = {rows: [
{minHeight: 5, maxHeight: 5},
{view: 'textarea', id: 'ticket_notes', name: 'ticket_notes', height: 300,
placeholder: 'Captura las notas'},
{minHeight: 5, maxHeight: 5},
{cols: [{}, {view: 'button', id: 'cmd_ticket_save_note', autowidth: true,
label: 'Guardar y Cerrar', type: 'iconButton', hotkey: 'Ctrl+enter'},
{}]},
{minHeight: 5, maxHeight: 5},
]}
var win_ticket_notes = {
init: function(){
webix.ui({
view: 'window',
id: 'win_ticket_notes',
height: 400,
width: 500,
modal: true,
position: 'center',
head: 'Notas',
body: body_ticket_notes,
})
$$('cmd_ticket_save_note').attachEvent('onItemClick', cmd_ticket_save_note_click)
}
}

11573
source/static/js/webix.d.ts vendored Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@ -1,14 +1,8 @@
<%inherit file="base.html"/> <%inherit file="base.html"/>
<%block name="media"> <%block name="media">
<link rel="stylesheet" href="/static/css/sidebar_air.css" type="text/css">
<link rel="stylesheet" href="/static/css/app.css" type="text/css">
<script src="/static/js/sidebar.js" type="text/javascript" ></script>
<script src="/static/js/controller/util.js" type="text/javascript" ></script>
<script src="/static/js/ui/admin.js" type="text/javascript" ></script> <script src="/static/js/ui/admin.js" type="text/javascript" ></script>
<script src="/static/js/controller/admin.js" type="text/javascript" ></script> <script src="/static/js/controller/admin.js" type="text/javascript" ></script>
</%block> </%block>
<%block name="content"> <%block name="content">

View File

@ -6,10 +6,13 @@
<meta charset="utf-8" /> <meta charset="utf-8" />
<link rel="shortcut icon" href="/static/img/favicon.png"> <link rel="shortcut icon" href="/static/img/favicon.png">
<link rel="stylesheet" href="/static/css/air.css" type="text/css"> <link rel="stylesheet" href="/static/css/air.css" type="text/css">
<link rel="stylesheet" href="/static/css/sidebar_air.css" type="text/css">
<link rel="stylesheet" href="/static/css/app.css" type="text/css"> <link rel="stylesheet" href="/static/css/app.css" type="text/css">
<script src="/static/js/webix_debug.js" type="text/javascript" ></script> <script src="/static/js/webix.js" type="text/javascript" ></script>
<script src="/static/js/es-MX.js" type="text/javascript" ></script> <script src="/static/js/es-MX.js" type="text/javascript" ></script>
<script src="/static/js/lokijs.min.js" type="text/javascript" ></script> <script src="/static/js/lokijs.min.js" type="text/javascript" ></script>
<script src="/static/js/controller/util.js" type="text/javascript" ></script>
<%block name="media"/> <%block name="media"/>
</head> </head>
@ -17,7 +20,7 @@
<body> <body>
<script type="text/javascript" charset="utf-8"> <script type="text/javascript" charset="utf-8">
webix.debug = true; webix.debug = false;
webix.i18n.setLocale("es-MX"); webix.i18n.setLocale("es-MX");
</script> </script>

View File

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<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">
<Folios>
<UUID>{uuid}</UUID>
</Folios>
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
<SignedInfo>
<CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315" />
<SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1" />
<Reference URI="">
<Transforms>
<Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" />
</Transforms>
<DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
<DigestValue />
</Reference>
</SignedInfo>
<SignatureValue />
<KeyInfo>
<X509Data>
<X509SubjectName />
<X509IssuerSerial />
<X509Certificate />
</X509Data>
<KeyValue />
</KeyInfo>
</Signature>
</Cancelacion>

View File

@ -0,0 +1,142 @@
<%inherit file="base.html"/>
<%block name="media">
<script src="/static/js/controller/util.js" type="text/javascript" ></script>
<script src="/static/js/ui/empresas.js" type="text/javascript" ></script>
</%block>
<%block name="content">
<div id="form_empresas"></div>
<script type="text/javascript" charset="utf-8">
function agregar_empresa(values){
webix.ajax().post("/empresas", values, function(text, data, xhr) {
var values = data.json();
if (values.ok) {
$$('form_empresas').clear()
$$('grid_empresas').add(values.row)
$$('grid_empresas').refresh()
msg_ok(values.msg)
} else {
msg_error(values.msg)
}
})
}
function validate_nuevo_rfc(){
var msg = ''
var form = this.getFormView();
if (!form.validate()) {
msg = 'Valores inválidos'
msg_error(msg)
return
}
var values = form.getValues()
if(!validar_rfc(values['alta_rfc'])){
return
}
values['opt'] = 1
msg = '¿Estás seguro de agregar este nuevo emisor?'
webix.confirm({
title: 'Agregar Emisor',
ok: 'Si',
cancel: 'No',
type: 'confirm-error',
text: msg,
callback:function(result){
if(result){
msg = 'Agregando empresa, espera la confirmación...'
msg_ok(msg)
agregar_empresa(values)
}
}
})
}
function borrar_empresa(id){
var row = $$('grid_empresas').getItem(id)
webix.ajax().del('/empresas', {rfc: row.rfc}, function(text, xml, xhr){
msg = 'Emisor eliminado correctamente'
if(xhr.status == 200){
$$('grid_empresas').remove(id)
msg_ok(msg)
}else{
msg = 'No se pudo eliminar'
msg_error(msg)
}
})
}
function grid_empresas_click(id, e, node){
if(id.column != 'delete'){
return
}
msg = '¿Estás seguro de borrar el RFC seleccionado?<BR><BR>ESTA ACCIÓN NO SE PUEDE DESHACER'
webix.confirm({
title: 'Borrar Emisor',
ok: 'Si',
cancel: 'No',
type: 'confirm-error',
text: msg,
callback:function(result){
if(result){
borrar_empresa(id.row)
}
}
})
}
function respaldar_bd(){
webix.ajax().post("/empresas", {'opt': 2}, function(text, data, xhr) {
var values = data.json();
if (values.ok) {
msg_ok(values.msg)
} else {
msg_error(values.msg)
}
})
}
function cmd_respaldar_bd(){
msg = '¿Estás seguro de respaldar las Bases de Datos?'
webix.confirm({
title: 'Respaldar BD',
ok: 'Si',
cancel: 'No',
type: 'confirm-error',
text: msg,
callback:function(result){
if(result){
msg = 'Respaldando Bases de datos...'
msg_ok(msg)
respaldar_bd()
}
}
})
}
webix.ready(function(){
webix.ui(ui_empresas)
$$('grid_empresas').attachEvent('onItemClick', grid_empresas_click)
})
</script>
</%block>

View File

@ -36,6 +36,11 @@ function validate_login(){
webix.ready(function(){ webix.ready(function(){
webix.ui(ui_login); webix.ui(ui_login);
webix.ajax().get("/values/titlelogin", function(text, data, xhr){
var value = data.json();
$$("title_login").setHTML(value);
})
$$("txt_rfc").focus();
}); });
</script> </script>

View File

@ -1,17 +1,21 @@
<%inherit file="base.html"/> <%inherit file="base.html"/>
<%block name="media"> <%block name="media">
<link rel="stylesheet" href="/static/css/sidebar_air.css" type="text/css">
<script src="/static/js/sidebar.js" type="text/javascript" ></script>
<script src="/static/js/controller/util.js" type="text/javascript" ></script>
<script src="/static/js/ui/partners.js" type="text/javascript" ></script> <script src="/static/js/ui/partners.js" type="text/javascript" ></script>
<script src="/static/js/ui/products.js" type="text/javascript" ></script> <script src="/static/js/ui/products.js" type="text/javascript" ></script>
<script src="/static/js/ui/bancos.js" type="text/javascript" ></script>
<script src="/static/js/ui/school.js" type="text/javascript" ></script>
<script src="/static/js/ui/nomina.js" type="text/javascript" ></script>
<script src="/static/js/ui/tickets.js" type="text/javascript" ></script>
<script src="/static/js/ui/invoices.js" type="text/javascript" ></script> <script src="/static/js/ui/invoices.js" type="text/javascript" ></script>
<script src="/static/js/ui/main.js" type="text/javascript" ></script> <script src="/static/js/ui/main.js" type="text/javascript" ></script>
<script src="/static/js/controller/partners.js" type="text/javascript" ></script> <script src="/static/js/controller/partners.js" type="text/javascript" ></script>
<script src="/static/js/controller/products.js" type="text/javascript" ></script> <script src="/static/js/controller/products.js" type="text/javascript" ></script>
<script src="/static/js/controller/bancos.js" type="text/javascript" ></script>
<script src="/static/js/controller/school.js" type="text/javascript" ></script>
<script src="/static/js/controller/nomina.js" type="text/javascript" ></script>
<script src="/static/js/controller/tickets.js" type="text/javascript" ></script>
<script src="/static/js/controller/invoices.js" type="text/javascript" ></script> <script src="/static/js/controller/invoices.js" type="text/javascript" ></script>
<script src="/static/js/controller/main.js" type="text/javascript" ></script> <script src="/static/js/controller/main.js" type="text/javascript" ></script>

Some files were not shown because too many files have changed in this diff Show More