#!usr/bin/python
# -*- coding: utf8 -*-
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by the
# Free Software Foundation; either version 3, 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 MERCHANTIBILITY
# or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
# for more details.

"Almacenamiento de duplicados electrónicos RG1361/02 y RG1579/03 AFIP"
from __future__ import division
from __future__ import print_function
from __future__ import absolute_import

from builtins import input
from builtins import str
from builtins import zip
from past.builtins import basestring
from builtins import object
from past.utils import old_div

__author__ = "Mariano Reingart (reingart@gmail.com)"
__copyright__ = "Copyright (C) 2009-2021 Mariano Reingart"
__license__ = "LGPL-3.0-or-later"
__version__ = "3.22d"

LICENCIA = """
sired.py: Generador de archivos ventas para SIRED/SIAP RG1361/02 RG1579/03
(Sistema Resúmen Electrónico de Datos / Almacenamiento de Duplicados)
Copyright (C) 2009-2015 Mariano Reingart reingart@gmail.com

Este progarma es software libre, se entrega ABSOLUTAMENTE SIN GARANTIA
y es bienvenido a redistribuirlo bajo la licencia GPLv3.

Para información adicional sobre garantía, soporte técnico comercial
e incorporación/distribución en programas propietarios ver PyAfipWs:
http://www.sistemasagiles.com.ar/trac/wiki/PyAfipWs
"""

import csv
import datetime
from decimal import Decimal
import os
import sys
import unicodedata
import sqlite3
import traceback

from pyafipws.utils import leer, escribir, C, N, A, I, B, get_install_dir

CUIT = "20267565393"

# ESPECIFICACIONES TECNICAS - ANEXO II RESOLUCION GENERAL N°1361
# http://www.afip.gov.ar/afip/resol136102_Anexo_II.html

categorias = {
    "responsable inscripto": "01",  # IVA Responsable Inscripto
    "responsable no inscripto": "02",  # IVA Responsable no Inscripto
    "no responsable": "03",  # IVA no Responsable
    "exento": "04",  # IVA Sujeto Exento
    "consumidor final": "05",  # Consumidor Final
    "monotributo": "06",  # Responsable Monotributo
    "responsable monotributo": "06",  # Responsable Monotributo
    "no categorizado": "07",  # Sujeto no Categorizado
    "importador": "08",  # Importador del Exterior
    "exterior": "09",  # Cliente del Exterior
    "liberado": "10",  # IVA Liberado Ley Nº 19.640
    "responsable inscripto - agente de percepción": "11",  # IVA Responsable Inscripto - Agente de Percepcion
}

codigos_operacion = {
    "Z": "Exportaciones a la zona franca",
    "X": "Exportaciones al Exterior",
    "E": "Operaciones Exentas",
}

CAB_FAC_TIPO1 = [
    ("tipo_reg", 1, N),
    ("fecha_cbte", 8, N),
    ("tipo_cbte", 2, N),
    ("ctl_fiscal", 1, C),
    ("punto_vta", 4, N),
    ("cbt_numero", 8, N),
    ("cbte_nro_reg", 8, N),
    ("cant_hojas", 3, N),
    ("tipo_doc", 2, N),
    ("nro_doc", 11, N),
    ("nombre", 30, A),
    ("imp_total", 15, I),
    ("imp_tot_conc", 15, I),
    ("imp_neto", 15, I),
    ("impto_liq", 15, I),
    ("impto_liq_rni", 15, I),
    ("imp_op_ex", 15, I),
    ("impto_perc", 15, I),
    ("imp_iibb", 15, I),
    ("impto_perc_mun", 15, I),
    ("imp_internos", 15, I),
    ("transporte", 15, I),
    ("categoria", 2, N),
    ("imp_moneda_id", 3, A),
    ("imp_moneda_ctz", 10, I),
    ("alicuotas_iva", 1, N),
    ("codigo_operacion", 1, C),
    ("cae", 14, N),
    ("fecha_vto", 8, N),
    ("fecha_anulacion", 8, A),
]

# campos especiales del encabezado:
IMPORTES = (
    "imp_total",
    "imp_tot_conc",
    "imp_neto",
    "impto_liq",
    "impto_liq_rni",
    "imp_op_ex",
    "impto_perc",
    "imp_iibb",
    "impto_perc_mun",
    "imp_internos",
)

# total
CAB_FAC_TIPO2 = [
    ("tipo_reg", 1, N),
    ("periodo", 6, N),
    ("relleno", 13, B),
    ("cant_reg_tipo_1", 8, N),
    ("relleno", 17, B),
    ("cuit", 11, N),
    ("relleno", 22, B),
    ("imp_total", 15, I),
    ("imp_tot_conc", 15, I),
    ("imp_neto", 15, I),
    ("impto_liq", 15, I),
    ("impto_liq_rni", 15, I),
    ("imp_op_ex", 15, I),
    ("impto_perc", 15, I),
    ("imp_iibb", 15, I),
    ("impto_perc_mun", 15, I),
    ("imp_internos", 15, I),
    ("relleno", 62, B),
]

