commit
21978f7420
@ -0,0 +1 @@ |
|||||||
|
venv/ |
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,3 @@ |
|||||||
|
from django.contrib import admin |
||||||
|
|
||||||
|
# Register your models here. |
||||||
@ -0,0 +1,6 @@ |
|||||||
|
from django.apps import AppConfig |
||||||
|
|
||||||
|
|
||||||
|
class InventarioConfig(AppConfig): |
||||||
|
default_auto_field = 'django.db.models.BigAutoField' |
||||||
|
name = 'Inventario' |
||||||
@ -0,0 +1,99 @@ |
|||||||
|
from django import forms |
||||||
|
from .models import proveedor, bodega, tipo_articulo, articulo,inventario,tipo_inv_movimiento |
||||||
|
|
||||||
|
class formulario_proveedor(forms.ModelForm): |
||||||
|
class Meta: |
||||||
|
model = proveedor |
||||||
|
fields = '__all__' |
||||||
|
widgets={ |
||||||
|
'nombre_proveedor': forms.TextInput(attrs={'class': 'form-control' , 'placeholder': 'Escribe el nombre del proveedor'}), |
||||||
|
'email': forms.EmailInput(attrs={'class':'form-control','placeholder':'email del proveedor'}), |
||||||
|
'direccion': forms.Textarea(attrs={'class':'form-control','placeholder':'direccion del proveedor','rows':'5'}), |
||||||
|
'telefono': forms.TextInput(attrs={'type':'tel','class':'form-control','placeholder':'0000-0000','maxlength':'9' ,'oninput':'formatearInput(this)'}), |
||||||
|
} |
||||||
|
|
||||||
|
class formulario_bodega(forms.ModelForm): |
||||||
|
class Meta: |
||||||
|
model = bodega |
||||||
|
fields = '__all__' |
||||||
|
widgets={ |
||||||
|
'nombre_bodega': forms.TextInput(attrs={'class': 'form-control' , 'placeholder': 'Escribe el nombre de la bodega'}), |
||||||
|
'estado': forms.CheckboxInput(attrs={'class':'form-check-input'}), |
||||||
|
'direccion_bodega': forms.Textarea(attrs={'class':'form-control','placeholder':'direccion de la bodega','rows':'5'}), |
||||||
|
'codigo_bodega': forms.TextInput(attrs={'class': 'form-control' , 'placeholder': 'Escribe el codigo de la bodega','oninput':'permitirSoloNumeros(this)'}), |
||||||
|
} |
||||||
|
|
||||||
|
class formulario_tipo(forms.ModelForm): |
||||||
|
class Meta: |
||||||
|
model = tipo_articulo |
||||||
|
fields = '__all__' |
||||||
|
widgets={ |
||||||
|
'tipo_articulo': forms.TextInput(attrs={'class': 'form-control' , 'placeholder': 'escriba el tipo de articulo'}), |
||||||
|
'descripcion': forms.Textarea(attrs={'class':'form-control','placeholder':'escriba una descripcion de este tipo de articulo','rows':'5'}), |
||||||
|
} |
||||||
|
|
||||||
|
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") |
||||||
|
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") |
||||||
|
class Meta: |
||||||
|
model = articulo |
||||||
|
fields = '__all__' |
||||||
|
widgets={ |
||||||
|
'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): |
||||||
|
|
||||||
|
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") |
||||||
|
class Meta: |
||||||
|
model = inventario |
||||||
|
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): |
||||||
|
tipo_inventario=forms.ModelChoiceField(queryset=tipo_inv_movimiento.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="tipo de inventario") |
||||||
|
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") |
||||||
|
articulo=forms.ModelChoiceField(queryset=articulo.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="articulo") |
||||||
|
def __init__(self, *args, **kwargs): |
||||||
|
super().__init__(*args, **kwargs) |
||||||
|
# Personaliza cómo se muestran las opciones en el dropdown |
||||||
|
self.fields['articulo'].label_from_instance = lambda obj: f"{obj.nombre_articulo} ({obj.medida})" if obj.medida else obj.nombre_articulo |
||||||
|
self.fields['bodega'].label_from_instance = lambda obj: f"{obj.nombre_bodega} ({obj.codigo_bodega})" if obj.codigo_bodega else obj.nombre_bodega |
||||||
|
|
||||||
|
|
||||||
|
class Meta: |
||||||
|
model = inventario |
||||||
|
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'}), |
||||||
|
} |
||||||
@ -0,0 +1,76 @@ |
|||||||
|
# Generated by Django 5.1.4 on 2024-12-11 06:38 |
||||||
|
|
||||||
|
import django.db.models.deletion |
||||||
|
from django.db import migrations, models |
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration): |
||||||
|
|
||||||
|
initial = True |
||||||
|
|
||||||
|
dependencies = [ |
||||||
|
] |
||||||
|
|
||||||
|
operations = [ |
||||||
|
migrations.CreateModel( |
||||||
|
name='bodega', |
||||||
|
fields=[ |
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), |
||||||
|
('codigo_bodega', models.IntegerField()), |
||||||
|
('nombre_bodega', models.CharField(max_length=40)), |
||||||
|
('estado', models.BooleanField(default=True)), |
||||||
|
], |
||||||
|
), |
||||||
|
migrations.CreateModel( |
||||||
|
name='proveedor', |
||||||
|
fields=[ |
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), |
||||||
|
('nombre_proveedor', models.CharField(max_length=100)), |
||||||
|
('email', models.EmailField(blank=True, max_length=254, null=True)), |
||||||
|
('telefono', models.CharField(blank=True, max_length=20, null=True)), |
||||||
|
('direccion', models.TextField(blank=True, null=True)), |
||||||
|
('estado', models.BooleanField(default=True)), |
||||||
|
], |
||||||
|
), |
||||||
|
migrations.CreateModel( |
||||||
|
name='tipo_articulo', |
||||||
|
fields=[ |
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), |
||||||
|
('tipo_articulo', models.CharField(max_length=100)), |
||||||
|
], |
||||||
|
), |
||||||
|
migrations.CreateModel( |
||||||
|
name='tipo_inv_movimiento', |
||||||
|
fields=[ |
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), |
||||||
|
('tipo_movimiento', models.CharField(max_length=100)), |
||||||
|
], |
||||||
|
), |
||||||
|
migrations.CreateModel( |
||||||
|
name='articulo', |
||||||
|
fields=[ |
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), |
||||||
|
('nombre_articulo', models.CharField(max_length=100)), |
||||||
|
('precio_articulo', models.DecimalField(decimal_places=2, max_digits=10)), |
||||||
|
('stock', models.IntegerField()), |
||||||
|
('creado', models.DateTimeField(auto_now_add=True)), |
||||||
|
('actualizado', models.DateTimeField(auto_now=True)), |
||||||
|
('activo', models.BooleanField(default=True)), |
||||||
|
('bodega', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='bodega', to='Inventario.bodega')), |
||||||
|
('proveedor', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='productos', to='Inventario.proveedor')), |
||||||
|
], |
||||||
|
), |
||||||
|
migrations.CreateModel( |
||||||
|
name='movimiento_inventario', |
||||||
|
fields=[ |
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), |
||||||
|
('cantidad', models.IntegerField()), |
||||||
|
('descripcion', models.TextField(blank=True)), |
||||||
|
('total_costo', models.DecimalField(decimal_places=2, max_digits=10)), |
||||||
|
('numero_mov', models.IntegerField()), |
||||||
|
('fecha', models.DateTimeField(auto_now_add=True)), |
||||||
|
('articulo', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='articulos', to='Inventario.articulo')), |
||||||
|
('tipo_movimiento', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='mov_inv', to='Inventario.tipo_inv_movimiento')), |
||||||
|
], |
||||||
|
), |
||||||
|
] |
||||||
@ -0,0 +1,138 @@ |
|||||||
|
# Generated by Django 5.1.4 on 2024-12-13 02:59 |
||||||
|
|
||||||
|
import django.db.models.deletion |
||||||
|
from django.db import migrations, models |
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration): |
||||||
|
|
||||||
|
dependencies = [ |
||||||
|
('Inventario', '0001_initial'), |
||||||
|
] |
||||||
|
|
||||||
|
operations = [ |
||||||
|
migrations.RenameField( |
||||||
|
model_name='movimiento_inventario', |
||||||
|
old_name='descripcion', |
||||||
|
new_name='observaciones', |
||||||
|
), |
||||||
|
migrations.RenameField( |
||||||
|
model_name='movimiento_inventario', |
||||||
|
old_name='total_costo', |
||||||
|
new_name='precio', |
||||||
|
), |
||||||
|
migrations.RemoveField( |
||||||
|
model_name='articulo', |
||||||
|
name='activo', |
||||||
|
), |
||||||
|
migrations.RemoveField( |
||||||
|
model_name='articulo', |
||||||
|
name='actualizado', |
||||||
|
), |
||||||
|
migrations.RemoveField( |
||||||
|
model_name='articulo', |
||||||
|
name='bodega', |
||||||
|
), |
||||||
|
migrations.RemoveField( |
||||||
|
model_name='articulo', |
||||||
|
name='creado', |
||||||
|
), |
||||||
|
migrations.RemoveField( |
||||||
|
model_name='articulo', |
||||||
|
name='precio_articulo', |
||||||
|
), |
||||||
|
migrations.RemoveField( |
||||||
|
model_name='articulo', |
||||||
|
name='stock', |
||||||
|
), |
||||||
|
migrations.RemoveField( |
||||||
|
model_name='movimiento_inventario', |
||||||
|
name='numero_mov', |
||||||
|
), |
||||||
|
migrations.RemoveField( |
||||||
|
model_name='movimiento_inventario', |
||||||
|
name='tipo_movimiento', |
||||||
|
), |
||||||
|
migrations.RemoveField( |
||||||
|
model_name='proveedor', |
||||||
|
name='estado', |
||||||
|
), |
||||||
|
migrations.AddField( |
||||||
|
model_name='articulo', |
||||||
|
name='codigo_articulo', |
||||||
|
field=models.IntegerField(null=True), |
||||||
|
), |
||||||
|
migrations.AddField( |
||||||
|
model_name='articulo', |
||||||
|
name='descripcion', |
||||||
|
field=models.TextField(blank=True), |
||||||
|
), |
||||||
|
migrations.AddField( |
||||||
|
model_name='articulo', |
||||||
|
name='tipo_articulo', |
||||||
|
field=models.ForeignKey(default=40, on_delete=django.db.models.deletion.CASCADE, to='Inventario.tipo_articulo'), |
||||||
|
preserve_default=False, |
||||||
|
), |
||||||
|
migrations.AddField( |
||||||
|
model_name='bodega', |
||||||
|
name='direccion_bodega', |
||||||
|
field=models.TextField(blank=True), |
||||||
|
), |
||||||
|
migrations.AddField( |
||||||
|
model_name='movimiento_inventario', |
||||||
|
name='bodega', |
||||||
|
field=models.ForeignKey(default=60, on_delete=django.db.models.deletion.CASCADE, to='Inventario.bodega'), |
||||||
|
preserve_default=False, |
||||||
|
), |
||||||
|
migrations.AddField( |
||||||
|
model_name='movimiento_inventario', |
||||||
|
name='tipo_inventario', |
||||||
|
field=models.ForeignKey(default=100, on_delete=django.db.models.deletion.CASCADE, to='Inventario.tipo_inv_movimiento'), |
||||||
|
preserve_default=False, |
||||||
|
), |
||||||
|
migrations.AddField( |
||||||
|
model_name='tipo_articulo', |
||||||
|
name='descripcion', |
||||||
|
field=models.TextField(blank=True), |
||||||
|
), |
||||||
|
migrations.AlterField( |
||||||
|
model_name='articulo', |
||||||
|
name='proveedor', |
||||||
|
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='Inventario.proveedor'), |
||||||
|
), |
||||||
|
migrations.AlterField( |
||||||
|
model_name='bodega', |
||||||
|
name='codigo_bodega', |
||||||
|
field=models.IntegerField(null=True), |
||||||
|
), |
||||||
|
migrations.AlterField( |
||||||
|
model_name='movimiento_inventario', |
||||||
|
name='articulo', |
||||||
|
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='Inventario.articulo'), |
||||||
|
), |
||||||
|
migrations.AlterField( |
||||||
|
model_name='movimiento_inventario', |
||||||
|
name='cantidad', |
||||||
|
field=models.IntegerField(null=True), |
||||||
|
), |
||||||
|
migrations.AlterField( |
||||||
|
model_name='movimiento_inventario', |
||||||
|
name='fecha', |
||||||
|
field=models.DateTimeField(null=True), |
||||||
|
), |
||||||
|
migrations.AlterField( |
||||||
|
model_name='tipo_articulo', |
||||||
|
name='tipo_articulo', |
||||||
|
field=models.CharField(max_length=40), |
||||||
|
), |
||||||
|
migrations.CreateModel( |
||||||
|
name='inventario', |
||||||
|
fields=[ |
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), |
||||||
|
('cantidad', models.IntegerField(null=True)), |
||||||
|
('precio', models.DecimalField(decimal_places=2, max_digits=10)), |
||||||
|
('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')), |
||||||
|
], |
||||||
|
), |
||||||
|
] |
||||||
@ -0,0 +1,22 @@ |
|||||||
|
# Generated by Django 5.1.4 on 2024-12-15 18:34 |
||||||
|
|
||||||
|
from django.db import migrations, models |
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration): |
||||||
|
|
||||||
|
dependencies = [ |
||||||
|
('Inventario', '0002_rename_descripcion_movimiento_inventario_observaciones_and_more'), |
||||||
|
] |
||||||
|
|
||||||
|
operations = [ |
||||||
|
migrations.RemoveField( |
||||||
|
model_name='articulo', |
||||||
|
name='codigo_articulo', |
||||||
|
), |
||||||
|
migrations.AddField( |
||||||
|
model_name='articulo', |
||||||
|
name='medida', |
||||||
|
field=models.CharField(blank=True, max_length=100, null=True), |
||||||
|
), |
||||||
|
] |
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,58 @@ |
|||||||
|
from django.db import models |
||||||
|
from django.contrib.auth.models import User |
||||||
|
|
||||||
|
|
||||||
|
# Create your models here. |
||||||
|
class tipo_articulo(models.Model): |
||||||
|
tipo_articulo= models.CharField(max_length=40) |
||||||
|
descripcion=models.TextField(blank=True) |
||||||
|
def __str__(self): |
||||||
|
return self.tipo_articulo |
||||||
|
|
||||||
|
class proveedor(models.Model): |
||||||
|
nombre_proveedor=models.CharField(max_length=100) |
||||||
|
email = models.EmailField(null=True, blank=True) |
||||||
|
telefono = models.CharField(max_length=20, null=True, blank=True) |
||||||
|
direccion = models.TextField(null=True, blank=True) |
||||||
|
def __str__(self): |
||||||
|
return self.nombre_proveedor |
||||||
|
|
||||||
|
class tipo_inv_movimiento(models.Model): |
||||||
|
tipo_movimiento=models.CharField(max_length=100) |
||||||
|
def __str__(self): |
||||||
|
return self.tipo_movimiento |
||||||
|
|
||||||
|
class bodega(models.Model): |
||||||
|
codigo_bodega=models.IntegerField(null=True) |
||||||
|
nombre_bodega= models.CharField(max_length=40) |
||||||
|
direccion_bodega=models.TextField(blank=True) |
||||||
|
estado= models.BooleanField(default=True) |
||||||
|
def __str__(self): |
||||||
|
return self.nombre_bodega |
||||||
|
|
||||||
|
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) |
||||||
|
proveedor = models.ForeignKey(proveedor, on_delete=models.CASCADE) |
||||||
|
tipo_articulo=models.ForeignKey(tipo_articulo, on_delete=models.CASCADE) |
||||||
|
def __str__(self): |
||||||
|
return self.nombre_articulo |
||||||
|
|
||||||
|
|
||||||
|
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 movimiento_inventario(models.Model): |
||||||
|
fecha=models.DateTimeField(null=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) |
||||||
|
|
||||||
@ -0,0 +1,105 @@ |
|||||||
|
<!DOCTYPE html> |
||||||
|
<html lang="en"> |
||||||
|
<head> |
||||||
|
<meta charset="UTF-8"> |
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
||||||
|
<title>Tienda la esquina</title> |
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css"> |
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css"> |
||||||
|
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script> |
||||||
|
</head> |
||||||
|
<body> |
||||||
|
<nav class="navbar bg-dark navbar-dark"> |
||||||
|
<div class="container-fluid "> |
||||||
|
<a class="navbar-brand " href="/">Tienda la esquina</a> |
||||||
|
<button class="navbar-toggler " type="button" data-bs-toggle="offcanvas" data-bs-target="#offcanvasNavbar" aria-controls="offcanvasNavbar" aria-label="Toggle navigation"> |
||||||
|
<span class="navbar-toggler-icon"></span> |
||||||
|
</button> |
||||||
|
<div class="offcanvas offcanvas-end bg-dark navbar-dark" tabindex="-1" id="offcanvasNavbar" aria-labelledby="offcanvasNavbarLabel"> |
||||||
|
<div class="offcanvas-header"> |
||||||
|
<h5 class="offcanvas-title"style="color:white;" id="offcanvasNavbarLabel">Bienvenido {{user.username}}</h5> |
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="offcanvas" aria-label="Close"></button> |
||||||
|
</div> |
||||||
|
<div class="offcanvas-body"> |
||||||
|
<ul class="navbar-nav justify-content-end flex-grow-1 pe-3"> |
||||||
|
<a style="color:white;"></a> |
||||||
|
<li class="nav-item"> |
||||||
|
<a class="nav-link active" aria-current="page" href="/">Home</a> |
||||||
|
</li> |
||||||
|
{% if user.is_authenticated %} |
||||||
|
<li class="nav-item dropdown "> |
||||||
|
<a class="nav-link dropdown-toggle " role="button" data-bs-toggle="dropdown" aria-expanded="false"> |
||||||
|
Gestores |
||||||
|
</a> |
||||||
|
<ul class="dropdown-menu dropdown-menu-dark"> |
||||||
|
<li> |
||||||
|
<p class="nav-link disabled text-center" aria-disabled="true">Gestor de proveedores</p> |
||||||
|
<hr class="dropdown-divider"> |
||||||
|
</li> |
||||||
|
<li><a class="dropdown-item" href="{% url 'proveedor' %}">Lista de proveedores</a></li> |
||||||
|
<li><a class="dropdown-item" href="{% url 'proveedor_registro' %}">Registro de proveedores</li> |
||||||
|
<li> |
||||||
|
<hr class="dropdown-divider"> |
||||||
|
<a class="nav-link disabled text-center" aria-disabled="true">Gestor de bodegas</a> |
||||||
|
<hr class="dropdown-divider"> |
||||||
|
</li> |
||||||
|
<li><a class="dropdown-item" href="{% url 'bodega_vista' %}">Lista de Bodegas</a></li> |
||||||
|
<li><a class="dropdown-item" href="{% url 'bodega_registro' %}">Registro de bodegas</li> |
||||||
|
<li> |
||||||
|
<hr class="dropdown-divider"> |
||||||
|
<a class="nav-link disabled text-center" aria-disabled="true">Gestor de tipos de articulo</a> |
||||||
|
<hr class="dropdown-divider"> |
||||||
|
</li> |
||||||
|
<li><a class="dropdown-item" href="{% url 'tipo_vista' %}">Lista de tipos de articulos</a></li> |
||||||
|
<li><a class="dropdown-item" href="{% url 'tipo_registro' %}">Registro de tipos de articulos</li> |
||||||
|
</ul> |
||||||
|
</li> |
||||||
|
<li> |
||||||
|
<hr class="dropdown-divider"> |
||||||
|
</li> |
||||||
|
<li class="nav-item"> |
||||||
|
<a class="nav-link" href="{% url 'articulo_vista' %}">Articulos</a> |
||||||
|
</li> |
||||||
|
<li> |
||||||
|
<hr class="dropdown-divider"> |
||||||
|
</li> |
||||||
|
<li class="nav-item"> |
||||||
|
<a class="nav-link" href="{% url 'inventario_vista' %}">inventario</a> |
||||||
|
</li> |
||||||
|
<li> |
||||||
|
<hr class="dropdown-divider"> |
||||||
|
</li> |
||||||
|
<li class="nav-item"> |
||||||
|
<a class="nav-link" href="{% url 'vista_inventario_mov' %}">movimiento de inventario</a> |
||||||
|
</li> |
||||||
|
<li> |
||||||
|
<hr class="dropdown-divider"> |
||||||
|
</li> |
||||||
|
<li class="nav-item"> |
||||||
|
<a class="nav-link" href="{% url 'logout' %}">logout</a> |
||||||
|
</li> |
||||||
|
<li> |
||||||
|
<hr class="dropdown-divider"> |
||||||
|
</li> |
||||||
|
{% else %} |
||||||
|
<li class="nav-item"> |
||||||
|
<a class="nav-link" href="{% url 'signin' %}">login</a> |
||||||
|
</li> |
||||||
|
{% endif %} |
||||||
|
|
||||||
|
</ul> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</nav> |
||||||
|
|
||||||
|
{% block content %} |
||||||
|
|
||||||
|
{% endblock %} |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script> |
||||||
|
|
||||||
|
</body> |
||||||
|
</html> |
||||||
@ -0,0 +1,51 @@ |
|||||||
|
{% extends "Base.html" %} |
||||||
|
|
||||||
|
{% block content %} |
||||||
|
|
||||||
|
<main class="conteiner"> |
||||||
|
<div class="row"> |
||||||
|
<div class="col-md-8 offset-md-2 mt-4"> |
||||||
|
<form action="/articulo/crear/" method="POST"> |
||||||
|
<div class="card"> |
||||||
|
<div class="card-header row col-12 justify-content-start mb-2 pr-0 mt-2"> |
||||||
|
<a class="btn btn-danger col-3" href="{% url 'articulo_vista' %}"><- volver</a> |
||||||
|
<h3 class="col-5">Registro articulo</h3> |
||||||
|
|
||||||
|
</div> |
||||||
|
<div class="card-body"> |
||||||
|
{% if error %} |
||||||
|
<div class="alert alert-danger" role="alert"> |
||||||
|
{{error}} |
||||||
|
<button type="button" class="close" data-dismiss="alert" aria-label="Close"> |
||||||
|
<span aria-hidden="true">×</span> |
||||||
|
</button> </div> |
||||||
|
{% endif %} |
||||||
|
{% if mensaje %} |
||||||
|
<div class="alert alert-success" role="alert"> |
||||||
|
Proveedor a sido guardado con exito |
||||||
|
<button type="button" class="close" data-dismiss="alert" aria-label="Close"> |
||||||
|
<span aria-hidden="true">×</span> |
||||||
|
</button> |
||||||
|
</div> |
||||||
|
{% endif %} |
||||||
|
{% csrf_token %} |
||||||
|
{{form.as_p}} |
||||||
|
|
||||||
|
<div class="d-grid gap-2 col-6 mx-auto mt-3"> |
||||||
|
<button class="btn btn-primary btn-lg ">Crear nuevo articulo</button> |
||||||
|
</div> |
||||||
|
|
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</form> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</main> |
||||||
|
|
||||||
|
<script> |
||||||
|
function permitirSoloNumeros(input) { |
||||||
|
input.value = input.value.replace(/[^0-9]/g, ''); |
||||||
|
} |
||||||
|
</script> |
||||||
|
|
||||||
|
{% endblock %} |
||||||
@ -0,0 +1,44 @@ |
|||||||
|
{% extends "Base.html" %} |
||||||
|
|
||||||
|
{% block content %} |
||||||
|
<main class="conteiner"> |
||||||
|
<div class="row"> |
||||||
|
<div class="col-md-10 offset-md-1 mt-4"> |
||||||
|
<form action="/articulo/" method="POST"> |
||||||
|
<div class="card-header row col-12 justify-content-end mb-2 pr-0 mt-2"> |
||||||
|
<h3 class="col-md-4">Vista articulo</h3> |
||||||
|
<a class="btn btn-success col-md-3" href="{% url 'articulo_registro' %}">Nuevo articulo</a> |
||||||
|
</div> |
||||||
|
<table class="table mt-3"> |
||||||
|
<thead> |
||||||
|
<tr> |
||||||
|
<th scope="col">#</th> |
||||||
|
<th scope="col">nombre del articulo</th> |
||||||
|
<th scope="col">medida</th> |
||||||
|
<th scope="col">descripcion</th> |
||||||
|
<th scope="col">tipo_articulo</th> |
||||||
|
<th scope="col">proveedors</th> |
||||||
|
<th scope="col">actualizar</th> |
||||||
|
|
||||||
|
</tr> |
||||||
|
</thead> |
||||||
|
<tbody> |
||||||
|
{% for articulo in persona %} |
||||||
|
<tr> |
||||||
|
<th scope="row">{{ articulo.id }}</th> |
||||||
|
<td> {{ articulo.nombre_articulo }} </td> |
||||||
|
<td> {{ articulo.medida }} </td> |
||||||
|
<td> {{ articulo.descripcion }} </td> |
||||||
|
<td> {{ articulo.tipo_articulo.tipo_articulo }} </td> |
||||||
|
<td> {{ articulo.proveedor.nombre_proveedor }} </td> |
||||||
|
<td> <a class="btn btn-warning" href="{% url 'articulo_update' articulo.id %}">Editar</a> </td> |
||||||
|
</tr> |
||||||
|
{% endfor %} |
||||||
|
</tbody> |
||||||
|
</table> |
||||||
|
</form> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</main> |
||||||
|
|
||||||
|
{% endblock %} |
||||||
@ -0,0 +1,29 @@ |
|||||||
|
{% extends "Base.html" %} |
||||||
|
|
||||||
|
{% block content %} |
||||||
|
<main class="conteiner"> |
||||||
|
<div class="row"> |
||||||
|
<div class="col-md-8 offset-md-2 mt-4"> |
||||||
|
<form method="POST"> |
||||||
|
<div class="card"> |
||||||
|
<div class="card-header row col-12 justify-content-start mb-2 pr-0 mt-2"> |
||||||
|
<a class="btn btn-danger col-3" href="{% url 'articulo_vista' %}"><- volver</a> |
||||||
|
<h3 class="col-5">Actualizar articulo</h3> |
||||||
|
|
||||||
|
</div> |
||||||
|
<div class="card-body"> |
||||||
|
{% csrf_token %} |
||||||
|
{{form.as_p}} |
||||||
|
|
||||||
|
<div class="d-grid gap-2 col-6 mx-auto mt-3"> |
||||||
|
<button class="btn btn-primary btn-lg ">actualizar datos de articulo</button> |
||||||
|
</div> |
||||||
|
|
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</form> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</main> |
||||||
|
|
||||||
|
{% endblock %} |
||||||
@ -0,0 +1,51 @@ |
|||||||
|
{% extends "Base.html" %} |
||||||
|
|
||||||
|
{% block content %} |
||||||
|
|
||||||
|
<main class="conteiner"> |
||||||
|
<div class="row"> |
||||||
|
<div class="col-md-8 offset-md-2 mt-4"> |
||||||
|
<form action="/bodega/crearbodega/" method="POST"> |
||||||
|
<div class="card"> |
||||||
|
<div class="card-header row col-12 justify-content-start mb-2 pr-0 mt-2"> |
||||||
|
<a class="btn btn-danger col-3" href="{% url 'bodega_vista' %}"> <- volver</a> |
||||||
|
<h3 class="col-5">Registro bodega</h3> |
||||||
|
</div> |
||||||
|
<div class="card-body"> |
||||||
|
{% if error %} |
||||||
|
<div class="alert alert-danger" role="alert"> |
||||||
|
{{error}} |
||||||
|
<button type="button" class="close" data-dismiss="alert" aria-label="Close"> |
||||||
|
<span aria-hidden="true">×</span> |
||||||
|
</button> </div> |
||||||
|
{% endif %} |
||||||
|
{% if mensaje %} |
||||||
|
<div class="alert alert-success" role="alert"> |
||||||
|
Proveedor a sido guardado con exito |
||||||
|
<button type="button" class="close" data-dismiss="alert" aria-label="Close"> |
||||||
|
<span aria-hidden="true">×</span> |
||||||
|
</button> |
||||||
|
</div> |
||||||
|
{% endif %} |
||||||
|
{% csrf_token %} |
||||||
|
{{form}} |
||||||
|
|
||||||
|
<div class="d-grid gap-2 col-6 mx-auto mt-3"> |
||||||
|
<button class="btn btn-primary btn-lg ">Crear nueva bodega</button> |
||||||
|
</div> |
||||||
|
|
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</form> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</main> |
||||||
|
|
||||||
|
<script> |
||||||
|
function permitirSoloNumeros(input) { |
||||||
|
input.value = input.value.replace(/[^0-9]/g, ''); |
||||||
|
} |
||||||
|
</script> |
||||||
|
|
||||||
|
|
||||||
|
{% endblock %} |
||||||
@ -0,0 +1,60 @@ |
|||||||
|
{% extends "Base.html" %} |
||||||
|
|
||||||
|
{% block content %} |
||||||
|
<main class="conteiner"> |
||||||
|
<div class="row"> |
||||||
|
<div class="col-md-10 offset-md-1 mt-4"> |
||||||
|
<form action="/bodega/" method="POST"> |
||||||
|
{% if error %} |
||||||
|
<div class="alert alert-danger" role="alert"> |
||||||
|
{{error}} |
||||||
|
<button type="button" class="close" data-dismiss="alert" aria-label="Close"> |
||||||
|
<span aria-hidden="true">×</span> |
||||||
|
</button> </div> |
||||||
|
{% endif %} |
||||||
|
{% if mensaje %} |
||||||
|
<div class="alert alert-success" role="alert"> |
||||||
|
Proveedor a sido guardado con exito |
||||||
|
<button type="button" class="close" data-dismiss="alert" aria-label="Close"> |
||||||
|
<span aria-hidden="true">×</span> |
||||||
|
</button> |
||||||
|
</div> |
||||||
|
{% endif %} |
||||||
|
<div class="card-header row col-12 justify-content-end mb-2 pr-0 mt-2"> |
||||||
|
<h3 class="col-md-4">Vista Bodega</h3> |
||||||
|
<a class="btn btn-success col-md-3" href="{% url 'bodega_registro' %}">Nueva bodega</a> |
||||||
|
</div> |
||||||
|
<table class="table mt-3"> |
||||||
|
<thead> |
||||||
|
<tr> |
||||||
|
<th scope="col">#</th> |
||||||
|
<th scope="col">codigo de la bodega</th> |
||||||
|
<th scope="col">nombre del supervisor</th> |
||||||
|
<th scope="col">estado</th> |
||||||
|
<th scope="col">Direccion</th> |
||||||
|
<th scope="col">Editar</th> |
||||||
|
</tr> |
||||||
|
</thead> |
||||||
|
<tbody> |
||||||
|
{% for bodega in persona %} |
||||||
|
<tr> |
||||||
|
<th scope="row"> {{bodega.id}} </th> |
||||||
|
<td> {{bodega.codigo_bodega}} </td> |
||||||
|
<td> {{bodega.nombre_bodega}} </td> |
||||||
|
{% if bodega.estado %} |
||||||
|
<td> activo </td> |
||||||
|
{% else %} |
||||||
|
<td> inactivo </td> |
||||||
|
{% endif %} |
||||||
|
<td> {{bodega.direccion_bodega}} </td> |
||||||
|
<td> <a class="btn btn-warning" href="{% url 'bodega_update' bodega.id %}">Editar</a> </td> |
||||||
|
</tr> |
||||||
|
{% endfor %} |
||||||
|
</tbody> |
||||||
|
</table> |
||||||
|
</form> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</main> |
||||||
|
|
||||||
|
{% endblock %} |
||||||
@ -0,0 +1,43 @@ |
|||||||
|
{% extends "Base.html" %} |
||||||
|
|
||||||
|
{% block content %} |
||||||
|
<main class="conteiner"> |
||||||
|
<div class="row"> |
||||||
|
<div class="col-md-8 offset-md-2 mt-4"> |
||||||
|
<form method="POST"> |
||||||
|
<div class="card"> |
||||||
|
<div class="card-header row col-12 justify-content-start mb-2 pr-0 mt-2"> |
||||||
|
<a class="btn btn-danger col-3" href="{% url 'bodega_vista' %}"><- volver</a> |
||||||
|
<h3 class="col-5">Actualizar bodega</h3> |
||||||
|
</div> |
||||||
|
<div class="card-body"> |
||||||
|
{% if error %} |
||||||
|
<div class="alert alert-danger" role="alert"> |
||||||
|
{{error}} |
||||||
|
<button type="button" class="close" data-dismiss="alert" aria-label="Close"> |
||||||
|
<span aria-hidden="true">×</span> |
||||||
|
</button> |
||||||
|
</div> |
||||||
|
{% endif %} |
||||||
|
{% if mensaje %} |
||||||
|
<div class="alert alert-success" role="alert"> |
||||||
|
bodega a sido actualizado con exito |
||||||
|
<button type="button" class="close" data-dismiss="alert" aria-label="Close"> |
||||||
|
<span aria-hidden="true">×</span> |
||||||
|
</button> |
||||||
|
</div> |
||||||
|
{% endif %} |
||||||
|
{% csrf_token %} |
||||||
|
{{form}} |
||||||
|
|
||||||
|
<div class="d-grid gap-2 col-6 mx-auto mt-3"> |
||||||
|
<button class="btn btn-primary btn-lg ">actualizar datos de proveedor</button> |
||||||
|
</div> |
||||||
|
|
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</form> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</main> |
||||||
|
{% endblock %} |
||||||
@ -0,0 +1,36 @@ |
|||||||
|
<!DOCTYPE html> |
||||||
|
<html> |
||||||
|
<head> |
||||||
|
<title>Gráfico Interactivo</title> |
||||||
|
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script> |
||||||
|
</head> |
||||||
|
<body> |
||||||
|
<h1>Gráfico de cantidad de productos</h1> |
||||||
|
<canvas id="grafico" width="400" height="200"></canvas> |
||||||
|
<script> |
||||||
|
const ctx = document.getElementById('grafico').getContext('2d'); |
||||||
|
const myChart = new Chart(ctx, { |
||||||
|
type: 'bar', |
||||||
|
data: { |
||||||
|
labels: {{ productos|safe }}, // Nombres de los productos |
||||||
|
datasets: [{ |
||||||
|
label: 'Cantidad de Productos', |
||||||
|
data: {{ cantidades|safe }}, // Cantidades de productos |
||||||
|
backgroundColor: 'rgba(75, 192, 192, 0.2)', |
||||||
|
borderColor: 'rgba(75, 192, 192, 1)', |
||||||
|
borderWidth: 1 |
||||||
|
}] |
||||||
|
}, |
||||||
|
options: { |
||||||
|
scales: { |
||||||
|
y: { |
||||||
|
beginAtZero: true |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
}); |
||||||
|
</script> |
||||||
|
<a href="{% url 'inventario_vista' %}" class="btn btn-secondary">volver</a> |
||||||
|
|
||||||
|
</body> |
||||||
|
</html> |
||||||
@ -0,0 +1,181 @@ |
|||||||
|
{% extends "Base.html" %} |
||||||
|
|
||||||
|
{% block content %} |
||||||
|
{% if user.is_authenticated %} |
||||||
|
<main class="conteiner col-md-10 offset-md-1 mt-5 g-4"> |
||||||
|
<div class="row row-cols-1 row-cols-md-2 g-4"> |
||||||
|
<div class="col"> |
||||||
|
<div class="card h-100"> |
||||||
|
<div class="card-body"> |
||||||
|
<p class="text-center">Precio promedio de cada tipo articulo</p> |
||||||
|
<div class="chart-container"> |
||||||
|
<canvas id="avgPriceChart"></canvas> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
<div class="col"> |
||||||
|
<div class="card h-100"> |
||||||
|
<div class="card-body"> |
||||||
|
<p class="text-center">articulos en bodegas</p> |
||||||
|
<div class="chart-container"> |
||||||
|
<canvas id="barChart"></canvas> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
<div class="col"> |
||||||
|
<div class="card h-100"> |
||||||
|
<div class="card-body"> |
||||||
|
<p class="text-center">Cantidad de tipos de articulos</p> |
||||||
|
<div class="chart-container"> |
||||||
|
<canvas id="graficoLinealMovimientos"></canvas> |
||||||
|
</div> |
||||||
|
<br> |
||||||
|
<div class="text-center"> |
||||||
|
<p class="text-center">Reportes del inventario</p> |
||||||
|
<a href="{% url 'generar_reporte_pdf' %}" class="btn btn-primary">Descargar PDF</a> |
||||||
|
<a href="{% url 'generar_reporte_excel' %}" class="btn btn-success">Descargar Excel</a> |
||||||
|
<a href="{% url 'generar_reporte_csv' %}" class="btn btn-info">Descargar CSV</a> |
||||||
|
</div> |
||||||
|
<br> |
||||||
|
<p class="text-center">Grafico de productos</p> |
||||||
|
<div class="text-center"> |
||||||
|
<a href="{% url 'generar_grafico_chartjs' %}" class="btn btn-secondary">generar grafico de produccion</a> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
<div class="col"> |
||||||
|
<div class="card h-100"> |
||||||
|
<div class="card-body"> |
||||||
|
<p class="text-center">Cantidad de tipos de articulos</p> |
||||||
|
<div class="chart-container"> |
||||||
|
<canvas id="pieChart"></canvas> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
|
||||||
|
|
||||||
|
</main> |
||||||
|
<script> |
||||||
|
// Datos dinámicos generados desde Django |
||||||
|
const tiposArticulo = {{ tipos_articulo | safe }}; |
||||||
|
const cantidadesPorTipo = {{ cantidades_por_tipo | safe }}; |
||||||
|
const bodegas = {{ bodegas | safe }}; |
||||||
|
const cantidadesPorBodega = {{ cantidades_por_bodega | safe }}; |
||||||
|
const tiposArticuloProm = {{ tipos_articulo_prom | safe }}; |
||||||
|
const preciosPromedio = {{ precios_promedio | safe }}; |
||||||
|
|
||||||
|
|
||||||
|
// 1. Gráfico de Pastel: Distribución de artículos por tipo |
||||||
|
new Chart(document.getElementById('pieChart').getContext('2d'), { |
||||||
|
type: 'pie', |
||||||
|
data: { |
||||||
|
labels: tiposArticulo, |
||||||
|
datasets: [{ |
||||||
|
data: cantidadesPorTipo, |
||||||
|
backgroundColor: ['rgba(255, 99, 132, 0.6)', 'rgba(54, 162, 235, 0.6)', 'rgba(255, 206, 86, 0.6)', 'rgba(75, 192, 192, 0.6)'], |
||||||
|
}] |
||||||
|
}, |
||||||
|
options: { |
||||||
|
responsive: true, |
||||||
|
plugins: { |
||||||
|
legend: { position: 'top' }, |
||||||
|
} |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
// 2. Gráfico de Barras: Cantidad de artículos por bodega |
||||||
|
new Chart(document.getElementById('barChart').getContext('2d'), { |
||||||
|
type: 'bar', |
||||||
|
data: { |
||||||
|
labels: bodegas, |
||||||
|
datasets: [{ |
||||||
|
label: 'Cantidad de Artículos', |
||||||
|
data: cantidadesPorBodega, |
||||||
|
backgroundColor: 'rgba(75, 192, 192, 0.6)', |
||||||
|
}] |
||||||
|
}, |
||||||
|
options: { |
||||||
|
responsive: true, |
||||||
|
plugins: { |
||||||
|
legend: { display: false }, |
||||||
|
}, |
||||||
|
scales: { |
||||||
|
x: { title: { display: true, text: 'Bodegas' } }, |
||||||
|
y: { title: { display: true, text: 'Cantidad' } }, |
||||||
|
} |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
// 3. Gráfico de Línea: Evolución de movimientos de inventario |
||||||
|
|
||||||
|
new Chart(document.getElementById('avgPriceChart').getContext('2d'), { |
||||||
|
type: 'bar', |
||||||
|
data: { |
||||||
|
labels: tiposArticuloProm, |
||||||
|
datasets: [{ |
||||||
|
label: 'Precio Promedio', |
||||||
|
data: preciosPromedio, |
||||||
|
backgroundColor: 'rgba(255, 159, 64, 0.6)', |
||||||
|
}] |
||||||
|
}, |
||||||
|
options: { |
||||||
|
responsive: true, |
||||||
|
plugins: { |
||||||
|
legend: { position: 'top' }, |
||||||
|
}, |
||||||
|
scales: { |
||||||
|
x: { title: { display: true, text: 'Tipos de Artículos' } }, |
||||||
|
y: { title: { display: true, text: 'Precio Promedio' } }, |
||||||
|
} |
||||||
|
} |
||||||
|
}); |
||||||
|
const ctx = document.getElementById('graficoLinealMovimientos').getContext('2d'); |
||||||
|
const graficoLineal = new Chart(ctx, { |
||||||
|
type: 'line', |
||||||
|
data: { |
||||||
|
labels: {{ nombres_tipos|safe }}, // Nombres de los tipos de movimientos |
||||||
|
datasets: [{ |
||||||
|
label: 'Cantidad Total Movida', |
||||||
|
data: {{ cantidades_por_tipos|safe }}, // Cantidades totales por tipo de movimiento |
||||||
|
borderColor: 'rgba(54, 162, 235, 1)', |
||||||
|
backgroundColor: 'rgba(54, 162, 235, 0.2)', |
||||||
|
borderWidth: 2, |
||||||
|
tension: 0.3, // Para suavizar la línea |
||||||
|
}] |
||||||
|
}, |
||||||
|
options: { |
||||||
|
responsive: true, |
||||||
|
plugins: { |
||||||
|
legend: { |
||||||
|
display: true, |
||||||
|
}, |
||||||
|
tooltip: { |
||||||
|
enabled: true, |
||||||
|
} |
||||||
|
}, |
||||||
|
scales: { |
||||||
|
x: { |
||||||
|
title: { |
||||||
|
display: true, |
||||||
|
text: 'Tipos de Movimiento' |
||||||
|
} |
||||||
|
}, |
||||||
|
y: { |
||||||
|
beginAtZero: true, |
||||||
|
title: { |
||||||
|
display: true, |
||||||
|
text: 'Cantidad Total' |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
}); |
||||||
|
</script> |
||||||
|
{% else %} |
||||||
|
{% endif %} |
||||||
|
|
||||||
|
{% endblock %} |
||||||
@ -0,0 +1,52 @@ |
|||||||
|
{% extends "Base.html" %} |
||||||
|
|
||||||
|
{% block content %} |
||||||
|
|
||||||
|
<main class="conteiner"> |
||||||
|
<div class="row"> |
||||||
|
<div class="col-md-8 offset-md-2 mt-4"> |
||||||
|
<form action="/inventariomov/crear/" method="POST"> |
||||||
|
<div class="card"> |
||||||
|
<div class="card-header row col-12 justify-content-start mb-2 pr-0 mt-2"> |
||||||
|
<a class="btn btn-danger col-3" href="{% url 'vista_inventario_mov' %}"><- volver</a> |
||||||
|
<h3 class="col-5">Registro inventario</h3> |
||||||
|
|
||||||
|
</div> |
||||||
|
<div class="card-body"> |
||||||
|
{% if error %} |
||||||
|
<div class="alert alert-danger" role="alert"> |
||||||
|
{{error}} |
||||||
|
<button type="button" class="close" data-dismiss="alert" aria-label="Close"> |
||||||
|
<span aria-hidden="true">×</span> |
||||||
|
</button> |
||||||
|
</div> |
||||||
|
{% endif %} |
||||||
|
{% if mensaje %} |
||||||
|
<div class="alert alert-success" role="alert"> |
||||||
|
inventario a sido creado con exito |
||||||
|
<button type="button" class="close" data-dismiss="alert" aria-label="Close"> |
||||||
|
<span aria-hidden="true">×</span> |
||||||
|
</button> |
||||||
|
</div> |
||||||
|
{% endif %} |
||||||
|
{% csrf_token %} |
||||||
|
{{form.as_p}} |
||||||
|
|
||||||
|
<div class="d-grid gap-2 col-6 mx-auto mt-3"> |
||||||
|
<button class="btn btn-primary btn-lg ">Crear nuevo inventario</button> |
||||||
|
</div> |
||||||
|
|
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</form> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</main> |
||||||
|
|
||||||
|
<script> |
||||||
|
function permitirSoloNumeros(input) { |
||||||
|
input.value = input.value.replace(/[^0-9]/g, ''); |
||||||
|
} |
||||||
|
</script> |
||||||
|
|
||||||
|
{% endblock %} |
||||||
@ -0,0 +1,45 @@ |
|||||||
|
{% extends "Base.html" %} |
||||||
|
|
||||||
|
{% block content %} |
||||||
|
<main class="conteiner"> |
||||||
|
<div class="row"> |
||||||
|
<div class="col-md-10 offset-md-1 mt-4"> |
||||||
|
<form action="/inventario/" method="POST"> |
||||||
|
<div class="card-header row col-12 justify-content-end mb-2 pr-0 mt-2"> |
||||||
|
<h3 class="col-md-4">Vista inventario</h3> |
||||||
|
</div> |
||||||
|
<a href="{% url 'generar_reporte_pdf' %}" class="btn btn-primary">Descargar PDF</a> |
||||||
|
<a href="{% url 'generar_reporte_excel' %}" class="btn btn-success">Descargar Excel</a> |
||||||
|
<a href="{% url 'generar_reporte_csv' %}" class="btn btn-info">Descargar CSV</a> |
||||||
|
<a href="{% url 'generar_grafico_chartjs' %}" class="btn btn-secondary">grafico</a> |
||||||
|
<br> |
||||||
|
<table class="table mt-3"> |
||||||
|
<thead> |
||||||
|
<tr> |
||||||
|
<th scope="col">#</th> |
||||||
|
<th scope="col">articulo</th> |
||||||
|
<th scope="col">Descripcion de articulo</th> |
||||||
|
<th scope="col">bodega</th> |
||||||
|
<th scope="col">cantidad</th> |
||||||
|
<th scope="col">precio</th> |
||||||
|
</tr> |
||||||
|
</thead> |
||||||
|
<tbody> |
||||||
|
{% for inventario in persona %} |
||||||
|
<tr> |
||||||
|
<th scope="row">{{ inventario.id }}</th> |
||||||
|
<td> {{ inventario.articulo.nombre_articulo }} {{inventario.articulo.medida}} </td> |
||||||
|
<td> {{ inventario.articulo.descripcion }} </td> |
||||||
|
<td> {{ inventario.bodega.nombre_bodega }} </td> |
||||||
|
<td> {{ inventario.cantidad }} </td> |
||||||
|
<td> {{ inventario.precio }} </td> |
||||||
|
</tr> |
||||||
|
{% endfor %} |
||||||
|
</tbody> |
||||||
|
</table> |
||||||
|
</form> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</main> |
||||||
|
|
||||||
|
{% endblock %} |
||||||
@ -0,0 +1,41 @@ |
|||||||
|
{% extends "Base.html" %} |
||||||
|
|
||||||
|
{% block content %} |
||||||
|
<main class="conteiner"> |
||||||
|
<div class="row"> |
||||||
|
<div class="col-md-10 offset-md-1 mt-4"> |
||||||
|
<form action="/inventariomov/" method="POST"> |
||||||
|
<div class="card-header row col-12 justify-content-end mb-2 pr-0 mt-2"> |
||||||
|
<h3 class="col-md-4">Vista inventario</h3> |
||||||
|
<a class="btn btn-success col-md-3" href="{% url 'registrar_movimiento' %}"> Nuevo movimiento de inventario</a> |
||||||
|
</div> |
||||||
|
<table class="table mt-3"> |
||||||
|
<thead> |
||||||
|
<tr> |
||||||
|
<th scope="col">#</th> |
||||||
|
<th scope="col">bodega</th> |
||||||
|
<th scope="col">articulo</th> |
||||||
|
<th scope="col">cantidad</th> |
||||||
|
<th scope="col">precio</th> |
||||||
|
<th scope="col">tipo de movimiento</th> |
||||||
|
</tr> |
||||||
|
</thead> |
||||||
|
<tbody> |
||||||
|
{% for movimiento_inventario in persona %} |
||||||
|
<tr> |
||||||
|
<th scope="row">{{ movimiento_inventario.id }}</th> |
||||||
|
<td> {{ movimiento_inventario.articulo.nombre_articulo }} {{movimiento_inventario.articulo.medida}} </td> |
||||||
|
<td> {{ movimiento_inventario.bodega.nombre_bodega }} </td> |
||||||
|
<td> {{ movimiento_inventario.cantidad }} </td> |
||||||
|
<td> {{ movimiento_inventario.precio }} </td> |
||||||
|
<td> {{ movimiento_inventario.tipo_inventario }} </td> |
||||||
|
</tr> |
||||||
|
{% endfor %} |
||||||
|
</tbody> |
||||||
|
</table> |
||||||
|
</form> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</main> |
||||||
|
|
||||||
|
{% endblock %} |
||||||
@ -0,0 +1,36 @@ |
|||||||
|
<!DOCTYPE html> |
||||||
|
<html> |
||||||
|
<head> |
||||||
|
<style> |
||||||
|
body { font-family: Arial, sans-serif; } |
||||||
|
table { width: 100%; border-collapse: collapse; margin-top: 20px; } |
||||||
|
th, td { border: 1px solid #000; padding: 8px; text-align: left; } |
||||||
|
th { background-color: #f2f2f2; } |
||||||
|
</style> |
||||||
|
</head> |
||||||
|
<body> |
||||||
|
<h1>Reporte de Inventario</h1> |
||||||
|
<table> |
||||||
|
<thead> |
||||||
|
<tr> |
||||||
|
<th>ID</th> |
||||||
|
<th>Bodega</th> |
||||||
|
<th>Artículo</th> |
||||||
|
<th>Cantidad</th> |
||||||
|
<th>Precio</th> |
||||||
|
</tr> |
||||||
|
</thead> |
||||||
|
<tbody> |
||||||
|
{% for item in inventarios %} |
||||||
|
<tr> |
||||||
|
<td>{{ item.id }}</td> |
||||||
|
<td>{{ item.bodega.nombre_bodega }}</td> |
||||||
|
<td>{{ item.articulo.nombre_articulo }}</td> |
||||||
|
<td>{{ item.cantidad }}</td> |
||||||
|
<td>{{ item.precio }}</td> |
||||||
|
</tr> |
||||||
|
{% endfor %} |
||||||
|
</tbody> |
||||||
|
</table> |
||||||
|
</body> |
||||||
|
</html> |
||||||
@ -0,0 +1,56 @@ |
|||||||
|
|
||||||
|
{% extends "Base.html" %} |
||||||
|
|
||||||
|
{% block content %} |
||||||
|
|
||||||
|
<main class="conteiner"> |
||||||
|
<div class="row"> |
||||||
|
<div class="col-md-8 offset-md-2 mt-4"> |
||||||
|
<form action="/proveedor/crear/" method="POST"> |
||||||
|
<div class="card"> |
||||||
|
<div class="card-header row col-12 justify-content-start mb-2 pr-0 mt-2"> |
||||||
|
<a class="btn btn-danger col-3" href="{% url 'proveedor' %}"><- volver</a> |
||||||
|
<h3 class="col-5">Registro Proveedor</h3> |
||||||
|
|
||||||
|
</div> |
||||||
|
<div class="card-body"> |
||||||
|
{% if error %} |
||||||
|
<div class="alert alert-danger" role="alert"> |
||||||
|
{{error}} |
||||||
|
<button type="button" class="close" data-dismiss="alert" aria-label="Close"> |
||||||
|
<span aria-hidden="true">×</span> |
||||||
|
</button> </div> |
||||||
|
{% endif %} |
||||||
|
{% if mensaje %} |
||||||
|
<div class="alert alert-success" role="alert"> |
||||||
|
Proveedor a sido guardado con exito |
||||||
|
<button type="button" class="close" data-dismiss="alert" aria-label="Close"> |
||||||
|
<span aria-hidden="true">×</span> |
||||||
|
</button> |
||||||
|
</div> |
||||||
|
{% endif %} |
||||||
|
{% csrf_token %} |
||||||
|
{{form}} |
||||||
|
|
||||||
|
<div class="d-grid gap-2 col-6 mx-auto mt-3"> |
||||||
|
<button class="btn btn-primary btn-lg ">Crear nuevo proveedor</button> |
||||||
|
</div> |
||||||
|
|
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</form> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</main> |
||||||
|
<script> |
||||||
|
function formatearInput(input) { |
||||||
|
// Remover caracteres no numéricos |
||||||
|
let valor = input.value.replace(/[^0-9]/g, ''); |
||||||
|
// Insertar el guion después de los primeros 4 dígitos |
||||||
|
if (valor.length > 4) { |
||||||
|
valor = valor.slice(0, 4) + '-' + valor.slice(4); |
||||||
|
} |
||||||
|
input.value = valor; |
||||||
|
} |
||||||
|
</script> |
||||||
|
{% endblock %} |
||||||
@ -0,0 +1,56 @@ |
|||||||
|
{% extends "Base.html" %} |
||||||
|
|
||||||
|
{% block content %} |
||||||
|
<main class="conteiner"> |
||||||
|
<div class="row"> |
||||||
|
<div class="col-md-10 offset-md-1 mt-4"> |
||||||
|
<form action="/proveedor/" method="POST"> |
||||||
|
{% if error %} |
||||||
|
<div class="alert alert-danger" role="alert"> |
||||||
|
{{error}} |
||||||
|
<button type="button" class="close" data-dismiss="alert" aria-label="Close"> |
||||||
|
<span aria-hidden="true">×</span> |
||||||
|
</button> </div> |
||||||
|
{% endif %} |
||||||
|
{% if mensaje %} |
||||||
|
<div class="alert alert-success" role="alert"> |
||||||
|
Proveedor a sido guardado con exito |
||||||
|
<button type="button" class="close" data-dismiss="alert" aria-label="Close"> |
||||||
|
<span aria-hidden="true">×</span> |
||||||
|
</button> |
||||||
|
</div> |
||||||
|
{% endif %} |
||||||
|
<div class="card-header row col-12 justify-content-end mb-2 pr-0 mt-2"> |
||||||
|
<h3 class="col-md-4">Proveedores</h3> |
||||||
|
<a class="btn btn-success col-md-3" href="{% url 'proveedor_registro' %}">Nuevo proveedor</a> |
||||||
|
</div> |
||||||
|
<table class="table mt-3"> |
||||||
|
<thead> |
||||||
|
<tr> |
||||||
|
<th scope="col">#</th> |
||||||
|
<th scope="col">Nombre</th> |
||||||
|
<th scope="col">Email</th> |
||||||
|
<th scope="col">Telefono</th> |
||||||
|
<th scope="col">Direccion</th> |
||||||
|
<th scope="col">Editar</th> |
||||||
|
</tr> |
||||||
|
</thead> |
||||||
|
<tbody> |
||||||
|
{% for proveedor in persona %} |
||||||
|
<tr> |
||||||
|
<th scope="row"> {{proveedor.id}} </th> |
||||||
|
<td> {{proveedor.nombre_proveedor}} </td> |
||||||
|
<td> {{proveedor.email}} </td> |
||||||
|
<td> {{proveedor.telefono}} </td> |
||||||
|
<td> {{proveedor.direccion}} </td> |
||||||
|
<td> <a class="btn btn-warning" href="{% url 'Proveedor_update' proveedor.id %}">Editar</a> </td> |
||||||
|
</tr> |
||||||
|
{% endfor %} |
||||||
|
</tbody> |
||||||
|
</table> |
||||||
|
</form> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</main> |
||||||
|
|
||||||
|
{% endblock %} |
||||||
@ -0,0 +1,43 @@ |
|||||||
|
{% extends "Base.html" %} |
||||||
|
|
||||||
|
{% block content %} |
||||||
|
<main class="conteiner"> |
||||||
|
<div class="row"> |
||||||
|
<div class="col-md-8 offset-md-2 mt-4"> |
||||||
|
<form method="POST"> |
||||||
|
<div class="card"> |
||||||
|
<div class="card-header row col-12 justify-content-start mb-2 pr-0 mt-2"> |
||||||
|
<a class="btn btn-danger col-3" href="{% url 'proveedor' %}"><- volver</a> |
||||||
|
<h3 class="col-5">Actualizar Proveedor</h3> |
||||||
|
|
||||||
|
</div> |
||||||
|
<div class="card-body"> |
||||||
|
{% if error %} |
||||||
|
<div class="alert alert-danger" role="alert"> |
||||||
|
{{error}} |
||||||
|
<button type="button" class="close" data-dismiss="alert" aria-label="Close"> |
||||||
|
<span aria-hidden="true">×</span> |
||||||
|
</button> </div> |
||||||
|
{% endif %} |
||||||
|
{% if mensaje %} |
||||||
|
<div class="alert alert-success" role="alert"> |
||||||
|
Proveedor a sido actualizado con exito |
||||||
|
<button type="button" class="close" data-dismiss="alert" aria-label="Close"> |
||||||
|
<span aria-hidden="true">×</span> |
||||||
|
</button> |
||||||
|
</div> |
||||||
|
{% endif %} |
||||||
|
{% csrf_token %} |
||||||
|
{{form}} |
||||||
|
|
||||||
|
<div class="d-grid gap-2 col-6 mx-auto mt-3"> |
||||||
|
<button class="btn btn-primary btn-lg ">actualizar datos de proveedor</button> |
||||||
|
</div> |
||||||
|
|
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</form> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</main> |
||||||
|
{% endblock %} |
||||||
@ -0,0 +1,28 @@ |
|||||||
|
{% extends "Base.html" %} |
||||||
|
|
||||||
|
{% block content %} |
||||||
|
|
||||||
|
<main class="conteiner"> |
||||||
|
<div class="row"> |
||||||
|
<div class="col-md-4 offset-md-4 mt-5"> |
||||||
|
<form action="/signin/" method="POST" class="card card-body"> |
||||||
|
<h1 class="text-center">Login</h1> |
||||||
|
{% csrf_token %} |
||||||
|
<div class="mb-3"> |
||||||
|
<label for="username"> Usuario:</label> |
||||||
|
<input type="text" name="username" id="username" |
||||||
|
class="form-control" placeholder="Escribe tu usuario"> |
||||||
|
</div> |
||||||
|
<div class="mb-3"> |
||||||
|
<label for="password"> Contraseña:</label> |
||||||
|
<input type="password" name="password" id="password" |
||||||
|
class="form-control" placeholder="escribe tu contraseña"> |
||||||
|
</div> |
||||||
|
<button class="btn btn-primary"> Signin </button> |
||||||
|
</form> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</main> |
||||||
|
|
||||||
|
|
||||||
|
{% endblock %} |
||||||
@ -0,0 +1,44 @@ |
|||||||
|
{% extends "Base.html" %} |
||||||
|
|
||||||
|
{% block content %} |
||||||
|
|
||||||
|
<main class="conteiner"> |
||||||
|
<div class="row"> |
||||||
|
<div class="col-md-8 offset-md-2 mt-4"> |
||||||
|
<form action="/tipo/creartipo_articulo/" method="POST"> |
||||||
|
<div class="card"> |
||||||
|
<div class="card-header row col-12 justify-content-start mb-2 pr-0 mt-2"> |
||||||
|
<a class="btn btn-danger col-3" href="{% url 'tipo_vista' %}"><- volver</a> |
||||||
|
<h3 class="col-5">Registro tipo articulo</h3> |
||||||
|
|
||||||
|
</div> |
||||||
|
<div class="card-body"> |
||||||
|
{% if error %} |
||||||
|
<div class="alert alert-danger" role="alert"> |
||||||
|
{{error}} |
||||||
|
<button type="button" class="close" data-dismiss="alert" aria-label="Close"> |
||||||
|
<span aria-hidden="true">×</span> |
||||||
|
</button> </div> |
||||||
|
{% endif %} |
||||||
|
{% if mensaje %} |
||||||
|
<div class="alert alert-success" role="alert"> |
||||||
|
el tipo de articulo a sido guardado con exito |
||||||
|
<button type="button" class="close" data-dismiss="alert" aria-label="Close"> |
||||||
|
<span aria-hidden="true">×</span> |
||||||
|
</button> |
||||||
|
</div> |
||||||
|
{% endif %} |
||||||
|
{% csrf_token %} |
||||||
|
{{form}} |
||||||
|
|
||||||
|
<div class="d-grid gap-2 col-6 mx-auto mt-3"> |
||||||
|
<button class="btn btn-primary btn-lg ">Crear nuevo tipo de articulo</button> |
||||||
|
</div> |
||||||
|
|
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</form> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</main> |
||||||
|
{% endblock %} |
||||||
@ -0,0 +1,51 @@ |
|||||||
|
{% extends "Base.html" %} |
||||||
|
|
||||||
|
{% block content %} |
||||||
|
<main class="conteiner"> |
||||||
|
<div class="row"> |
||||||
|
<div class="col-md-10 offset-md-1 mt-4"> |
||||||
|
<form action="/tipo/" method="POST"> |
||||||
|
{% if error %} |
||||||
|
<div class="alert alert-danger" role="alert"> |
||||||
|
{{error}} |
||||||
|
<button type="button" class="close" data-dismiss="alert" aria-label="Close"> |
||||||
|
<span aria-hidden="true">×</span> |
||||||
|
</button> </div> |
||||||
|
{% endif %} |
||||||
|
{% if mensaje %} |
||||||
|
<div class="alert alert-success" role="alert"> |
||||||
|
Proveedor a sido guardado con exito |
||||||
|
<button type="button" class="close" data-dismiss="alert" aria-label="Close"> |
||||||
|
<span aria-hidden="true">×</span> |
||||||
|
</button> |
||||||
|
</div> |
||||||
|
{% endif %} |
||||||
|
<div class="card-header row col-12 justify-content-end mb-2 pr-0 mt-2"> |
||||||
|
<h3 class="col-md-4">Tipos de articulos</h3> |
||||||
|
<a class="btn btn-success col-md-3" href="{% url 'tipo_registro' %}">Nuevo tipo de articulo</a> |
||||||
|
</div> |
||||||
|
<table class="table mt-3"> |
||||||
|
<thead> |
||||||
|
<tr> |
||||||
|
<th scope="col">#</th> |
||||||
|
<th scope="col">tipo de articulo</th> |
||||||
|
<th scope="col">Descripcion</th> |
||||||
|
<th scope="col">Actualizar</th> |
||||||
|
</tr> |
||||||
|
</thead> |
||||||
|
<tbody> |
||||||
|
{% for tipo_articulo in persona %} |
||||||
|
<tr> |
||||||
|
<th scope="row"> {{tipo_articulo.id}} </th> |
||||||
|
<td> {{tipo_articulo.tipo_articulo}} </td> |
||||||
|
<td> {{tipo_articulo.descripcion}} </td> |
||||||
|
<td> <a class="btn btn-warning" href="{% url 'tipo_update' tipo_articulo.id %}">Editar</a> </td> </tr> |
||||||
|
{% endfor %} |
||||||
|
</tbody> |
||||||
|
</table> |
||||||
|
</form> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</main> |
||||||
|
|
||||||
|
{% endblock %} |
||||||
@ -0,0 +1,43 @@ |
|||||||
|
{% extends "Base.html" %} |
||||||
|
|
||||||
|
{% block content %} |
||||||
|
<main class="conteiner"> |
||||||
|
<div class="row"> |
||||||
|
<div class="col-md-8 offset-md-2 mt-4"> |
||||||
|
<form method="POST"> |
||||||
|
<div class="card"> |
||||||
|
<div class="card-header row col-12 justify-content-start mb-2 pr-0 mt-2"> |
||||||
|
<a class="btn btn-danger col-3" href="{% url 'tipo_vista' %}"><- volver</a> |
||||||
|
<h3 class="col-5">Actualizar tipo de articulo</h3> |
||||||
|
|
||||||
|
</div> |
||||||
|
<div class="card-body"> |
||||||
|
{% if error %} |
||||||
|
<div class="alert alert-danger" role="alert"> |
||||||
|
{{error}} |
||||||
|
<button type="button" class="close" data-dismiss="alert" aria-label="Close"> |
||||||
|
<span aria-hidden="true">×</span> |
||||||
|
</button> </div> |
||||||
|
{% endif %} |
||||||
|
{% if mensaje %} |
||||||
|
<div class="alert alert-success" role="alert"> |
||||||
|
tipo de articulo a sido actualizado con exito |
||||||
|
<button type="button" class="close" data-dismiss="alert" aria-label="Close"> |
||||||
|
<span aria-hidden="true">×</span> |
||||||
|
</button> |
||||||
|
</div> |
||||||
|
{% endif %} |
||||||
|
{% csrf_token %} |
||||||
|
{{form}} |
||||||
|
|
||||||
|
<div class="d-grid gap-2 col-6 mx-auto mt-3"> |
||||||
|
<button class="btn btn-primary btn-lg ">actualizar datos de tipo articulo</button> |
||||||
|
</div> |
||||||
|
|
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</form> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</main> |
||||||
|
{% endblock %} |
||||||
@ -0,0 +1,3 @@ |
|||||||
|
from django.test import TestCase |
||||||
|
|
||||||
|
# Create your tests here. |
||||||
@ -0,0 +1,338 @@ |
|||||||
|
from django.shortcuts import render,redirect,get_object_or_404 |
||||||
|
from django.template.loader import get_template |
||||||
|
from django.http import HttpResponse |
||||||
|
from django.contrib.auth.forms import AuthenticationForm |
||||||
|
from django.contrib.auth import login, logout, authenticate |
||||||
|
from django.db import transaction |
||||||
|
from django.utils import timezone |
||||||
|
from django.contrib.auth.decorators import login_required |
||||||
|
from django.db.models import Avg, Sum |
||||||
|
from decimal import Decimal |
||||||
|
from .forms import formulario_proveedor,formulario_bodega, formulario_tipo, formulario_articulo,formilario_inventario |
||||||
|
from .models import proveedor, bodega, tipo_articulo, articulo,inventario,tipo_inv_movimiento,movimiento_inventario |
||||||
|
|
||||||
|
# Create your views here. |
||||||
|
def base(request): |
||||||
|
return render(request, 'index.html') |
||||||
|
|
||||||
|
def generar_grafico_chartjs(request): |
||||||
|
# Gráfico de Pastel: Cantidad por tipo de artículo |
||||||
|
tipos_articulo = tipo_articulo.objects.all() |
||||||
|
tipos_nombres = [t.tipo_articulo for t in tipos_articulo] |
||||||
|
cantidades_por_tipo = [articulo.objects.filter(tipo_articulo=t).count() for t in tipos_articulo] |
||||||
|
|
||||||
|
# Gráfico de Barras: Cantidad de artículos por bodega |
||||||
|
bodegas = bodega.objects.all() |
||||||
|
bodegas_nombres = [b.nombre_bodega for b in bodegas] |
||||||
|
cantidades_por_bodega = [inventario.objects.filter(bodega=b).count() for b in bodegas] |
||||||
|
|
||||||
|
# Gráfico de Línea: Movimientos en el tiempo |
||||||
|
|
||||||
|
tipos_movimientos = tipo_inv_movimiento.objects.all() |
||||||
|
nombres_tipos = [mov.tipo_movimiento for mov in tipos_movimientos] |
||||||
|
|
||||||
|
# Obtener la cantidad total movida para cada tipo de movimiento |
||||||
|
cantidades_por_tipos = [ |
||||||
|
movimiento_inventario.objects.filter(tipo_inventario=mov).aggregate(total=Sum('cantidad'))['total'] or 0 |
||||||
|
for mov in tipos_movimientos |
||||||
|
] |
||||||
|
tipos_articulo_prom = [t.tipo_articulo for t in tipos_articulo] |
||||||
|
|
||||||
|
precios_promedio = [ |
||||||
|
float(inventario.objects.filter(articulo__tipo_articulo=t).aggregate(avg=Avg('precio'))['avg'] or 0) |
||||||
|
for t in tipos_articulo |
||||||
|
] |
||||||
|
|
||||||
|
context = { |
||||||
|
'tipos_articulo': tipos_nombres, |
||||||
|
'cantidades_por_tipo': cantidades_por_tipo, |
||||||
|
'bodegas': bodegas_nombres, |
||||||
|
'cantidades_por_bodega': cantidades_por_bodega, |
||||||
|
'tipos_articulo_prom': tipos_articulo_prom, |
||||||
|
'precios_promedio': precios_promedio, |
||||||
|
'nombres_tipos': nombres_tipos, |
||||||
|
'cantidades_por_tipos': cantidades_por_tipos, |
||||||
|
} |
||||||
|
return render(request, 'index.html', context) |
||||||
|
|
||||||
|
def signin(request): |
||||||
|
if request.method == 'GET': |
||||||
|
return render ( request, 'signin.html',{ |
||||||
|
'form': AuthenticationForm |
||||||
|
}) |
||||||
|
else: |
||||||
|
user=authenticate(request, username=request.POST['username'], password=request.POST['password']) |
||||||
|
if user is None: |
||||||
|
return render ( request, 'signin.html',{ |
||||||
|
'form': AuthenticationForm , |
||||||
|
'error': 'Username or password is incorrect' |
||||||
|
}) |
||||||
|
else: |
||||||
|
login(request, user) |
||||||
|
return redirect('home') |
||||||
|
|
||||||
|
@login_required |
||||||
|
def signout(request): |
||||||
|
logout(request) |
||||||
|
return redirect('home') |
||||||
|
|
||||||
|
@login_required |
||||||
|
def proveedor_registro(request): |
||||||
|
if request.method=='GET': |
||||||
|
return render(request, 'proveedor_nuevo.html',{ |
||||||
|
'form': formulario_proveedor |
||||||
|
}) |
||||||
|
else: |
||||||
|
try: |
||||||
|
form = formulario_proveedor (request.POST) |
||||||
|
form.save() |
||||||
|
form = formulario_proveedor () |
||||||
|
return render(request, 'proveedor_nuevo.html',{ |
||||||
|
'form': formulario_proveedor, |
||||||
|
'mensaje':'Please provide valid data' |
||||||
|
}) |
||||||
|
except ValueError: |
||||||
|
return render(request, 'proveedor_nuevo.html',{ |
||||||
|
'form': formulario_proveedor, |
||||||
|
'error':'Please provide valid data' |
||||||
|
}) |
||||||
|
|
||||||
|
@login_required |
||||||
|
def proveedor_lista(request): |
||||||
|
persona = proveedor.objects.all |
||||||
|
return render(request,"proveedor_registro.html", {'persona':persona}) |
||||||
|
|
||||||
|
@login_required |
||||||
|
def Proveedor_update(request,task_id): |
||||||
|
if request.method == 'GET': |
||||||
|
tarea = get_object_or_404 (proveedor, pk=task_id) |
||||||
|
form = formulario_proveedor(instance=tarea) |
||||||
|
return render(request, 'proveedor_update.html',{'tasks':tarea, 'form':form} ) |
||||||
|
else: |
||||||
|
try: |
||||||
|
tarea=get_object_or_404(proveedor,pk=task_id) |
||||||
|
form = formulario_proveedor(request.POST, instance=tarea) |
||||||
|
form.save() |
||||||
|
return redirect('proveedor') |
||||||
|
except ValueError: |
||||||
|
return render(request, 'proveedor_update.html',{'tasks':tarea, 'form':form, 'error': 'error updating task'} ) |
||||||
|
|
||||||
|
@login_required |
||||||
|
def bodega_registro(request): |
||||||
|
if request.method=='GET': |
||||||
|
return render(request, 'bodega_nuevo.html',{ |
||||||
|
'form': formulario_bodega |
||||||
|
}) |
||||||
|
else: |
||||||
|
try: |
||||||
|
form = formulario_bodega (request.POST) |
||||||
|
form.save() |
||||||
|
form = formulario_bodega () |
||||||
|
return render(request, 'bodega_nuevo.html',{ |
||||||
|
'form': formulario_bodega, |
||||||
|
'mensaje':'Please provide valid data' |
||||||
|
}) |
||||||
|
except ValueError: |
||||||
|
return render(request, 'bodega_nuevo.html',{ |
||||||
|
'form': formulario_bodega, |
||||||
|
'error':'Please provide valid data' |
||||||
|
}) |
||||||
|
|
||||||
|
@login_required |
||||||
|
def bodega_vista(request): |
||||||
|
persona = bodega.objects.all |
||||||
|
return render(request,"bodega_registro.html", {'persona':persona}) |
||||||
|
|
||||||
|
@login_required |
||||||
|
def bodega_update(request,task_id): |
||||||
|
if request.method == 'GET': |
||||||
|
tarea = get_object_or_404 (bodega, pk=task_id) |
||||||
|
form = formulario_bodega(instance=tarea) |
||||||
|
return render(request, 'bodega_update.html',{'tasks':tarea, 'form':form} ) |
||||||
|
else: |
||||||
|
try: |
||||||
|
tarea=get_object_or_404(bodega,pk=task_id) |
||||||
|
form = formulario_bodega(request.POST, instance=tarea) |
||||||
|
form.save() |
||||||
|
return redirect('bodega_vista') |
||||||
|
except ValueError: |
||||||
|
return render(request, 'bodega_update.html',{'tasks':tarea, 'form':form, 'error': 'error updating task'} ) |
||||||
|
|
||||||
|
@login_required |
||||||
|
def tipo_registro(request): |
||||||
|
if request.method=='GET': |
||||||
|
return render(request, 'tipo_articulo_nuevo.html',{ |
||||||
|
'form': formulario_tipo |
||||||
|
}) |
||||||
|
else: |
||||||
|
try: |
||||||
|
form = formulario_tipo (request.POST) |
||||||
|
form.save() |
||||||
|
form = formulario_tipo () |
||||||
|
return render(request, 'tipo_articulo_nuevo.html',{ |
||||||
|
'form': formulario_tipo, |
||||||
|
'mensaje':'Please provide valid data' |
||||||
|
}) |
||||||
|
except ValueError: |
||||||
|
return render(request, 'tipo_articulo_nuevo.html',{ |
||||||
|
'form': formulario_tipo, |
||||||
|
'error':'Please provide valid data' |
||||||
|
}) |
||||||
|
|
||||||
|
@login_required |
||||||
|
def tipo_vista(request): |
||||||
|
persona = tipo_articulo.objects.all |
||||||
|
return render(request,"tipo_articulo_registro.html", {'persona':persona}) |
||||||
|
|
||||||
|
@login_required |
||||||
|
def tipo_update(request,task_id): |
||||||
|
if request.method == 'GET': |
||||||
|
tarea = get_object_or_404 (tipo_articulo, pk=task_id) |
||||||
|
form = formulario_tipo(instance=tarea) |
||||||
|
return render(request, 'tipo_articulo_update.html',{'tasks':tarea, 'form':form} ) |
||||||
|
else: |
||||||
|
try: |
||||||
|
tarea=get_object_or_404(tipo_articulo,pk=task_id) |
||||||
|
form = formulario_tipo(request.POST, instance=tarea) |
||||||
|
form.save() |
||||||
|
return redirect('tipo_vista') |
||||||
|
except ValueError: |
||||||
|
return render(request, 'tipo_articulo_update.html',{'tasks':tarea, 'form':form, 'error': 'error updating task'} ) |
||||||
|
|
||||||
|
@login_required |
||||||
|
def articulo_registro(request): |
||||||
|
if request.method=='GET': |
||||||
|
return render(request, 'articulo_nuevo.html',{ |
||||||
|
'form': formulario_articulo |
||||||
|
}) |
||||||
|
else: |
||||||
|
try: |
||||||
|
form = formulario_articulo (request.POST) |
||||||
|
form.save() |
||||||
|
form = formulario_articulo () |
||||||
|
return redirect('articulo_registro') |
||||||
|
|
||||||
|
except ValueError: |
||||||
|
return render(request, 'articulo_nuevo.html',{ |
||||||
|
'form': formulario_articulo, |
||||||
|
'error':'Please provide valid data' |
||||||
|
}) |
||||||
|
|
||||||
|
@login_required |
||||||
|
def articulo_vista(request): |
||||||
|
persona = articulo.objects.select_related('tipo_articulo','proveedor').all() |
||||||
|
return render(request,"articulo_registro.html", {'persona':persona}) |
||||||
|
|
||||||
|
@login_required |
||||||
|
def articulo_update(request,task_id): |
||||||
|
if request.method == 'GET': |
||||||
|
tarea = get_object_or_404 (articulo, pk=task_id) |
||||||
|
form = formulario_articulo(instance=tarea) |
||||||
|
return render(request, 'articulo_update.html',{'tasks':tarea, 'form':form} ) |
||||||
|
else: |
||||||
|
try: |
||||||
|
tarea=get_object_or_404(articulo,pk=task_id) |
||||||
|
form = formulario_articulo(request.POST, instance=tarea) |
||||||
|
form.save() |
||||||
|
return redirect('articulo_vista') |
||||||
|
except ValueError: |
||||||
|
return render(request, 'articulo_update.html',{'tasks':tarea, 'form':form, 'error': 'error updating'} ) |
||||||
|
|
||||||
|
@login_required |
||||||
|
def inventario_registro(request): |
||||||
|
if request.method=='GET': |
||||||
|
return render(request, 'inventario_nuevo.html',{ |
||||||
|
'form': formilario_inventario |
||||||
|
}) |
||||||
|
else: |
||||||
|
try: |
||||||
|
form = formilario_inventario (request.POST) |
||||||
|
form.save() |
||||||
|
form = formilario_inventario () |
||||||
|
return render(request, 'inventario_nuevo.html',{ |
||||||
|
'form': formilario_inventario, |
||||||
|
'mensaje':'Please provide valid data' |
||||||
|
}) |
||||||
|
except ValueError: |
||||||
|
return render(request, 'inventario_nuevo.html',{ |
||||||
|
'form': formilario_inventario, |
||||||
|
'error':'Please provide valid data' |
||||||
|
}) |
||||||
|
|
||||||
|
@login_required |
||||||
|
def inventario_vista(request): |
||||||
|
persona = inventario.objects.select_related('articulo','bodega').all() |
||||||
|
return render(request,"inventario_registro.html", {'persona':persona}) |
||||||
|
|
||||||
|
def registrar_movimiento_y_actualizar_inventario(movimiento_data): |
||||||
|
""" |
||||||
|
Registra un movimiento de inventario y actualiza el inventario relacionado. |
||||||
|
""" |
||||||
|
try: |
||||||
|
with transaction.atomic(): |
||||||
|
# Registrar el movimiento de inventario |
||||||
|
movimiento = movimiento_inventario.objects.create( |
||||||
|
articulo=movimiento_data['articulo'], |
||||||
|
bodega=movimiento_data['bodega'], |
||||||
|
tipo_inventario=movimiento_data['tipo_inventario'], |
||||||
|
cantidad=movimiento_data['cantidad'], |
||||||
|
precio=movimiento_data['precio'], |
||||||
|
observaciones=movimiento_data.get('observaciones', '') |
||||||
|
) |
||||||
|
|
||||||
|
# Buscar o crear el inventario correspondiente |
||||||
|
inventario_obj, created = inventario.objects.get_or_create( |
||||||
|
articulo=movimiento.articulo, |
||||||
|
bodega=movimiento.bodega, |
||||||
|
defaults={'cantidad': 0, 'precio': movimiento.precio} |
||||||
|
) |
||||||
|
|
||||||
|
# Actualizar stock según el tipo de movimiento |
||||||
|
if movimiento.tipo_inventario.id == 1: # Asegúrate de que 'nombre' sea un campo válido |
||||||
|
inventario_obj.cantidad += movimiento.cantidad |
||||||
|
elif movimiento.tipo_inventario.id == 2: |
||||||
|
if inventario_obj.cantidad < movimiento.cantidad: |
||||||
|
raise ValueError( |
||||||
|
f"No hay suficiente stock. Disponible: {inventario_obj.cantidad}, solicitado: {movimiento.cantidad}" |
||||||
|
) |
||||||
|
inventario_obj.cantidad -= movimiento.cantidad |
||||||
|
|
||||||
|
# Actualizar el precio en Inventario si es necesario |
||||||
|
if inventario_obj.precio != movimiento.precio: |
||||||
|
inventario_obj.precio = movimiento.precio |
||||||
|
|
||||||
|
# Guardar los cambios en el inventario |
||||||
|
inventario_obj.save() |
||||||
|
|
||||||
|
return movimiento, inventario_obj |
||||||
|
|
||||||
|
except Exception as e: |
||||||
|
raise ValueError(f"Error al registrar movimiento y actualizar inventario: {str(e)}") |
||||||
|
|
||||||
|
|
||||||
|
def registrar_movimiento(request): |
||||||
|
""" |
||||||
|
Vista para registrar un nuevo movimiento de inventario. |
||||||
|
""" |
||||||
|
if request.method == 'POST': |
||||||
|
form = formilario_inventario(request.POST) |
||||||
|
if form.is_valid(): |
||||||
|
movimiento_data = form.cleaned_data |
||||||
|
try: |
||||||
|
movimiento, inventario_obj = registrar_movimiento_y_actualizar_inventario(movimiento_data) |
||||||
|
return redirect('registrar_movimiento') # Redirigir a una página de éxito |
||||||
|
except ValueError as e: |
||||||
|
return render(request, 'inventario_nuevo.html', { |
||||||
|
'form': form, |
||||||
|
'error': str(e) |
||||||
|
}) |
||||||
|
else: |
||||||
|
form = formilario_inventario() |
||||||
|
|
||||||
|
return render(request, 'inventario_nuevo.html', {'form': form}) |
||||||
|
|
||||||
|
@login_required |
||||||
|
def vista_inventario_mov(request): |
||||||
|
persona = movimiento_inventario.objects.select_related('articulo','bodega','tipo_inventario').all() |
||||||
|
return render(request,"mov_inventario_registro.html", {'persona':persona}) |
||||||
|
|
||||||
@ -0,0 +1,83 @@ |
|||||||
|
from django.contrib.auth.decorators import login_required |
||||||
|
from django.http import HttpResponse |
||||||
|
import openpyxl |
||||||
|
import csv |
||||||
|
from django.template.loader import get_template |
||||||
|
from xhtml2pdf import pisa |
||||||
|
from django.shortcuts import render |
||||||
|
from .models import inventario |
||||||
|
|
||||||
|
@login_required |
||||||
|
def generar_reporte_pdf(request): |
||||||
|
# Obtener datos del inventario |
||||||
|
inventarios = inventario.objects.all() |
||||||
|
|
||||||
|
# Cargar la plantilla |
||||||
|
template = get_template('pdf.html') |
||||||
|
context = {'inventarios': inventarios} |
||||||
|
|
||||||
|
# Renderizar la plantilla a HTML |
||||||
|
html = template.render(context) |
||||||
|
|
||||||
|
# Crear un objeto de respuesta PDF |
||||||
|
response = HttpResponse(content_type='application/pdf') |
||||||
|
response['Content-Disposition'] = 'inline; filename="reporte.pdf"' |
||||||
|
|
||||||
|
# Convertir HTML a PDF |
||||||
|
pisa_status = pisa.CreatePDF(html, dest=response) |
||||||
|
|
||||||
|
if pisa_status.err: |
||||||
|
return HttpResponse(f'Error al generar PDF: {pisa_status.err}', status=500) |
||||||
|
|
||||||
|
return response |
||||||
|
|
||||||
|
@login_required |
||||||
|
def generar_reporte_excel(request): |
||||||
|
# Crear un nuevo libro de Excel |
||||||
|
wb = openpyxl.Workbook() |
||||||
|
ws = wb.active |
||||||
|
ws.title = 'Reporte de Inventario' |
||||||
|
|
||||||
|
# Encabezados |
||||||
|
headers = ['ID', 'Bodega', 'Artículo', 'Cantidad', 'Precio'] |
||||||
|
ws.append(headers) |
||||||
|
|
||||||
|
# Datos del inventario |
||||||
|
for item in inventario.objects.all(): |
||||||
|
ws.append([item.id, item.bodega.nombre_bodega, item.articulo.nombre_articulo, item.cantidad, item.precio]) |
||||||
|
|
||||||
|
# Configurar la respuesta HTTP |
||||||
|
response = HttpResponse(content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet') |
||||||
|
response['Content-Disposition'] = 'attachment; filename="reporte_inventario.xlsx"' |
||||||
|
|
||||||
|
# Guardar el archivo en la respuesta |
||||||
|
wb.save(response) |
||||||
|
return response |
||||||
|
|
||||||
|
@login_required |
||||||
|
def generar_reporte_csv(request): |
||||||
|
# Configurar la respuesta HTTP |
||||||
|
response = HttpResponse(content_type='text/csv') |
||||||
|
response['Content-Disposition'] = 'attachment; filename="reporte_inventario.csv"' |
||||||
|
|
||||||
|
writer = csv.writer(response) |
||||||
|
|
||||||
|
# Encabezados |
||||||
|
writer.writerow(['ID', 'Bodega', 'Artículo', 'Cantidad', 'Precio']) |
||||||
|
|
||||||
|
# Datos del inventario |
||||||
|
for item in inventario.objects.all(): |
||||||
|
writer.writerow([item.id, item.bodega.nombre_bodega, item.articulo.nombre_articulo, item.cantidad, item.precio]) |
||||||
|
|
||||||
|
return response |
||||||
|
|
||||||
|
@login_required |
||||||
|
def generar_grafico_chartjs(request): |
||||||
|
inventarios = inventario.objects.all() |
||||||
|
productos = [item.articulo.nombre_articulo for item in inventarios] |
||||||
|
cantidades = [item.cantidad for item in inventarios] |
||||||
|
|
||||||
|
return render(request, 'grafico_chartjs.html', { |
||||||
|
'productos': productos, |
||||||
|
'cantidades': cantidades, |
||||||
|
}) |
||||||
@ -0,0 +1,2 @@ |
|||||||
|
import pymysql |
||||||
|
pymysql.install_as_MySQLdb() |
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,16 @@ |
|||||||
|
""" |
||||||
|
ASGI config for TiendAlfa project. |
||||||
|
|
||||||
|
It exposes the ASGI callable as a module-level variable named ``application``. |
||||||
|
|
||||||
|
For more information on this file, see |
||||||
|
https://docs.djangoproject.com/en/5.1/howto/deployment/asgi/ |
||||||
|
""" |
||||||
|
|
||||||
|
import os |
||||||
|
|
||||||
|
from django.core.asgi import get_asgi_application |
||||||
|
|
||||||
|
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'TiendAlfa.settings') |
||||||
|
|
||||||
|
application = get_asgi_application() |
||||||
@ -0,0 +1,132 @@ |
|||||||
|
""" |
||||||
|
Django settings for TiendAlfa project. |
||||||
|
|
||||||
|
Generated by 'django-admin startproject' using Django 5.1.4. |
||||||
|
|
||||||
|
For more information on this file, see |
||||||
|
https://docs.djangoproject.com/en/5.1/topics/settings/ |
||||||
|
|
||||||
|
For the full list of settings and their values, see |
||||||
|
https://docs.djangoproject.com/en/5.1/ref/settings/ |
||||||
|
""" |
||||||
|
|
||||||
|
from pathlib import Path |
||||||
|
|
||||||
|
# Build paths inside the project like this: BASE_DIR / 'subdir'. |
||||||
|
BASE_DIR = Path(__file__).resolve().parent.parent |
||||||
|
|
||||||
|
|
||||||
|
# Quick-start development settings - unsuitable for production |
||||||
|
# See https://docs.djangoproject.com/en/5.1/howto/deployment/checklist/ |
||||||
|
|
||||||
|
# SECURITY WARNING: keep the secret key used in production secret! |
||||||
|
SECRET_KEY = 'django-insecure-f)f0@t_(a&ht0mo$&#nftao+_=s&^31u9))d=685^vs8&np=5k' |
||||||
|
|
||||||
|
# SECURITY WARNING: don't run with debug turned on in production! |
||||||
|
DEBUG = True |
||||||
|
|
||||||
|
ALLOWED_HOSTS = ['store.stevz.dev','127.0.0.1'] |
||||||
|
|
||||||
|
|
||||||
|
# Application definition |
||||||
|
|
||||||
|
INSTALLED_APPS = [ |
||||||
|
'django.contrib.admin', |
||||||
|
'django.contrib.auth', |
||||||
|
'django.contrib.contenttypes', |
||||||
|
'django.contrib.sessions', |
||||||
|
'django.contrib.messages', |
||||||
|
'django.contrib.staticfiles', |
||||||
|
'Inventario', |
||||||
|
|
||||||
|
] |
||||||
|
|
||||||
|
MIDDLEWARE = [ |
||||||
|
'django.middleware.security.SecurityMiddleware', |
||||||
|
'django.contrib.sessions.middleware.SessionMiddleware', |
||||||
|
'django.middleware.common.CommonMiddleware', |
||||||
|
'django.middleware.csrf.CsrfViewMiddleware', |
||||||
|
'django.contrib.auth.middleware.AuthenticationMiddleware', |
||||||
|
'django.contrib.messages.middleware.MessageMiddleware', |
||||||
|
'django.middleware.clickjacking.XFrameOptionsMiddleware', |
||||||
|
] |
||||||
|
|
||||||
|
ROOT_URLCONF = 'TiendAlfa.urls' |
||||||
|
|
||||||
|
TEMPLATES = [ |
||||||
|
{ |
||||||
|
'BACKEND': 'django.template.backends.django.DjangoTemplates', |
||||||
|
'DIRS': [], |
||||||
|
'APP_DIRS': True, |
||||||
|
'OPTIONS': { |
||||||
|
'context_processors': [ |
||||||
|
'django.template.context_processors.debug', |
||||||
|
'django.template.context_processors.request', |
||||||
|
'django.contrib.auth.context_processors.auth', |
||||||
|
'django.contrib.messages.context_processors.messages', |
||||||
|
], |
||||||
|
}, |
||||||
|
}, |
||||||
|
] |
||||||
|
|
||||||
|
WSGI_APPLICATION = 'TiendAlfa.wsgi.application' |
||||||
|
|
||||||
|
|
||||||
|
# Database |
||||||
|
# https://docs.djangoproject.com/en/5.1/ref/settings/#databases |
||||||
|
|
||||||
|
DATABASES = { |
||||||
|
'default': { |
||||||
|
'ENGINE': 'django.db.backends.mysql', # Motor de MySQL |
||||||
|
'NAME': 'ttienda', # Nombre de la base de datos |
||||||
|
'USER': 'ttienda', # Usuario de MySQL |
||||||
|
'PASSWORD': 'Tienda$2024', # Contraseña de MySQL |
||||||
|
'HOST': '10.138.214.232', # Dirección del servidor (por defecto localhost) |
||||||
|
'PORT': '3306', # Puerto de MySQL (por defecto 3306) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Password validation |
||||||
|
# https://docs.djangoproject.com/en/5.1/ref/settings/#auth-password-validators |
||||||
|
|
||||||
|
AUTH_PASSWORD_VALIDATORS = [ |
||||||
|
{ |
||||||
|
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', |
||||||
|
}, |
||||||
|
{ |
||||||
|
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', |
||||||
|
}, |
||||||
|
{ |
||||||
|
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', |
||||||
|
}, |
||||||
|
{ |
||||||
|
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', |
||||||
|
}, |
||||||
|
] |
||||||
|
|
||||||
|
|
||||||
|
# Internationalization |
||||||
|
# https://docs.djangoproject.com/en/5.1/topics/i18n/ |
||||||
|
|
||||||
|
LANGUAGE_CODE = 'en-us' |
||||||
|
|
||||||
|
TIME_ZONE = 'UTC' |
||||||
|
|
||||||
|
USE_I18N = True |
||||||
|
|
||||||
|
USE_TZ = True |
||||||
|
|
||||||
|
|
||||||
|
# Static files (CSS, JavaScript, Images) |
||||||
|
# https://docs.djangoproject.com/en/5.1/howto/static-files/ |
||||||
|
|
||||||
|
STATIC_URL = 'static/' |
||||||
|
|
||||||
|
LOGIN_URL= '/signin/' |
||||||
|
|
||||||
|
# Default primary key field type |
||||||
|
# https://docs.djangoproject.com/en/5.1/ref/settings/#default-auto-field |
||||||
|
|
||||||
|
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' |
||||||
@ -0,0 +1,54 @@ |
|||||||
|
""" |
||||||
|
URL configuration for TiendAlfa project. |
||||||
|
|
||||||
|
The `urlpatterns` list routes URLs to views. For more information please see: |
||||||
|
https://docs.djangoproject.com/en/5.1/topics/http/urls/ |
||||||
|
Examples: |
||||||
|
Function views |
||||||
|
1. Add an import: from my_app import views |
||||||
|
2. Add a URL to urlpatterns: path('', views.home, name='home') |
||||||
|
Class-based views |
||||||
|
1. Add an import: from other_app.views import Home |
||||||
|
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') |
||||||
|
Including another URLconf |
||||||
|
1. Import the include() function: from django.urls import include, path |
||||||
|
2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) |
||||||
|
""" |
||||||
|
from django.contrib import admin |
||||||
|
from django.urls import path |
||||||
|
from Inventario import views, viewspdf |
||||||
|
|
||||||
|
urlpatterns = [ |
||||||
|
path('admin/', admin.site.urls), |
||||||
|
path('',views.generar_grafico_chartjs, name='home'), |
||||||
|
path('logout/',views.signout, name ='logout'), |
||||||
|
path('signin/',views.signin, name ='signin'), |
||||||
|
path('proveedor/',views.proveedor_lista, name ='proveedor'), |
||||||
|
path('proveedor/crear/',views.proveedor_registro, name ='proveedor_registro'), |
||||||
|
path('proveedor/<int:task_id>/',views.Proveedor_update, name ='Proveedor_update'), |
||||||
|
path('bodega/',views.bodega_vista, name ='bodega_vista'), |
||||||
|
path('bodega/crearbodega/',views.bodega_registro, name ='bodega_registro'), |
||||||
|
path('bodega/<int:task_id>/',views.bodega_update, name ='bodega_update'), |
||||||
|
path('tipo/',views.tipo_vista, name ='tipo_vista'), |
||||||
|
path('tipo/creartipo_articulo/',views.tipo_registro, name ='tipo_registro'), |
||||||
|
path('tipo/<int:task_id>/',views.tipo_update, name ='tipo_update'), |
||||||
|
path('articulo/',views.articulo_vista, name ='articulo_vista'), |
||||||
|
path('articulo/crear/',views.articulo_registro, name ='articulo_registro'), |
||||||
|
path('articulo/<int:task_id>/',views.articulo_update, name ='articulo_update'), |
||||||
|
path('inventario/',views.inventario_vista, name ='inventario_vista'), |
||||||
|
path('inventariomov/crear/',views.registrar_movimiento, name ='registrar_movimiento'), |
||||||
|
path('inventariomov/',views.vista_inventario_mov, name ='vista_inventario_mov'), |
||||||
|
path('reporte/pdf/',viewspdf.generar_reporte_pdf, name ='generar_reporte_pdf'), |
||||||
|
path('reporte/excel/',viewspdf.generar_reporte_excel, name ='generar_reporte_excel'), |
||||||
|
path('reporte/csv/',viewspdf.generar_reporte_csv, name ='generar_reporte_csv'), |
||||||
|
path('grafico/chartjs/',viewspdf.generar_grafico_chartjs, name ='generar_grafico_chartjs'), |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
] |
||||||
@ -0,0 +1,16 @@ |
|||||||
|
""" |
||||||
|
WSGI config for TiendAlfa project. |
||||||
|
|
||||||
|
It exposes the WSGI callable as a module-level variable named ``application``. |
||||||
|
|
||||||
|
For more information on this file, see |
||||||
|
https://docs.djangoproject.com/en/5.1/howto/deployment/wsgi/ |
||||||
|
""" |
||||||
|
|
||||||
|
import os |
||||||
|
|
||||||
|
from django.core.wsgi import get_wsgi_application |
||||||
|
|
||||||
|
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'TiendAlfa.settings') |
||||||
|
|
||||||
|
application = get_wsgi_application() |
||||||
@ -0,0 +1,22 @@ |
|||||||
|
#!/usr/bin/env python |
||||||
|
"""Django's command-line utility for administrative tasks.""" |
||||||
|
import os |
||||||
|
import sys |
||||||
|
|
||||||
|
|
||||||
|
def main(): |
||||||
|
"""Run administrative tasks.""" |
||||||
|
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'TiendAlfa.settings') |
||||||
|
try: |
||||||
|
from django.core.management import execute_from_command_line |
||||||
|
except ImportError as exc: |
||||||
|
raise ImportError( |
||||||
|
"Couldn't import Django. Are you sure it's installed and " |
||||||
|
"available on your PYTHONPATH environment variable? Did you " |
||||||
|
"forget to activate a virtual environment?" |
||||||
|
) from exc |
||||||
|
execute_from_command_line(sys.argv) |
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__': |
||||||
|
main() |
||||||
Loading…
Reference in new issue