Código fuente para impar.datecontext
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# "THE WISKEY-WARE LICENSE":
# <jbc.develop@gmail.com> and <nluczywo@gmail.com> wrote this file.
# As long as you retain this notice you can do whatever you want with this stuff.
# If we meet some day, and you think this stuff is worth it, you can buy me a
# WISKEY in return Juan BC and Nadia AL
#===============================================================================
# DOCS
#===============================================================================
"""Manejador de contexto de fechas para los impuestos"""
#===============================================================================
# IMPORTS
#===============================================================================
import os
import datetime
import importlib
import re
#===============================================================================
# CONSTANTS
#===============================================================================
PATH = os.path.abspath(os.path.dirname(__file__))
IMPS_PATH = os.path.join(PATH, "_impuestos")
DATE_FORMAT = "%Y%m%d"
TAXES = {}
TAXES_MIN_DATE = {}
for impuesto in os.listdir(IMPS_PATH):
imp_path = os.path.join(IMPS_PATH, impuesto)
if os.path.isdir(imp_path) and not impuesto.startswith("_"):
modnames = []
rx_imp = re.compile(
"^{}_[0-9]{{4}}[0-9]{{2}}[0-9]{{2}}[.]py$".format(impuesto),
re.UNICODE
)
for fname in os.listdir(imp_path):
if rx_imp.match(fname):
date = datetime.datetime.strptime(
fname.rsplit("_", 1)[-1][:-3], DATE_FORMAT
).date()
modname = "impar._impuestos.{}.{}".format(
impuesto, fname.rsplit(".", 1)[0]
)
modnames.append((date, modname))
modnames.sort(reverse=True)
if modnames:
TAXES[impuesto] = tuple(modnames)
TAXES_MIN_DATE[impuesto] = modnames[-1][0]
#===============================================================================
# ERRORS
#===============================================================================
[documentos]class NotSuitableTax(ImportError):
"""Si no existe un impuesto de fecha menor o igual a del contexto"""
pass
[documentos]class TaxNotExists(ImportError):
"""El impuesto no existe"""
pass
#===============================================================================
# CLASS
#===============================================================================
[documentos]class DateContext(object):
"""Define un contexto de calculo para impuestos. En la practica asume todas
las legislaciones a la fecha dada en el contexto para cada impuesto
Si usted quiere calcular el impuestoA a con las legislaciones vigentes
al dia 31 de octubre del 2012 deberia ejecutar el codigo de la siguiente
manera
>>> import datetime, impar
>>> ctx = impar.DateContext(datetime(2012, 10, 31))
>>> impuestoA = ctx.get("impuestoA")
>>> impuestoA.calcular(...)
:param date: Fecha del contexto (hasta que fehca maxima se toman en
cuenta las legislaciones para los impuestos). Si el
valor es ``None`` se toma la fecha actual.
:type date: datetime.date
"""
def __init__(self, date=None):
"""Crea una nueva instancia de contexto"""
self._date = parse_date(date)
self._strdate = self._date.strftime(DATE_FORMAT)
self._valid_taxes = valid_taxes(self._date)
self._buff = {}
def __repr__(self):
"""obj.__repr__() <==> repr(obj)"""
return "<DateContext '{}' at {}>".format(self._date, hex(id(self)))
def _load(self, tax):
if tax not in self._buff:
if tax not in TAXES:
msg = "impuesto '{}' inexistente"
raise TaxNotExists(msg.format(tax))
elif tax not in self._valid_taxes:
msg = "No hay una implementacion valida del impuesto {} para este contexto"
raise NotSuitableTax(msg.format(tax))
for date, modname in TAXES[tax]:
if date <= self.date:
self._buff[tax] = date, importlib.import_module(modname)
break
[documentos] def get(self, impuesto):
"""Retorna una implementación del impuesto dada la fecha del contexto
:param impuesto: nombre del impuesto
:type impuesto: str
"""
self._load(impuesto)
return self._buff[impuesto][1]
[documentos] def date_of(self, impuesto):
"""Retorna la fecha exacta de la implementación del impuesto utilizada
por este contexto
:param impuesto: nombre del impuesto
:type impuesto: str
"""
self._load(impuesto)
return self._buff[impuesto][0]
@property
[documentos] def date(self):
"""Fecha del contexto"""
return self._date
@property
[documentos] def valid_taxes(self):
"""Impuestos validos para este contexto"""
return self._valid_taxes
#~ #===============================================================================
#~ # FUNCTIONS
#~ #===============================================================================
[documentos]def parse_date(date):
"""Convierte varios tipos de objetos a instancias de datetime.date
Todas estas funciones son equivalentes:
>>> # asumiendo que hoy es 25 de diciembre del 2013
>>> parse_date()
>>> parse_date(datetime.datetime.now())
>>> parse_date(datetime.datetime.now().date())
>>> parse_date(datetime.datetime(2013,12,25))
>>> parse_date(datetime.date(2013,12,25))
>>> parse_date("20131225")
>>> parse_date("2013-12-25")
>>> parse_date("2013_12_25")
>>> parse_date("2013_12-25")
>>> parse_date("2013-12_25")
>>> parse_date("2013/12/25")
>>> parse_date("2013/12-25")
>>> parse_date("2013-12/25")
>>> parse_date("2013/12_25")
>>> parse_date("2013_12/25")
>>> parse_date((2013, 12, 25))
"""
if isinstance(date, datetime.date):
return date
elif date is None:
return datetime.datetime.now().date()
elif isinstance(date, basestring):
date = date.replace("-", "").replace("_", "").replace("/", "")
return datetime.datetime.strptime(date, DATE_FORMAT).date()
elif isinstance(date, datetime.datetime):
return date.date()
return datetime.date(*date)
[documentos]def valid_taxes(date=None):
"""Retorna una tupla de todos los impuestos validos para una fecha
dada, o todos si la fecha es None"""
valids = []
if date:
for tax, mindate in TAXES_MIN_DATE.items():
if mindate <= date:
valids.append(tax)
else:
valids = TAXES_MIN_DATE.keys()
return frozenset(valids)
[documentos]def same_implementation(date0, date1):
"""Retorna un set con todos los impuestos que poseen la misma implentacion
en ambas fechas
"""
ctx0 = DateContext(date0)
ctx1 = DateContext(date0)
return frozenset(
tax for tax in ctx0.valid_taxes.intersection(ctx1.valid_taxes)
if ctx0.date_of(tax) == ctx1.date_of(tax)
)
#===============================================================================
# MAIN
#===============================================================================
if __name__ == "__main__":
print(__doc__)