DETALLE = [
    ("tipo_cbte", 2, N),
    ("ctl_fiscal", 1, C),
    ("fecha_cbte", 8, N),
    ("punto_vta", 4, N),
    ("cbt_numero", 8, N),
    ("cbte_nro_reg", 8, N),
    ("qty", 12, I),
    ("pro_umed", 2, N),
    ("pro_precio_uni", 16, I),
    ("imp_bonif", 15, I),
    ("imp_ajuste", 16, I),
    ("imp_total", 16, I),
    ("alicuota_iva", 4, I),
    ("gravado", 1, C),
    ("anulacion", 1, C),
    ("codigo", 50, A),
    ("ds", 150, A),
]

VENTAS_TIPO1 = [
    ("tipo_reg", 1, N),
    ("fecha_cbte", 8, N),
    ("tipo_cbte", 2, N),
    ("ctl_fiscal", 1, C),
    ("punto_vta", 4, N),
    ("cbt_numero", 20, N),
    ("cbte_nro_reg", 20, N),
    ("tipo_doc", 2, N),
    ("nro_doc", 11, N),
    ("nombre", 30, A),
    ("imp_total", 15, I),
    ("imp_tot_conc", 15, I),
    ("imp_neto", 15, I),
    ("alicuota_iva", 4, I),
    ("impto_liq", 15, I),
    ("impto_liq_rni", 15, I),
    ("imp_op_ex", 15, I),
    ("impto_perc", 15, I),
    ("imp_iibb", 15, I),
    ("impto_perc_mun", 15, I),
    ("imp_internos", 15, I),
    ("categoria", 2, N),
    ("imp_moneda_id", 3, A),
    ("imp_moneda_ctz", 10, I),
    ("alicuotas_iva", 1, N),
    ("codigo_operacion", 1, C),
    ("cae", 14, N),
    ("fecha_vto", 8, N),
    ("fecha_anulacion", 8, A),
    ("info_adic", 75 - 0, B),
]

VENTAS_TIPO2 = [
    ("tipo_reg", 1, N),
    ("periodo", 6, N),
    ("relleno", 29, B),
    ("cant_reg_tipo_1", 12, N),
    ("relleno", 10, B),
    ("cuit", 11, N),
    ("relleno", 30, B),
    ("imp_total", 15, I),
    ("imp_tot_conc", 15, I),
    ("imp_neto", 15, I),
    ("Relleno", 4, B),
    ("impto_liq", 15, I),
    ("impto_liq_rni", 15, I),
    ("imp_op_ex", 15, I),
    ("impto_perc", 15, I),
    ("imp_iibb", 15, I),
    ("impto_perc_mun", 15, I),
    ("imp_internos", 15, I),
    ("relleno", 122, B),
]

# Regimen de informacion de compras y ventas

from pyafipws.rg3685 import REGINFO_CV_VENTAS_CBTE, REGINFO_CV_VENTAS_CBTE_ALICUOTA


def format_as_dict(format):
    return dict([(k[0], None) for k in format])


def leer_planilla(entrada, sep=","):
    "Convierte una planilla CSV a una lista de diccionarios [{'col': celda}]"

    items = []
    csv_reader = csv.reader(open(entrada), dialect="excel", delimiter=sep)
    for row in csv_reader:
        items.append(row)
    if len(items) < 2:
        raise RuntimeError("El archivo no tiene filas validos")
    if len(items[0]) < 2:
        raise RuntimeError("El archivo no tiene columnas (usar %s de separador)" % sep)
    cols = [str(it).strip() for it in items[0]]

    # armar diccionario por cada linea
    items = [
        dict([(cols[i], str(v).strip()) for i, v in enumerate(item)])
        for item in items[1:]
    ]

    return items


def leer_json(entrada="sired.json"):
    "Carga los datos en formato JSON [{'col': celda}]"

    import json

    items = json.load(open(entrada))

    return items


def grabar_json(salida="sired.json"):
    "Guarda los datos en formato JSON"

    import json

    json.dump(items, open(salida, "w"), sort_keys=True, indent=4)


