Merge branch 'main' of ssh://gitea.onecluster.org:6666/OneTeam/don_confiao
This commit is contained in:
commit
a466d8b20a
@ -1,5 +1,7 @@
|
||||
from django.contrib import admin
|
||||
from .models import Sale, SaleLine
|
||||
from .models import Sale, SaleLine, Product, ProductCategory
|
||||
|
||||
admin.site.register(Sale)
|
||||
admin.site.register(SaleLine)
|
||||
admin.site.register(Product)
|
||||
admin.site.register(ProductCategory)
|
||||
|
4
tienda_ilusion/don_confiao/example_products.csv
Normal file
4
tienda_ilusion/don_confiao/example_products.csv
Normal file
@ -0,0 +1,4 @@
|
||||
"producto","unidad","precio","categorias"
|
||||
"Aceite","Unidad", 50000,"Aceites&Alimentos"
|
||||
"Café","Unidad", 14000,"Cafes&Alimentos"
|
||||
"Arroz","Unidad", 7000,"Cafes&Alimentos"
|
|
4
tienda_ilusion/don_confiao/example_products2.csv
Normal file
4
tienda_ilusion/don_confiao/example_products2.csv
Normal file
@ -0,0 +1,4 @@
|
||||
"producto","unidad","precio","categorias"
|
||||
"Aceite","Unidad", 50000,"Aceites&Alimentos"
|
||||
"Café","Unidad", 15000,"Cafes&Alimentos"
|
||||
"Arroz","Unidad", 6000,"Alimentos&Granos"
|
|
4
tienda_ilusion/don_confiao/forms.py
Normal file
4
tienda_ilusion/don_confiao/forms.py
Normal file
@ -0,0 +1,4 @@
|
||||
from django import forms
|
||||
|
||||
class ImportProductsForm(forms.Form):
|
||||
csv_file = forms.FileField()
|
@ -0,0 +1,30 @@
|
||||
# Generated by Django 5.0.6 on 2024-06-29 18:55
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('don_confiao', '0008_alter_sale_phone_alter_saleline_description'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='ProductCategory',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=100)),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Product',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=100)),
|
||||
('price', models.DecimalField(decimal_places=2, max_digits=9)),
|
||||
('measuring_unit', models.CharField(choices=[('UNIT', 'Unit')], default='UNIT', max_length=20)),
|
||||
('categories', models.ManyToManyField(to='don_confiao.productcategory')),
|
||||
],
|
||||
),
|
||||
]
|
@ -0,0 +1,18 @@
|
||||
# Generated by Django 5.0.6 on 2024-06-29 21:04
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('don_confiao', '0009_productcategory_product'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='productcategory',
|
||||
name='name',
|
||||
field=models.CharField(max_length=100, unique=True),
|
||||
),
|
||||
]
|
@ -0,0 +1,18 @@
|
||||
# Generated by Django 5.0.6 on 2024-06-29 21:48
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('don_confiao', '0010_alter_productcategory_name'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='product',
|
||||
name='name',
|
||||
field=models.CharField(max_length=100, unique=True),
|
||||
),
|
||||
]
|
@ -1,5 +1,5 @@
|
||||
from django.db import models
|
||||
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
class Sale(models.Model):
|
||||
|
||||
@ -16,3 +16,25 @@ class SaleLine(models.Model):
|
||||
quantity = models.IntegerField(null=True)
|
||||
unit_price = models.DecimalField(max_digits=9, decimal_places=2)
|
||||
description = models.CharField(max_length=255, null=True, blank=True)
|
||||
|
||||
class MeasuringUnits(models.TextChoices):
|
||||
UNIT = 'UNIT', _('Unit')
|
||||
|
||||
class ProductCategory(models.Model):
|
||||
name = models.CharField(max_length=100, unique=True)
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
class Product(models.Model):
|
||||
name = models.CharField(max_length=100, unique=True)
|
||||
price = models.DecimalField(max_digits=9, decimal_places=2)
|
||||
measuring_unit = models.CharField(
|
||||
max_length=20,
|
||||
choices=MeasuringUnits.choices,
|
||||
default=MeasuringUnits.UNIT
|
||||
)
|
||||
categories = models.ManyToManyField(ProductCategory)
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
@ -0,0 +1,10 @@
|
||||
{% if form.is_multipart %}
|
||||
<form enctype="multipart/form-data" method="post">
|
||||
{% else %}
|
||||
<form method="post">
|
||||
{% endif %}
|
||||
|
||||
{% csrf_token %}
|
||||
{{ form }}
|
||||
<input type="submit" value="Importar">
|
||||
</form>
|
@ -2,5 +2,6 @@
|
||||
<h2>Don Confiao</h2>
|
||||
<ul>
|
||||
<li><a href='./comprar'>Comprar</a></li>
|
||||
<li><a href='./compras'>Compras</a></li>
|
||||
<li><a href='./productos'>Productos</a></li>
|
||||
<li><a href='./importar_productos'>Importar Productos</a></li>
|
||||
</ul>
|
||||
|
106
tienda_ilusion/don_confiao/test_products.py
Normal file
106
tienda_ilusion/don_confiao/test_products.py
Normal file
@ -0,0 +1,106 @@
|
||||
from django.test import Client, TestCase
|
||||
from django.contrib.auth.models import AnonymousUser, User
|
||||
from django.conf import settings
|
||||
|
||||
from .views import import_products, products
|
||||
from .models import ProductCategory, Product
|
||||
|
||||
import os
|
||||
import json
|
||||
|
||||
class TestProducts(TestCase):
|
||||
def setUp(self):
|
||||
self.client = Client()
|
||||
|
||||
def test_import_products(self):
|
||||
self._import_csv()
|
||||
all_products = self._get_products()
|
||||
self.assertEqual(
|
||||
len(all_products),
|
||||
3
|
||||
)
|
||||
|
||||
def test_import_products_with_categories(self):
|
||||
self._import_csv()
|
||||
all_products = self._get_products()
|
||||
self.assertIn("Aceites", all_products[0]["categories"])
|
||||
|
||||
def test_don_repeat_categories_on_import(self):
|
||||
self._import_csv()
|
||||
categories_on_csv = ["Cafes", "Alimentos", "Aceites"]
|
||||
categories = ProductCategory.objects.all()
|
||||
self.assertCountEqual(
|
||||
[c.name for c in categories],
|
||||
categories_on_csv
|
||||
)
|
||||
|
||||
def test_update_products(self):
|
||||
self._import_csv()
|
||||
first_products = self._get_products()
|
||||
self._import_csv('example_products2.csv')
|
||||
seconds_products = self._get_products()
|
||||
self.assertEqual(len(first_products), len(seconds_products))
|
||||
|
||||
def test_preserve_id_on_import(self):
|
||||
self._import_csv()
|
||||
id_aceite = Product.objects.get(name='Aceite').id
|
||||
self._import_csv('example_products2.csv')
|
||||
id_post_updated = Product.objects.get(name='Aceite').id
|
||||
self.assertEqual(id_aceite, id_post_updated)
|
||||
|
||||
def test_update_categories_on_import(self):
|
||||
self._import_csv()
|
||||
first_products = self._get_products()
|
||||
first_categories = {p["name"]: p["categories"] for p in first_products}
|
||||
self._import_csv('example_products2.csv')
|
||||
updated_products = self._get_products()
|
||||
updated_categories = {p["name"]: p["categories"] for p in updated_products}
|
||||
|
||||
self.assertIn('Cafes', first_categories['Arroz'])
|
||||
self.assertNotIn('Granos', first_categories['Arroz'])
|
||||
|
||||
self.assertIn('Granos', updated_categories['Arroz'])
|
||||
self.assertNotIn('Cafes', updated_categories['Arroz'])
|
||||
|
||||
|
||||
def test_update_price(self):
|
||||
self._import_csv()
|
||||
first_products = self._get_products()
|
||||
first_prices = {p["name"]: p["price_list"] for p in first_products}
|
||||
expected_first_prices = {
|
||||
"Aceite": '50000.00',
|
||||
"Café": '14000.00',
|
||||
"Arroz": '7000.00'
|
||||
}
|
||||
self.assertDictEqual(
|
||||
expected_first_prices,
|
||||
first_prices
|
||||
)
|
||||
|
||||
self._import_csv('example_products2.csv')
|
||||
updated_products = self._get_products()
|
||||
updated_prices = {p["name"]: p["price_list"] for p in updated_products}
|
||||
expected_updated_prices = {
|
||||
"Aceite": '50000.00',
|
||||
"Café": '15000.00',
|
||||
"Arroz": '6000.00'
|
||||
}
|
||||
|
||||
self.assertDictEqual(
|
||||
expected_updated_prices,
|
||||
updated_prices
|
||||
)
|
||||
|
||||
def _get_products(self):
|
||||
products_response = self.client.get("/don_confiao/productos")
|
||||
return json.loads(products_response.content.decode('utf-8'))
|
||||
|
||||
def _import_csv(self, csv_file='example_products.csv'):
|
||||
app_name = "don_confiao"
|
||||
app_dir = os.path.join(settings.BASE_DIR, app_name)
|
||||
example_csv = os.path.join(app_dir, csv_file)
|
||||
with open(example_csv, 'rb') as csv:
|
||||
self.client.post(
|
||||
"/don_confiao/importar_productos",
|
||||
{"csv_file": csv}
|
||||
)
|
@ -2,9 +2,11 @@ from django.urls import path
|
||||
|
||||
from . import views
|
||||
|
||||
app_name = 'don_confiao'
|
||||
urlpatterns = [
|
||||
path("", views.index, name="wellcome"),
|
||||
path("comprar", views.buy, name="buy"),
|
||||
path("compras", views.purchases, name="purchases"),
|
||||
path("productos", views.products, name="products"),
|
||||
path("importar_productos", views.import_products, name="import_products")
|
||||
]
|
||||
|
@ -1,9 +1,12 @@
|
||||
from django.shortcuts import render
|
||||
from django.http import HttpResponse, JsonResponse
|
||||
from django.http import HttpResponse, HttpResponseRedirect, JsonResponse
|
||||
from django.template import loader
|
||||
|
||||
from .models import Sale
|
||||
from .models import Sale, Product, ProductCategory
|
||||
from .forms import ImportProductsForm
|
||||
|
||||
import csv
|
||||
import io
|
||||
|
||||
def index(request):
|
||||
return render(request, 'don_confiao/index.html')
|
||||
@ -28,19 +31,54 @@ def purchases(request):
|
||||
|
||||
|
||||
def products(request):
|
||||
products = [
|
||||
{
|
||||
"name": "Aceite de Coco Artesanal 500ml",
|
||||
"price_list": 50000,
|
||||
"uom": "Unit",
|
||||
"category": "Aceites"
|
||||
},
|
||||
{
|
||||
"name": "Cafe 500ml",
|
||||
"price_list": 14000,
|
||||
"uom": "Unit",
|
||||
"category": "Cafes"
|
||||
},
|
||||
]
|
||||
return JsonResponse(
|
||||
products, safe=False)
|
||||
rproducts = []
|
||||
products = Product.objects.all()
|
||||
for product in products:
|
||||
rproduct = {
|
||||
"name": product.name,
|
||||
"price_list": product.price,
|
||||
"uom": product.measuring_unit,
|
||||
"categories": [c.name for c in product.categories.all()]
|
||||
}
|
||||
rproducts.append(rproduct)
|
||||
|
||||
return JsonResponse(rproducts, safe=False)
|
||||
|
||||
|
||||
def import_products(request):
|
||||
if request.method == "POST":
|
||||
form = ImportProductsForm(request.POST, request.FILES)
|
||||
if form.is_valid():
|
||||
handle_import_products_file(request.FILES["csv_file"])
|
||||
return HttpResponseRedirect("productos")
|
||||
else:
|
||||
form = ImportProductsForm()
|
||||
return render(
|
||||
request,
|
||||
"don_confiao/import_products.html",
|
||||
{'form': form}
|
||||
)
|
||||
|
||||
def _categories_from_csv_string(categories_string, separator="&"):
|
||||
categories = categories_string.split(separator)
|
||||
clean_categories = [c.strip() for c in categories]
|
||||
return [_category_from_name(category) for category in clean_categories]
|
||||
|
||||
def _category_from_name(name):
|
||||
return ProductCategory.objects.get_or_create(name=name)[0]
|
||||
|
||||
def handle_import_products_file(csv_file):
|
||||
data = io.StringIO(csv_file.read().decode('utf-8'))
|
||||
reader = csv.DictReader(data, quotechar='"')
|
||||
for row in reader:
|
||||
product, created = Product.objects.update_or_create(
|
||||
name=row['producto'],
|
||||
defaults={
|
||||
'price': row['precio'],
|
||||
'measuring_unit': row['unidad']
|
||||
}
|
||||
)
|
||||
categories = _categories_from_csv_string(row["categorias"])
|
||||
product.categories.clear()
|
||||
for category in categories:
|
||||
product.categories.add(category)
|
||||
|
Loading…
Reference in New Issue
Block a user