diff --git a/Inventario/__pycache__/forms.cpython-313.pyc b/Inventario/__pycache__/forms.cpython-313.pyc index b8fe110..6fd3fff 100644 Binary files a/Inventario/__pycache__/forms.cpython-313.pyc and b/Inventario/__pycache__/forms.cpython-313.pyc differ diff --git a/Inventario/__pycache__/models.cpython-313.pyc b/Inventario/__pycache__/models.cpython-313.pyc index 69f475c..f31d56e 100644 Binary files a/Inventario/__pycache__/models.cpython-313.pyc and b/Inventario/__pycache__/models.cpython-313.pyc differ diff --git a/Inventario/__pycache__/views.cpython-313.pyc b/Inventario/__pycache__/views.cpython-313.pyc index 7fdd3c4..46650bf 100644 Binary files a/Inventario/__pycache__/views.cpython-313.pyc and b/Inventario/__pycache__/views.cpython-313.pyc differ diff --git a/Inventario/forms.py b/Inventario/forms.py index 2d57d19..1c0b469 100644 --- a/Inventario/forms.py +++ b/Inventario/forms.py @@ -1,5 +1,5 @@ from django import forms -from .models import proveedor, bodega, tipo_articulo, articulo,inventario,tipo_inv_movimiento +from .models import proveedor, bodega, tipo_articulo, articulo,inventario,tipo_inv_movimiento,Venta class formulario_proveedor(forms.ModelForm): class Meta: @@ -36,11 +36,23 @@ class formulario_articulo(forms.ModelForm): tipo_articulo=forms.ModelChoiceField(queryset=tipo_articulo.objects.all(), empty_label="Selecciona un tipo de articulo", widget=forms.Select(attrs={'class': 'btn btn-secondary dropdown-toggle', 'data-bs-toggle': 'dropdown', 'aria-expanded': 'false'}), - label="tipo_articulo") + label="Tipo de articulo") proveedor=forms.ModelChoiceField(queryset=proveedor.objects.all(), empty_label="Selecciona un proveedor", widget=forms.Select(attrs={'class': 'btn btn-secondary dropdown-toggle ', 'data-bs-toggle': 'dropdown', 'aria-expanded': 'false'}), - label="proveedor") + label="Proveedor") + + precio_compra=forms.DecimalField( + label="Precio de compra", + max_digits=10, + decimal_places=2, + widget=forms.NumberInput(attrs={'class': 'form-control', 'placeholder': 'Escribe un decimal'})) + + precio_venta=forms.DecimalField( + label="Precio de venta", + max_digits=10, + decimal_places=2, + widget=forms.NumberInput(attrs={'class': 'form-control', 'placeholder': 'Escribe un decimal'})) class Meta: model = articulo fields = '__all__' @@ -48,6 +60,7 @@ class formulario_articulo(forms.ModelForm): 'nombre_articulo': forms.TextInput(attrs={'class': 'form-control' , 'placeholder': 'escriba el nombre del articulo'}), 'medida': forms.TextInput(attrs={'class':'form-control','placeholder':'medida del producto lts, cm, etc'}), 'descripcion': forms.Textarea(attrs={'class':'form-control','placeholder':'describa el producto','rows':'5'}), + } class formilario_inventarios(forms.ModelForm): @@ -65,7 +78,6 @@ class formilario_inventarios(forms.ModelForm): fields = '__all__' widgets={ 'cantidad': forms.TextInput(attrs={'class': 'form-control' , 'placeholder': 'Cantidad de productos','oninput':'permitirSoloNumeros(this)',}), - 'precio': forms.NumberInput(attrs={'class':'form-control','placeholder':'Ej: 99.99','step': '0.01','min':'0','max':'10000',}), } class formilario_inventario(forms.ModelForm): @@ -93,7 +105,28 @@ class formilario_inventario(forms.ModelForm): fields = '__all__' widgets={ 'fecha': forms.DateInput(attrs={'type': 'date','class': 'form-control','placeholder': 'Seleccione una fecha'}), - 'precio': forms.NumberInput(attrs={'class':'form-control','placeholder':'Ej: 99.99','step': '0.01','min':'0','max':'10000',}), 'cantidad': forms.TextInput(attrs={'class': 'form-control' , 'placeholder': 'Cantidad de productos','oninput':'permitirSoloNumeros(this)',}), 'observacion': forms.Textarea(attrs={'class':'form-control','placeholder':'observacion','rows':'5'}), } + +class VentaForm(forms.ModelForm): + + articulo=forms.ModelChoiceField(queryset=articulo.objects.all(), + empty_label="selecciones un articulo", + widget=forms.Select(attrs={'class': 'btn btn-secondary dropdown-toggle', 'data-bs-toggle': 'dropdown', 'aria-expanded': 'false'}), + label="articulo") + bodega=forms.ModelChoiceField(queryset=bodega.objects.all(), + empty_label="Selecciona una bodega", + widget=forms.Select(attrs={'class': 'btn btn-secondary dropdown-toggle ', 'data-bs-toggle': 'dropdown', 'aria-expanded': 'false'}), + label="bodega") + cantidad=forms.DecimalField( + label="cantidad", + max_digits=10, + decimal_places=2, + widget=forms.NumberInput(attrs={'class': 'form-control', 'placeholder': 'Escribe un decimal'})) + class Meta: + model = Venta + fields = ['bodega', 'articulo', 'cantidad', 'observaciones'] + widgets={ + 'observaciones': forms.Textarea(attrs={'class':'form-control','placeholder':'describa el producto','rows':'5'}), + } diff --git a/Inventario/migrations/0004_remove_inventario_precio_and_more.py b/Inventario/migrations/0004_remove_inventario_precio_and_more.py new file mode 100644 index 0000000..7233ff5 --- /dev/null +++ b/Inventario/migrations/0004_remove_inventario_precio_and_more.py @@ -0,0 +1,50 @@ +# Generated by Django 5.1.4 on 2024-12-17 22:48 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('Inventario', '0003_remove_articulo_codigo_articulo_articulo_medida'), + ] + + operations = [ + migrations.RemoveField( + model_name='inventario', + name='precio', + ), + migrations.RemoveField( + model_name='movimiento_inventario', + name='precio', + ), + migrations.AddField( + model_name='articulo', + name='precio_compra', + field=models.DecimalField(decimal_places=2, max_digits=10, null=True), + ), + migrations.AddField( + model_name='articulo', + name='precio_venta', + field=models.DecimalField(decimal_places=2, max_digits=10, null=True), + ), + migrations.AlterField( + model_name='movimiento_inventario', + name='fecha', + field=models.DateTimeField(auto_now_add=True, null=True), + ), + migrations.CreateModel( + name='Venta', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('fecha', models.DateTimeField(auto_now_add=True)), + ('cantidad', models.PositiveIntegerField()), + ('precio_unitario', models.DecimalField(blank=True, decimal_places=2, max_digits=10, null=True)), + ('total', models.DecimalField(blank=True, decimal_places=2, max_digits=12)), + ('observaciones', models.TextField(blank=True)), + ('articulo', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='Inventario.articulo')), + ('bodega', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='Inventario.bodega')), + ], + ), + ] diff --git a/Inventario/migrations/__pycache__/0004_remove_inventario_precio_and_more.cpython-313.pyc b/Inventario/migrations/__pycache__/0004_remove_inventario_precio_and_more.cpython-313.pyc new file mode 100644 index 0000000..dd430da Binary files /dev/null and b/Inventario/migrations/__pycache__/0004_remove_inventario_precio_and_more.cpython-313.pyc differ diff --git a/Inventario/models.py b/Inventario/models.py index c0078ce..9ee72c7 100644 --- a/Inventario/models.py +++ b/Inventario/models.py @@ -1,5 +1,7 @@ from django.db import models from django.contrib.auth.models import User +from django.db import transaction + # Create your models here. @@ -34,6 +36,8 @@ class articulo(models.Model): nombre_articulo=models.CharField(max_length=100) medida = models.CharField(max_length=100, blank=True, null=True) descripcion = models.TextField(blank=True) + precio_compra=models.DecimalField( max_digits=10, decimal_places=2, null=True) + precio_venta=models.DecimalField( max_digits=10, decimal_places=2, null=True) proveedor = models.ForeignKey(proveedor, on_delete=models.CASCADE) tipo_articulo=models.ForeignKey(tipo_articulo, on_delete=models.CASCADE) def __str__(self): @@ -44,15 +48,60 @@ class inventario(models.Model): bodega=models.ForeignKey(bodega, on_delete=models.CASCADE) articulo=models.ForeignKey(articulo, on_delete=models.CASCADE) cantidad=models.IntegerField(null=True) - precio=models.DecimalField( max_digits=10, decimal_places=2) +class Venta(models.Model): + fecha = models.DateTimeField(auto_now_add=True) + bodega = models.ForeignKey(bodega, on_delete=models.CASCADE) + articulo = models.ForeignKey(articulo, on_delete=models.CASCADE) + cantidad = models.PositiveIntegerField() + precio_unitario = models.DecimalField(max_digits=10, decimal_places=2, blank=True, null=True) + total = models.DecimalField(max_digits=12, decimal_places=2, blank=True) + observaciones = models.TextField(blank=True) + + def save(self, *args, **kwargs): + # Asignar el precio de venta del artículo si no se ha definido + if not self.precio_unitario: + self.precio_unitario = self.articulo.precio_venta or 0 + + # Calcular el total antes de guardar + if not self.total: + self.total = self.cantidad * self.precio_unitario + + # Usar una transacción atómica para asegurar que todo suceda de forma consistente + with transaction.atomic(): + # Guardar la venta primero + super(Venta, self).save(*args, **kwargs) + + # Obtener el artículo y bodega para verificar el inventario + inventario_item = inventario.objects.get(bodega=self.bodega, articulo=self.articulo) + # Verificar que haya suficiente cantidad en el inventario + if inventario_item.cantidad >= self.cantidad: + # Crear el movimiento de inventario (salida) + tipo_salida = tipo_inv_movimiento.objects.get(tipo_movimiento="Salida") + movimiento = movimiento_inventario( + tipo_inventario=tipo_salida, + bodega=self.bodega, + articulo=self.articulo, + cantidad=self.cantidad, + observaciones=f"Salida por venta. {self.observaciones}" + ) + movimiento.save() + + # Actualizar el inventario después de la venta + inventario_item.cantidad -= self.cantidad + inventario_item.save() + else: + # Si no hay suficiente inventario, lanzar una excepción + raise ValueError("No hay suficiente inventario para realizar esta venta.") + + def __str__(self): + return f"Venta de {self.articulo.nombre_articulo} - {self.cantidad} unidades" class movimiento_inventario(models.Model): - fecha=models.DateTimeField(null=True) + fecha=models.DateTimeField(null=True, auto_now_add=True) tipo_inventario=models.ForeignKey(tipo_inv_movimiento,on_delete=models.CASCADE) bodega=models.ForeignKey(bodega, on_delete=models.CASCADE) articulo=models.ForeignKey(articulo, on_delete=models.CASCADE) cantidad=models.IntegerField(null=True) - precio=models.DecimalField(max_digits=10, decimal_places=2) observaciones=models.TextField(blank=True) diff --git a/Inventario/templates/Base.html b/Inventario/templates/Base.html index 29657a7..ac73800 100644 --- a/Inventario/templates/Base.html +++ b/Inventario/templates/Base.html @@ -12,13 +12,13 @@