def generar_encabezado(items):
    "Crear archivo de cabecera de facturas emitidas"

    periodo = items[0]["fecha_cbte"][:6]

    out = open("CABECERA_%s.txt" % periodo, "w")
    totales = format_as_dict(CAB_FAC_TIPO2)
    totales["periodo"] = periodo
    for key in IMPORTES:
        totales[key] = Decimal(0)

    for item in items:
        vals = format_as_dict(CAB_FAC_TIPO1)
        vals["fecha_anulacion"] = ""
        for k in list(item.keys()):
            vals[k] = item[k]
            if k in totales and k in IMPORTES:
                totales[k] = totales[k] + Decimal(item[k])
        vals["tipo_reg"] = "1"
        vals["ctl_fiscal"] = item.get("ctl_fiscal", " ")  # C para controlador
        vals["cbte_nro_reg"] = vals["cbt_numero"]
        vals["cant_hojas"] = "01"
        vals["transporte"] = "0"
        vals["categoria"] = categorias[item["categoria"].lower()]
        if vals["imp_moneda_id"] is None:
            vals["imp_moneda_id"] = "PES"
            vals["imp_moneda_ctz"] = "1.000"
        vals["alicuotas_iva"] = max(len(item.get("ivas", [])), 1)
        if vals["codigo_operacion"] is None:
            if int(item["tipo_cbte"]) in (19, 20, 21):
                vals["codigo_operacion"] = "E"
            else:
                vals["codigo_operacion"] = " "
        if vals["imp_tot_conc"] is None:
            vals["imp_tot_conc"] = "0"
        s = escribir(vals, CAB_FAC_TIPO1)
        out.write(s)

    totales["tipo_reg"] = "2"
    totales["cant_reg_tipo_1"] = str(len(items))
    totales["cuit"] = CUIT
    s = escribir(totales, CAB_FAC_TIPO2)
    out.write(s)
    out.close()


def generar_detalle(items):
    "Crear archivo de detalle de facturas emitidas"

    periodo = items[0]["fecha_cbte"][:6]

    out = open("DETALLE_%s.txt" % periodo, "w")

    # recorro las facturas y detalles de artículos vendidos:
    for item in items:
        for it in item.get("detalles", [{}]):
            vals = format_as_dict(DETALLE)
            # datos generales de la factura:
            vals["tipo_reg"] = "1"
            for k in ("tipo_cbte", "fecha_cbte", "punto_vta", "cbt_numero"):
                vals[k] = item[k]
            vals["cbte_nro_reg"] = item["cbt_numero"]  # no hay varias hojas
            vals["ctl_fiscal"] = item.get("ctl_fiscal", " ")  # C para controlador
            vals["anulacion"] = item.get("anulacion", " ")
            # datos del artículo:
            vals["qty"] = it.get("qty", "1")  # cantidad
            vals["pro_umed"] = it.get("umed", "07")  # unidad de medida
            vals["pro_precio_uni"] = it.get("precio", item["imp_neto"])
            vals["imp_bonif"] = it.get("bonif", "0.00")
            vals["imp_ajuste"] = it.get("ajuste", "0.00")
            vals["imp_total"] = it.get("importe", "0.00")
            # iva
            if "iva_id" in it and it["iva_id"]:
                # mapear alicuota de iva según código usado en MTX
                iva_id = int(it["iva_id"])
                if iva_id in (1, 2):
                    alicuota = None
                else:
                    alicuota = {3: "0.00", 4: "10.5", 5: "21", 6: "27"}[iva_id]
                if alicuota is None:
                    vals["gravado"] = "E"
                else:
                    vals["gravado"] = "G"
                vals["alicuota_iva"] = alicuota or "0.00"
            else:
                # tomar datos generales:
                vals["alicuota_iva"] = (
                    old_div(Decimal(item["imp_total"]), Decimal(item["imp_neto"])) - 1
                ) * 100
                if float(item.get("impto_liq", item.get("imp_iva", 0))) == 0:
                    vals["gravado"] = "E"
                else:
                    vals["gravado"] = "G"
            # diseño libre: código de barras y descripción:
            vals["codigo"] = it.get("codigo", "")
            vals["ds"] = it.get("ds", "")
            s = escribir(vals, DETALLE)
            out.write(s)

    out.close()


