162 lines
6.8 KiB
Python
162 lines
6.8 KiB
Python
import re
|
|
from datetime import datetime
|
|
|
|
from django.db import models
|
|
from django.core.validators import MinLengthValidator
|
|
from django.core.exceptions import ValidationError
|
|
from django.utils.timezone import now
|
|
|
|
|
|
def validate_rfc(value):
|
|
l = 4
|
|
if len(value)==12:
|
|
l = 3
|
|
s = value[0:l]
|
|
r = re.match('[A-ZÑ&]{%s}' % l, s)
|
|
if not r:
|
|
raise ValidationError('Caracteres inválidos al inicio del RFC')
|
|
|
|
s = value[-3:]
|
|
r = re.match('[A-Z0-9]{3}', s)
|
|
if not r:
|
|
raise ValidationError('Caracteres inválidos al final del RFC')
|
|
|
|
s = value[l:l+6]
|
|
r = re.match('[0-9]{6}', s)
|
|
msg = 'Fecha inválida en el RFC'
|
|
if not r:
|
|
raise ValidationError(msg)
|
|
|
|
try:
|
|
datetime.strptime(s,"%y%m%d")
|
|
except:
|
|
raise ValidationError(msg)
|
|
|
|
|
|
class RFCField(models.CharField):
|
|
description = 'Field to RFC of México'
|
|
default_validators = [MinLengthValidator(12), validate_rfc]
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
kwargs['max_length'] = 13
|
|
super().__init__(*args, **kwargs)
|
|
|
|
def to_python(self, value):
|
|
return value.upper()
|
|
|
|
|
|
class Clients(models.Model):
|
|
rfc = RFCField('RFC', unique=True)
|
|
name = models.CharField('Razón Social', max_length=500)
|
|
key = models.TextField('Key', default='', blank=True)
|
|
cer = models.TextField('Cer', default='', blank=True)
|
|
serial_number = models.CharField('Fiel Serie', default='', blank=True, max_length=100)
|
|
date_from = models.DateTimeField('Desde', null=True, blank=True)
|
|
date_to = models.DateTimeField('Hasta', null=True, blank=True)
|
|
|
|
class Meta:
|
|
ordering = ['name']
|
|
verbose_name = 'Cliente'
|
|
verbose_name_plural = 'Clientes'
|
|
|
|
def __str__(self):
|
|
return self.name
|
|
|
|
|
|
class Cfdi(models.Model):
|
|
uuid = models.UUIDField('UUID', unique=True)
|
|
version = models.CharField('Version', max_length=10)
|
|
version_nomina = models.CharField('Ver. Nom.', max_length=10, default='', blank=True)
|
|
serie = models.CharField('Serie', max_length=50, default='', blank=True)
|
|
folio = models.CharField('Folio', max_length=50, default='', blank=True)
|
|
date_cfdi = models.DateTimeField('Fecha CFDI')
|
|
date_stamp = models.DateTimeField('Fecha Timbrado')
|
|
date_cancel = models.DateTimeField('Fecha Cancelada', null=True)
|
|
type_cfdi = models.CharField('Tipo', max_length=100, default='', blank=True)
|
|
no_cert = models.CharField('No. Certificado', max_length=50, default='', blank=True)
|
|
no_cert_sat = models.CharField('Cert. SAT', max_length=50, default='', blank=True)
|
|
place_expedition = models.CharField('Lugar Expedición', max_length=200, default='', blank=True)
|
|
currency = models.CharField('Moneda', max_length=100, default='MXN', blank=True)
|
|
type_change = models.DecimalField('T.C.', max_digits=19, decimal_places=6, null=True)
|
|
way_pay = models.CharField('Forma de pago', max_length=100, default='', blank=True)
|
|
method_pay = models.CharField('Método de pago', max_length=100, default='', blank=True)
|
|
subtotal = models.DecimalField('Subtotal', max_digits=19, decimal_places=6)
|
|
discount = models.DecimalField('Descuento', max_digits=19, decimal_places=6, null=True)
|
|
tax_trasladados = models.DecimalField('I. Trasladados', max_digits=19, decimal_places=6, null=True)
|
|
tax_retenidos = models.DecimalField('I. Retenidos', max_digits=19, decimal_places=6, null=True)
|
|
tax_others = models.DecimalField('Otros I.', max_digits=19, decimal_places=6, null=True)
|
|
total = models.DecimalField('Total', max_digits=19, decimal_places=6)
|
|
emisor_rfc = RFCField('Emisor RFC')
|
|
emisor = models.CharField('Emisor', max_length=500, default='', blank=True)
|
|
regimen_fiscal = models.CharField('Regimen Fiscal', max_length=10, default='', blank=True)
|
|
registro_patronal = models.CharField('Registro Patronal', max_length=50, default='', blank=True)
|
|
receptor_rfc = RFCField('Receptor RFC')
|
|
receptor = models.CharField('Receptor', max_length=500, default='', blank=True)
|
|
uso_cfdi = models.CharField('Uso CFDI', max_length=50, default='', blank=True)
|
|
rfc_pac = models.CharField('RFC PAC', max_length=15, default='', blank=True)
|
|
status_sat = models.CharField('Estatus SAT', max_length=100, default='', blank=True)
|
|
xml = models.TextField('XML')
|
|
|
|
class Meta:
|
|
ordering = ['-date_cfdi']
|
|
verbose_name = 'Cfdi'
|
|
verbose_name_plural = 'Cfdis'
|
|
|
|
def __str__(self):
|
|
return str(self.uuid)
|
|
|
|
|
|
class Taxes(models.Model):
|
|
TYPES = [
|
|
('T', 'Traslado'),
|
|
('R', 'Retención'),
|
|
]
|
|
cfdi = models.ForeignKey(Cfdi,
|
|
related_name='cfdi_taxes', on_delete=models.CASCADE)
|
|
type_tax = models.CharField('Tipo', max_length=1, choices=TYPES)
|
|
key_sat = models.CharField('Clave SAT', max_length=5, default='', blank=True)
|
|
importe = models.DecimalField('Importe', max_digits=19, decimal_places=6)
|
|
type_factor = models.CharField('Tipo Factor', max_length=10, default='', blank=True)
|
|
rate = models.DecimalField('Tasa', max_digits=19, decimal_places=6, null=True)
|
|
|
|
class Meta:
|
|
verbose_name = 'Impuesto'
|
|
verbose_name_plural = 'Impuestos'
|
|
|
|
|
|
class CfdiDetails(models.Model):
|
|
cfdi = models.ForeignKey(Cfdi,
|
|
related_name='cfdi_details', on_delete=models.CASCADE)
|
|
key = models.CharField('Clave', max_length=15, default='', blank=True)
|
|
key_sat = models.CharField('Clave SAT', max_length=15, default='', blank=True)
|
|
key_unit = models.CharField('Clave Unidad', max_length=15, default='', blank=True)
|
|
unit = models.CharField('Unidad', max_length=100, default='', blank=True)
|
|
description = models.CharField('Descripción', max_length=5000, default='', blank=True)
|
|
cant = models.DecimalField('Cantidad', max_digits=19, decimal_places=6)
|
|
value = models.DecimalField('Valor Unitario', max_digits=19, decimal_places=6)
|
|
discount = models.DecimalField('Descuento', max_digits=19, decimal_places=6, null=True)
|
|
importe = models.DecimalField('Importe', max_digits=19, decimal_places=6)
|
|
|
|
class Meta:
|
|
verbose_name = 'Concepto'
|
|
verbose_name_plural = 'Conceptos'
|
|
|
|
|
|
class DetailTaxes(models.Model):
|
|
TYPES = [
|
|
('T', 'Traslado'),
|
|
('R', 'Retención'),
|
|
]
|
|
detail = models.ForeignKey(CfdiDetails,
|
|
related_name='detail', on_delete=models.CASCADE)
|
|
type_tax = models.CharField('Tipo', max_length=1, choices=TYPES)
|
|
key_sat = models.CharField('Clave SAT', max_length=5, default='', blank=True)
|
|
base = models.DecimalField('Base', max_digits=19, decimal_places=6)
|
|
importe = models.DecimalField('Importe', max_digits=19, decimal_places=6)
|
|
type_factor = models.CharField('Tipo Factor', max_length=10, default='', blank=True)
|
|
rate = models.DecimalField('Tasa', max_digits=19, decimal_places=6, null=True)
|
|
|
|
class Meta:
|
|
verbose_name = 'Impuesto'
|
|
verbose_name_plural = 'Impuestos'
|