estagio/venda/models.py
#-*- coding: UTF-8 -*-
from django.db import models
from django.core.exceptions import ValidationError
from django.utils.translation import ugettext_lazy as _
from django.contrib.auth.models import User
from django.utils.encoding import python_2_unicode_compatible
from django.core.urlresolvers import reverse
from geoposition.fields import GeopositionField
import datetime
from pessoal.models import Cliente, EnderecoEntregaCliente
from parametros_financeiros.models import FormaPagamento, GrupoEncargo
from movimento.models import Produtos
from configuracoes.models import Parametrizacao
from utilitarios.funcoes_data import datetime_settings_timezone
from contas_receber.models import ContasReceber
from caixa.funcoes import caixa_aberto
@python_2_unicode_compatible
class Venda(models.Model):
u"""
Classe Venda.
Criada para registrar todas as vendas efetivadas no estabelecimento.
Criada em 05/10/2014.
"""
total = models.DecimalField(max_digits=20, decimal_places=2, verbose_name=_(u"Total (R$)"), help_text=u'Valor total da venda.')
data_venda = models.DateTimeField(null=True, db_index=True, verbose_name=_(u"Data da venda"))
data_pedido = models.DateTimeField(null=True, db_index=True, verbose_name=_(u"Data do pedido"))
data_cancelamento = models.DateTimeField(null=True, db_index=True, verbose_name=_(u"Data do cancelamento"))
desconto = models.DecimalField(max_digits=20, decimal_places=0, blank=True, null=True, verbose_name=_(u"Desconto (%)"), help_text=_(u"Desconto sob o valor total da venda."))
status = models.BooleanField(default=False, db_index=True, verbose_name=_(u"Cancelado?"), help_text=_(u"Marcando o Checkbox, a venda será cancelada e os itens financeiros estornados."))
cliente = models.ForeignKey(Cliente, on_delete=models.PROTECT, verbose_name=_(u"Cliente"))
forma_pagamento = models.ForeignKey(FormaPagamento, on_delete=models.PROTECT, verbose_name=_(u"Forma de pagamento"))
grupo_encargo = models.ForeignKey(GrupoEncargo, blank=False, null=False, verbose_name=_(u"Grupo de encargo"), on_delete=models.PROTECT)
observacao = models.TextField(blank=True, verbose_name=_(u"Observações"), help_text=_(u"Descreva na área as informações relavantes da venda."))
pedido = models.CharField(max_length=1, blank=True, db_index=True, choices=((u'S', _(u"Sim")), (u'N', _(u"Não")),), verbose_name=_(u"Pedido?"))
status_pedido = models.BooleanField(default=False, db_index=True, verbose_name=_(u"Pedido confirmado?"), help_text=_(u"Marcando o Checkbox, os itens financeiros serão gerados e o estoque movimentado."))
vendedor = models.ForeignKey(User, blank=True, null=True, on_delete=models.DO_NOTHING, verbose_name=_(u"Vendedor"))
class Meta(object):
verbose_name = _(u"Venda")
verbose_name_plural = _(u"Vendas")
permissions = ((u"pode_exportar_venda", _(u"Exportar Vendas")),)
def __str__(self):
return u'%s' % (self.id)
def formata_data_venda(self):
if self.data_venda:
return self.data_venda
return '-'
formata_data_venda.allow_tags = True
formata_data_venda.short_description = _(u"Data da venda")
formata_data_venda.admin_order_field = 'data_venda'
def vendedor_associado(self):
if self.vendedor:
return u"<b>%s (%s %s)</b>" % (self.vendedor, self.vendedor.first_name, self.vendedor.last_name)
return '-'
vendedor_associado.allow_tags = True
vendedor_associado.short_description = _(u"Vendedor")
def conta_associada(self):
try:
conta = ContasReceber.objects.get(vendas__pk=self.pk).pk
except:
conta = None
if conta:
url = reverse("admin:contas_receber_contasreceber_change", args=[conta])
return u"<a href='%s'>%s</a>" % (url, conta)
return '-'
conta_associada.allow_tags = True
conta_associada.short_description = _(u"Conta a receber")
conta_associada.admin_order_field = 'contas_receber'
def clean(self):
"""
Bloqueia o registro de uma venda quando não há caixa aberto.
"""
if not caixa_aberto() and not self.pk:
raise ValidationError(_(u"Não há caixa aberto. Para efetivar uma venda é necessário ter o caixa aberto."))
def save(self, *args, **kwargs):
"""
Método que trata a geração e cálculo da parte financeira de uma venda.
"""
if self.pk:
conta_gerada = ContasReceber.objects.filter(vendas=self.pk).exists()
super(Venda, self).save(*args, **kwargs)
# Gera financeiro somente se venda for confirmada
if self.pedido == 'N' and self.data_venda and not conta_gerada or (self.status_pedido and not conta_gerada):
# Descrição informada no contas à receber
descricao = _(u"Conta aberta proveniente de venda %(venda)s") % {'venda': self}
# Insere o contas à receber
venda = ContasReceber(data=self.data_venda,
valor_total=self.total,
descricao=descricao,
vendas=self,
cliente=self.cliente,
forma_pagamento=self.forma_pagamento,
grupo_encargo=self.grupo_encargo,
status=False
)
venda.save()
try:
cancela_venda = self.botao_acionado
except:
cancela_venda = None
# trata cancelamento de venda/pedido de venda efetuada
if not self.status and cancela_venda == '_addcancelavenda':
# Define a venda/pedido de venda com status cancelado
self.status = True
self.save()
# Numa venda cancelada: acrescenta a quantidade dos produtos cancelados novamente ao estoque.
for i in ItensVenda.objects.filter(vendas=self.pk).values_list('id', 'produto', 'quantidade'):
produto = Produtos.objects.get(pk=i[1])
produto.quantidade = produto.quantidade + i[2]
produto.save()
# desativa a movimentação feita dos itens de venda
item_venda = ItensVenda.objects.get(pk=i[0])
item_venda.remove_estoque = False
item_venda.save()
if conta_gerada:
# Fecha a conta à receber
conta = ContasReceber.objects.get(vendas=self.pk)
conta.status = True
conta.save()
else:
# Chama a função save original para o save atual do modelo
super(Venda, self).save(*args, **kwargs)
@python_2_unicode_compatible
class ItensVenda(models.Model):
u"""
Classe ItensVenda.
Inline criada para ser exibida na página de vendas.
Nesta, todos os itens de uma venda são registrados.
Criada em 05/10/2014.
"""
quantidade = models.IntegerField(verbose_name=_(u"Quantidade"))
valor_unitario = models.DecimalField(max_digits=20, decimal_places=2, verbose_name=_(u"Valor unitário (R$)"))
valor_total = models.DecimalField(max_digits=20, decimal_places=2, verbose_name=_(u"Total (R$)"))
desconto = models.DecimalField(max_digits=20, decimal_places=0, blank=True, null=True, verbose_name=_(u"Desconto (%)"))
produto = models.ForeignKey(Produtos, on_delete=models.PROTECT, verbose_name=_(u"Produto"))
vendas = models.ForeignKey(Venda, on_delete=models.PROTECT, verbose_name=_(u"Venda"))
remove_estoque = models.BooleanField(default=False, verbose_name=_(u"Removido do estoque?"))
class Meta(object):
verbose_name = _(u"Item de Venda")
verbose_name_plural = _(u"Itens de Venda")
def __str__(self):
return u'%s' % (self.id)
def delete(self, *args, **kwargs):
# Quando item de venda é removido, as quantidades em estoque são acrescidas
item = ItensVenda.objects.get(pk=self.pk)
produto = Produtos.objects.get(pk=item.produto.pk)
produto.quantidade = produto.quantidade + item.quantidade
super(ItensVenda, self).delete(*args, **kwargs)
produto.save()
def save(self, *args, **kwargs):
"""
Método que trata a remoção da quantidade de produtos ao estoque.
"""
if self.pk is None:
# Subtrai a quantidade de produtos vendidos com a que já existe no estoque
super(ItensVenda, self).save(*args, **kwargs)
self.remove_estoque = True
self.save()
produto = Produtos.objects.get(pk=self.produto.pk)
produto.quantidade = produto.quantidade - self.quantidade
produto.save()
else:
super(ItensVenda, self).save(*args, **kwargs)
# def clean_fields(self, *args, **kwargs):
# """
# Método que trata o movimento no estoque.
# """
# quant_produto_estoque = Produtos.objects.filter(pk=self.produto.pk).values_list('quantidade')[0][0]
# if self.pk is None and self.quantidade > quant_produto_estoque:
# raise ValidationError({'quantidade': ["Há somente %(quantidade)s unidade(s) deste produto em estoque." % {'quantidade': quant_produto_estoque},]})
@python_2_unicode_compatible
class EntregaVenda(models.Model):
status = models.BooleanField(default=False, db_index=True, verbose_name=_(u"Entrega agendada?"))
endereco = models.ForeignKey(EnderecoEntregaCliente, null=True, blank=True, on_delete=models.PROTECT, verbose_name=_(u"Endereço"))
data = models.DateTimeField(null=True, blank=True, db_index=True, verbose_name=_(u"Data de entrega"))
observacao = models.TextField(blank=True, verbose_name=_(u"Observações"), help_text=_(u"Descreva na área as informações relavantes da entrega."))
posicao = GeopositionField(blank=True, verbose_name=_(u"Posição"))
venda = models.OneToOneField(Venda, null=True, blank=True, verbose_name=_(u"Venda"))
# status_entrega = models.CharField(max_length=1, blank=True, choices=((u'1', _(u"Realizada")), (u'2', _(u"Em andamento")), (u'3', _(u"Atrasada")),), verbose_name=_(u"Status da entrega"))
class Meta(object):
verbose_name = _(u"Entrega")
verbose_name_plural = _(u"Entregas")
permissions = ((u"pode_exportar_entregavenda", _(u"Exportar Entregas")),)
def __str__(self):
return u'%s' % (self.id)
def clean_fields(self, *args, **kwargs):
quantidade = Parametrizacao.objects.get().intervalo_dias_entrega_venda
venda = Venda.objects.get(pk=self.venda.pk)
data_minima_para_entrega = datetime_settings_timezone(venda.data_venda) + datetime.timedelta(days=quantidade)
# Data de entrega não pode ser menor que data de venda + quantidade de dias para entrega (configurada nas parametrizações do sistema)
if self.data and self.data < data_minima_para_entrega:
raise ValidationError({'data': [_(u"Data de entrega inválida. Data mínima para entrega dos produtos: %(data_minima_entrega)s") % {'data_minima_entrega': data_minima_para_entrega.strftime('%d/%m/%Y às %H:%M:%S')},]})