def generar_ventas(items):
    "Crear archivos de ventas (registros tipo 1 y tipo 2 totales)"

    periodo = items[0]["fecha_cbte"][:6]

    out = open("VENTAS_%s.txt" % periodo, "w")
    totales = format_as_dict(VENTAS_TIPO2)
    totales["periodo"] = periodo
    for key in IMPORTES:
        totales[key] = Decimal(0)

    # recorro las facturas e itero sobre los subtotales por alicuota de IVA:
    for item in items:
        ivas = item.get("ivas", [{}])
        for i, iva in enumerate(ivas):
            vals = format_as_dict(VENTAS_TIPO1)
            # datos generales de la factura:
            vals["tipo_reg"] = "1"
            # copio los campos que no varían para las distintas alicuotas de IVA
            for k, l, t in VENTAS_TIPO1[1:10] + VENTAS_TIPO1[21:30]:
                vals[k] = item.get(k)
            vals["fecha_anulacion"] = ""
            vals["ctl_fiscal"] = item.get("ctl_fiscal", " ")  # C para controlador
            vals["anulacion"] = item.get("anulacion", " ")
            vals["cbte_nro_reg"] = item["cbt_numero"]
            vals["cant_hojas"] = "01"
            vals["transporte"] = "0"
            vals["categoria"] = categorias[item["categoria"].lower()]
            if vals["imp_moneda_id"] is None:
                vals["imp_moneda_id"] = "PES"
                vals["imp_moneda_ctz"] = "1.000"
            # subtotales por alícuota de IVA
            if "iva_id" in iva:
                # mapear alicuota de iva según código usado en MTX
                iva_id = int(iva["iva_id"])
                if iva_id == 1:
                    vals["imp_tot_conc"] = iva["base_imp"]
                    alicuota = None
                elif iva_id == 2:
                    vals["imp_op_ex"] = iva["base_imp"]
                    alicuota = None
                else:
                    alicuota = {3: "0.00", 4: "10.5", 5: "21", 6: "27"}[iva_id]
                    vals["imp_neto"] = iva["base_imp"]
                    vals["impto_liq"] = iva["importe"]
                vals["alicuota_iva"] = alicuota or "0.00"
            else:
                # tomar datos generales:
                vals["alicuota_iva"] = (
                    old_div(Decimal(item["imp_total"]), Decimal(item["imp_neto"])) - 1
                ) * 100
                vals["alicuotas_iva"] = "01"
                if float(item.get("impto_liq", item.get("imp_iva", 0))) == 0:
                    vals["codigo_operacion"] = "E"
                else:
                    vals["codigo_operacion"] = " "
                if vals["imp_tot_conc"] is None:
                    vals["imp_tot_conc"] = "0"

            # acumulo los totales para el registro tipo 2
            for k in IMPORTES:
                totales[k] = totales[k] + Decimal(vals[k] or 0)

            # otros impuestos (TODO: recorrer tributos) solo ultimo registro:
            if len(ivas) == i - 1:
                for k in ("impto_perc", "imp_iibb", "impto_perc_mun", "imp_internos"):
                    if k in item:
                        vals[k] = item[k]
                        totales[k] = totales[k] + Decimal(vals[k] or 0)

            s = escribir(vals, VENTAS_TIPO1)
            out.write(s)

    totales["tipo_reg"] = "2"
    totales["cant_reg_tipo_1"] = str(len(items))
    totales["cuit"] = CUIT
    s = escribir(totales, VENTAS_TIPO2)
    out.write(s)
    out.close()


class SIRED(object):
    "Componente para Sistema Resúmen Electrónico de Datos RG1361/02 RG1579/03"

    _public_methods_ = [
        "CrearBD",
        "CrearFactura",
        "AgregarDetalleItem",
        "AgregarIva",
        "AgregarTributo",
        "AgregarCmpAsoc",
        "AgregarPermiso",
        "AgregarDato",
        "GuardarFactura",
        "ObtenerFactura",
    ]
    _public_attrs_ = [
        "InstallDir",
        "Traceback",
        "Excepcion",
        "Version",
    ]
    _readonly_attrs_ = _public_attrs_
    _reg_progid_ = "SIRED"
    _reg_clsid_ = "{3DC74AD5-939F-42AB-8381-FCA7AF783C77}"

    def __init__(self):
        self.db_path = os.path.join(self.InstallDir, "sired.db")
        self.Version = __version__
        # Abrir la base de datos
        crear = not os.path.exists(self.db_path)
        self.db = sqlite3.connect(self.db_path)
        self.db.row_factory = sqlite3.Row
        self.cursor = self.db.cursor()
        if crear:
            from pyafipws.formatos.formato_txt import (
                ENCABEZADO,
                DETALLE,
                TRIBUTO,
                IVA,
                CMP_ASOC,
                PERMISO,
                DATO,
            )
            from pyafipws.formatos.formato_sql import esquema_sql

            tipos_registro = [
                ("encabezado", ENCABEZADO),
                ("detalle", DETALLE),
                ("tributo", TRIBUTO),
                ("iva", IVA),
                ("cmp_asoc", CMP_ASOC),
                ("permiso", PERMISO),
                ("dato", DATO),
            ]
            for sql in esquema_sql(tipos_registro):
                self.cursor.execute(sql)

    def CrearFactura(
        self,
        concepto=1,
        tipo_doc=80,
        nro_doc="",
        tipo_cbte=1,
        punto_vta=0,
        cbte_nro=0,
        imp_total=0.00,
        imp_tot_conc=0.00,
        imp_neto=0.00,
        imp_iva=0.00,
        imp_trib=0.00,
        imp_op_ex=0.00,
        fecha_cbte="",
        fecha_venc_pago="",
        fecha_serv_desde=None,
        fecha_serv_hasta=None,
        moneda_id="PES",
        moneda_ctz="1.0000",
        cae="",
        fch_venc_cae="",
        id_impositivo="",
        nombre_cliente="",
        domicilio_cliente="",
        pais_dst_cmp=None,
        obs_comerciales="",
        obs_generales="",
        forma_pago="",
        incoterms="",
        idioma_cbte=7,
        motivos_obs="",
        descuento=0.0,
        email="",
        **kwargs
    ):
        "Creo un objeto factura (internamente)"
        fact = {
            "tipo_doc": tipo_doc,
            "nro_doc": nro_doc,
            "tipo_cbte": tipo_cbte,
            "punto_vta": punto_vta,
            "cbte_nro": cbte_nro,
            "imp_total": imp_total,
            "imp_tot_conc": imp_tot_conc,
            "imp_neto": imp_neto,
            "imp_iva": imp_iva,
            "imp_trib": imp_trib,
            "imp_op_ex": imp_op_ex,
            "fecha_cbte": fecha_cbte,
            "fecha_venc_pago": fecha_venc_pago,
            "moneda_id": moneda_id,
            "moneda_ctz": moneda_ctz,
            "concepto": concepto,
            "nombre_cliente": nombre_cliente,
            "domicilio_cliente": domicilio_cliente,
            "pais_dst_cmp": pais_dst_cmp,
            "obs_comerciales": obs_comerciales,
            "obs_generales": obs_generales,
            "id_impositivo": id_impositivo,
            "forma_pago": forma_pago,
            "incoterms": incoterms,
            "cae": cae,
            "fecha_vto": fch_venc_cae,
            "motivos_obs": motivos_obs,
            "descuento": descuento,
            "email": email,
            "cbtes_asoc": [],
            "tributos": [],
            "ivas": [],
            "permisos": [],
            "detalles": [],
            "datos": [],
        }
        if fecha_serv_desde:
            fact["fecha_serv_desde"] = fecha_serv_desde
        if fecha_serv_hasta:
            fact["fecha_serv_hasta"] = fecha_serv_hasta
        self.factura = fact
        return True

    def EstablecerParametro(self, parametro, valor):
        "Modifico un parametro general a la factura (internamente)"
        self.factura[parametro] = valor
        return True

    def AgregarDato(self, campo, valor, pagina="T"):
        "Agrego un dato a la factura (internamente)"
        self.factura["datos"].append({"campo": campo, "valor": valor, "pagina": pagina})
        return True

    def AgregarDetalleItem(
        self,
        u_mtx,
        cod_mtx,
        codigo,
        ds,
        qty,
        umed,
        precio,
        bonif,
        iva_id,
        imp_iva,
        importe,
        despacho,
        dato_a=None,
        dato_b=None,
        dato_c=None,
        dato_d=None,
        dato_e=None,
    ):
        "Agrego un item a una factura (internamente)"
        ##ds = unicode(ds, "latin1") # convierto a latin1
        # Nota: no se calcula neto, iva, etc (deben venir calculados!)
        item = {
            "u_mtx": u_mtx,
            "cod_mtx": cod_mtx,
            "codigo": codigo,
            "ds": ds,
            "qty": qty,
            "umed": umed,
            "precio": precio,
            "bonif": bonif,
            "iva_id": iva_id,
            "imp_iva": imp_iva,
            "importe": importe,
            "despacho": despacho,
            "dato_a": dato_a,
            "dato_b": dato_b,
            "dato_c": dato_c,
            "dato_d": dato_d,
            "dato_e": dato_e,
        }
        self.factura["detalles"].append(item)
        return True

    def AgregarCmpAsoc(self, tipo=1, pto_vta=0, nro=0, **kwarg):
        "Agrego un comprobante asociado a una factura (interna)"
        cmp_asoc = {"cbte_tipo": tipo, "cbte_punto_vta": pto_vta, "cbte_nro": nro}
        self.factura["cbtes_asoc"].append(cmp_asoc)
        return True

    def AgregarTributo(
        self, tributo_id=0, desc="", base_imp=0.00, alic=0, importe=0.00, **kwarg
    ):
        "Agrego un tributo a una factura (interna)"
        tributo = {
            "tributo_id": tributo_id,
            "desc": desc,
            "base_imp": base_imp,
            "alic": alic,
            "importe": importe,
        }
        self.factura["tributos"].append(tributo)
        return True

    def AgregarIva(self, iva_id=0, base_imp=0.0, importe=0.0, **kwarg):
        "Agrego un tributo a una factura (interna)"
        iva = {"iva_id": iva_id, "base_imp": base_imp, "importe": importe}
        self.factura["ivas"].append(iva)
        return True

    def GuardarFactura(self):
        from pyafipws.formatos.formato_sql import escribir

        escribir([self.factura], self.db)
        return self.factura["id"]

    def ActualizarFactura(self, id_factura):
        from pyafipws.formatos.formato_sql import modificar

        self.factura["id"] = id_factura
        modificar(self.factura, self.db)
        return True

    def ObtenerFactura(self, id_factura=None):
        from pyafipws.formatos.formato_sql import leer, max_id

        if not id_factura:
            id_factura = max_id(self.db)
        facts = list(leer(self.db, ids=[id_factura]))
        if facts:
            self.factura = facts[0]
        return True

    def Consultar(self, **kwargs):
        from pyafipws.formatos.formato_sql import leer

        return leer(self.db, **kwargs)


# busco el directorio de instalación (global para que no cambie si usan otra dll)
INSTALL_DIR = SIRED.InstallDir = get_install_dir()

def main():
    
    try:
        if hasattr(sys, "frozen") or False:
            p = os.path.dirname(os.path.abspath(sys.executable))
            os.chdir(p)
        ##sys.stdout = open("salida.txt", "a")
        entrada = {}
        for i, k in enumerate(("encabezados", "detalles", "ivas", "tributos")):
            if len(sys.argv) > i + 1:
                filename = sys.argv[i + 1]
                if not filename.startswith("--") and os.path.exists(filename):
                    entrada[k] = filename
        if not entrada:
            entrada["encabezado"] = "facturas3.csv"

        if "--prueba" in sys.argv:
            sired = SIRED()

            # creo una factura de ejemplo
            tipo_cbte = 2
            punto_vta = 4000
            fecha = datetime.datetime.now().strftime("%Y%m%d")
            concepto = 3
            tipo_doc = 80
            nro_doc = "30000000007"
            cbte_nro = 12345678
            imp_total = "122.00"
            imp_tot_conc = "3.00"
            imp_neto = "100.00"
            imp_iva = "21.00"
            imp_trib = "1.00"
            imp_op_ex = "2.00"
            imp_subtotal = "100.00"
            fecha_cbte = fecha
            fecha_venc_pago = fecha
            # Fechas del período del servicio facturado (solo si concepto = 1?)
            fecha_serv_desde = fecha
            fecha_serv_hasta = fecha
            moneda_id = "PES"
            moneda_ctz = "1.000"
            obs_generales = "Observaciones Generales, texto libre"
            obs_comerciales = "Observaciones Comerciales, texto libre"

            nombre_cliente = "Joao Da Silva"
            domicilio_cliente = "Rua 76 km 34.5 Alagoas"
            pais_dst_cmp = 16
            id_impositivo = "PJ54482221-l"
            moneda_id = "012"
            moneda_ctz = 0.5
            forma_pago = "30 dias"
            incoterms = "FOB"
            idioma_cbte = 1
            motivo = "11"

            cae = None
            fch_venc_cae = None

            sired.CrearFactura(
                concepto,
                tipo_doc,
                nro_doc,
                tipo_cbte,
                punto_vta,
                cbte_nro,
                imp_total,
                imp_tot_conc,
                imp_neto,
                imp_iva,
                imp_trib,
                imp_op_ex,
                fecha_cbte,
                fecha_venc_pago,
                fecha_serv_desde,
                fecha_serv_hasta,
                moneda_id,
                moneda_ctz,
                cae,
                fch_venc_cae,
                id_impositivo,
                nombre_cliente,
                domicilio_cliente,
                pais_dst_cmp,
                obs_comerciales,
                obs_generales,
                forma_pago,
                incoterms,
                idioma_cbte,
                motivo,
            )

            tipo = 91
            pto_vta = 2
            nro = 1234
            sired.AgregarCmpAsoc(tipo, pto_vta, nro)
            tipo = 5
            pto_vta = 2
            nro = 1234
            sired.AgregarCmpAsoc(tipo, pto_vta, nro)

            tributo_id = 99
            desc = "Impuesto Municipal Matanza"
            base_imp = "100.00"
            alic = "1.00"
            importe = "1.00"
            sired.AgregarTributo(tributo_id, desc, base_imp, alic, importe)

            iva_id = 5  # 21%
            base_imp = 100
            importe = 21
            sired.AgregarIva(iva_id, base_imp, importe)

            u_mtx = 123456
            cod_mtx = 1234567890123
            codigo = "P0001"
            ds = "Descripcion del producto P0001\n" + "Lorem ipsum sit amet " * 10
            qty = 1.00
            umed = 7
            precio = 100.00
            bonif = 0.00
            iva_id = 5
            imp_iva = 21.00
            importe = 121.00
            despacho = u"Nº 123456"
            sired.AgregarDetalleItem(
                u_mtx,
                cod_mtx,
                codigo,
                ds,
                qty,
                umed,
                precio,
                bonif,
                iva_id,
                imp_iva,
                importe,
                despacho,
            )

            sired.AgregarDato("prueba", "1234")
            print("Prueba!")
            id_factura = sired.GuardarFactura()
            fact = sired.factura.copy()
            ok = sired.ObtenerFactura(id_factura)
            f = sired.factura

            # verificar que los datos se hayan grabado y leido correctamente:
            difs = []

            def cmp_dict(d1, d2, prefijo=None):
                global difs
                if difs is None:
                    difs = []
                for k in set(list(d1.keys()) + list(d2.keys())):
                    if k in d1 and k in d2:
                        if isinstance(d1[k], list):
                            for i, (v1, v2) in enumerate(zip(d1[k], d2[k])):
                                cmp_dict(v1, v2, (k, i))
                        else:
                            if isinstance(d1[k], Decimal) or isinstance(d2[k], Decimal):
                                d1[k] = float(d1[k])
                                d2[k] = float(d2[k])
                            if isinstance(d1[k], int) or isinstance(d2[k], int):
                                d1[k] = int(d1[k])
                                d2[k] = int(d2[k])
                            if d1[k] != d2[k]:
                                difs.append(("Dif", prefijo, k, d1[k], d2[k]))

            cmp_dict(fact, f)
            for dif in difs:
                print(dif)

            sired.EstablecerParametro("cae", "61123022925855")
            sired.EstablecerParametro("fch_venc_cae", "20110320")
            sired.EstablecerParametro("motivo_obs", "")
            ok = sired.ActualizarFactura(id_factura)
            ok = sired.ObtenerFactura(id_factura)
            assert sired.factura["cae"] == "61123022925855"

            sys.exit(0)

        if "--leer" in sys.argv:
            if "--completar_padron" in sys.argv:
                from pyafipws.padron import PadronAFIP

                padron = PadronAFIP()
                padron.Conectar(trace="--trace" in sys.argv)
                from pyafipws.formatos import formato_txt

                formato = formato_txt.ENCABEZADO
                categorias_iva = dict(
                    [(int(v), k) for k, v in list(categorias.items())]
                )

            else:
                formato = VENTAS_TIPO1

            claves = [
                clave
                for clave, pos, leng in formato
                if clave not in ("tipo", "info_adic")
            ]
            csv = csv.DictWriter(
                open("ventas.csv", "wb"), claves, extrasaction="ignore"
            )
            csv.writerow(dict([(k, k) for k in claves]))
            f = open("VENTAS.txt")
            for linea in f:
                if str(linea[0]) == "2":
                    datos = leer(linea, REGINFO_CV_VENTAS_CBTE)

                    if "--completar_padron" in sys.argv:
                        cuit = datos["nro_doc"]
                        print("Consultando AFIP online...", cuit, end=" ")
                        ok = padron.Consultar(cuit)
                        print(padron.direccion, padron.provincia)
                        datos["nombre_cliente"] = padron.denominacion.encode("latin1")
                        datos["domicilio_cliente"] = padron.direccion.encode("latin1")
                        datos["localidad_cliente"] = "%s (CP %s) " % (
                            padron.localidad.encode("latin1"),
                            padron.cod_postal.encode("latin1"),
                        )
                        datos["provincia_cliente"] = padron.provincia.encode("latin1")
                        datos["cbte_nro"] = datos["cbt_numero_desde"]
                        # datos['id_impositivo'] = categorias_iva[int(datos['categoria'])]
                    csv.writerow(datos)
            f.close()
        else:
            # cargar datos desde planillas CSV separadas o JSON:
            if entrada["encabezados"].lower().endswith("csv"):
                facturas = items = leer_planilla(entrada["encabezados"], ";")

                # pre-procesar:
                for factura in facturas:
                    for k, v in list(factura.items()):
                        # decodificar strings (evitar problemas unicode)
                        if isinstance(v, basestring):
                            if isinstance(v, str):
                                v = v.decode("latin1", "ignore")
                            factura[k] = unicodedata.normalize("NFKD", v).encode(
                                "ASCII", "ignore"
                            )
                            print(k, factura[k])
                        # convertir tipos de datos desde los strings del CSV
                        if k.startswith("imp"):
                            factura[k] = float(v)
                        if k in (
                            "cbt_desde",
                            "cbt_hasta",
                            "concepto",
                            "punto_vta",
                            "tipo_cbte",
                            "tipo_doc",
                            "nro_doc",
                            "cbt_numero",
                        ):
                            factura[k] = int(v)

                    alicuotas = {3: 0, 4: 10.5, 5: 21.0, 6: 27}
                    ivas = {}
                    imp_iva = 0.00

                    ruta = os.path.dirname(entrada["encabezados"])
                    prefijos = (
                        "%(tipo_cbte)02d%(cbt_numero)08d",
                        "%(tipo_cbte)02d%(cbt_numero)06d",
                        "%(tipo_cbte)02d%(punto_vta)04d%(cbt_numero)08d",
                    )
                    for prefijo in prefijos:
                        fn = os.path.join(ruta, "%s.csv" % (prefijo % factura))
                        print("Detalle: ", fn)
                        if os.path.exists(fn):
                            det = fn
                            print("encontrado!")
                            break
                    else:
                        if "detalles" in entrada:
                            det = entrada["detalles"]
                        else:
                            det = None

                    if det:
                        detalles = leer_planilla(det, ";")

                    for det in detalles:
                        iva_id = det.get("iva_id", 5)
                        if isinstance(det.get("ds"), str):
                            det["ds"] = det["ds"].decode("latin1", "ignore")
                        if "ds" in det:
                            det["ds"] = unicodedata.normalize("NFKD", det["ds"]).encode(
                                "ASCII", "ignore"
                            )
                        print(det)
                        if iva_id:
                            iva_id = int(iva_id)
                            if iva_id not in ivas:
                                ivas[iva_id] = {
                                    "base_imp": 0,
                                    "importe": 0,
                                    "iva_id": iva_id,
                                }

                            importe = det.get("importe", det.get("total"))
                            if importe:
                                iva = det.get("imp_iva", None)
                                importe = round(float(importe.replace(",", ".")), 2)
                                if not iva is None:
                                    iva = round(float(iva.replace(",", ".")), 2)
                                # si el iva es incorrecto o no está, liquidar:
                                if not iva and iva_id > 3:
                                    # extraer IVA incluido factura B:
                                    if factura["tipo_cbte"] in (6, 7, 8):
                                        neto = round(
                                            old_div(
                                                importe,
                                                ((100 + alicuotas[iva_id]) / 100.0),
                                            ),
                                            2,
                                        )
                                        iva = importe - neto
                                    else:
                                        neto = importe
                                        iva = round(neto * alicuotas[iva_id] / 100.0, 2)
                                    print("importe iva calc:", importe, iva)
                                else:
                                    neto = importe
                                    # descontar IVA incluido factura B:
                                    if factura["tipo_cbte"] in (6, 7, 8):
                                        neto = neto - iva
                                imp_iva += iva
                                ivas[iva_id]["importe"] += iva
                                ivas[iva_id]["base_imp"] += neto
                                det["imp_iva"] = iva

                    # rearmar estructuras internas:
                    factura["detalles"] = detalles
                    factura["ivas"] = list(ivas.values())
                    factura["datos"] = []
                    factura["tributos"] = []
                    if "imp_iva" not in factura or factura["imp_iva"] == "":
                        print("debe agregar el IVA total en el encabezado...")
                        factura["imp_iva"] = imp_iva
                    if "cbt_numero" in factura:
                        factura["cbt_desde"] = factura["cbt_numero"]
                        factura["cbt_hasta"] = factura["cbt_numero"]
                    if "nombre" in factura:
                        factura["nombre_cliente"] = factura["nombre"]
                        factura["domicilio_cliente"] = factura["domicilio"]
                    factura["cbte_nro"] = factura["cbt_desde"]
                    if not "concepto" in factura:
                        factura["concepto"] = 1

                    # limpio campos que no correspondan (productos vs servicios):
                    if factura["concepto"] == 1:
                        factura["fecha_venc_pago"] = None

            elif entrada["encabezados"].lower().endswith(".json"):
                items = leer_json(entrada["encabezados"])

            print("Generando encabezado...")
            generar_encabezado(items)
            print("Generando detalle...")
            generar_detalle(items)
            print("Generando ventas...")
            generar_ventas(items)
            if "--json" in sys.argv:
                grabar_json()

        print("Hecho.")
    except Exception as e:
        if "--debug" in sys.argv:
            raise
        print("Error: por favor corriga los datos y vuelva a intentar:")
        print(str(e))
        f = open("traceback.txt", "w+")
        import traceback

        traceback.print_exc(file=f)
        f.close()
    if "--debug" in sys.argv:
        input("presione enter para continuar...")
    ##sys.stdout.close()

if __name__ == "__main__":
    main()