diff --git a/tienda_ilusion/don_confiao/admin.py b/tienda_ilusion/don_confiao/admin.py
index 659a321..0756544 100644
--- a/tienda_ilusion/don_confiao/admin.py
+++ b/tienda_ilusion/don_confiao/admin.py
@@ -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)
diff --git a/tienda_ilusion/don_confiao/example_products.csv b/tienda_ilusion/don_confiao/example_products.csv
new file mode 100644
index 0000000..46228a7
--- /dev/null
+++ b/tienda_ilusion/don_confiao/example_products.csv
@@ -0,0 +1,4 @@
+"producto","unidad","precio","categorias"
+"Aceite","Unidad", 50000,"Aceites&Alimentos"
+"Café","Unidad", 14000,"Cafes&Alimentos"
+"Arroz","Unidad", 7000,"Cafes&Alimentos"
diff --git a/tienda_ilusion/don_confiao/example_products2.csv b/tienda_ilusion/don_confiao/example_products2.csv
new file mode 100644
index 0000000..787e48d
--- /dev/null
+++ b/tienda_ilusion/don_confiao/example_products2.csv
@@ -0,0 +1,4 @@
+"producto","unidad","precio","categorias"
+"Aceite","Unidad", 50000,"Aceites&Alimentos"
+"Café","Unidad", 15000,"Cafes&Alimentos"
+"Arroz","Unidad", 6000,"Alimentos&Granos"
diff --git a/tienda_ilusion/don_confiao/forms.py b/tienda_ilusion/don_confiao/forms.py
new file mode 100644
index 0000000..a9ef913
--- /dev/null
+++ b/tienda_ilusion/don_confiao/forms.py
@@ -0,0 +1,4 @@
+from django import forms
+
+class ImportProductsForm(forms.Form):
+ csv_file = forms.FileField()
diff --git a/tienda_ilusion/don_confiao/migrations/0009_productcategory_product.py b/tienda_ilusion/don_confiao/migrations/0009_productcategory_product.py
new file mode 100644
index 0000000..6c7e15f
--- /dev/null
+++ b/tienda_ilusion/don_confiao/migrations/0009_productcategory_product.py
@@ -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')),
+ ],
+ ),
+ ]
diff --git a/tienda_ilusion/don_confiao/migrations/0010_alter_productcategory_name.py b/tienda_ilusion/don_confiao/migrations/0010_alter_productcategory_name.py
new file mode 100644
index 0000000..c400651
--- /dev/null
+++ b/tienda_ilusion/don_confiao/migrations/0010_alter_productcategory_name.py
@@ -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),
+ ),
+ ]
diff --git a/tienda_ilusion/don_confiao/migrations/0011_alter_product_name.py b/tienda_ilusion/don_confiao/migrations/0011_alter_product_name.py
new file mode 100644
index 0000000..9a6a092
--- /dev/null
+++ b/tienda_ilusion/don_confiao/migrations/0011_alter_product_name.py
@@ -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),
+ ),
+ ]
diff --git a/tienda_ilusion/don_confiao/models.py b/tienda_ilusion/don_confiao/models.py
index afe69e5..6d40cac 100644
--- a/tienda_ilusion/don_confiao/models.py
+++ b/tienda_ilusion/don_confiao/models.py
@@ -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
diff --git a/tienda_ilusion/don_confiao/templates/don_confiao/import_products.html b/tienda_ilusion/don_confiao/templates/don_confiao/import_products.html
new file mode 100644
index 0000000..376952d
--- /dev/null
+++ b/tienda_ilusion/don_confiao/templates/don_confiao/import_products.html
@@ -0,0 +1,10 @@
+{% if form.is_multipart %}
+
diff --git a/tienda_ilusion/don_confiao/templates/don_confiao/index.html b/tienda_ilusion/don_confiao/templates/don_confiao/index.html
index 2fbf6b4..7b791c0 100644
--- a/tienda_ilusion/don_confiao/templates/don_confiao/index.html
+++ b/tienda_ilusion/don_confiao/templates/don_confiao/index.html
@@ -2,5 +2,6 @@
Don Confiao
diff --git a/tienda_ilusion/don_confiao/test_products.py b/tienda_ilusion/don_confiao/test_products.py
new file mode 100644
index 0000000..0b4e0dc
--- /dev/null
+++ b/tienda_ilusion/don_confiao/test_products.py
@@ -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}
+ )
diff --git a/tienda_ilusion/don_confiao/urls.py b/tienda_ilusion/don_confiao/urls.py
index bb2e394..a9b716f 100644
--- a/tienda_ilusion/don_confiao/urls.py
+++ b/tienda_ilusion/don_confiao/urls.py
@@ -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")
]
diff --git a/tienda_ilusion/don_confiao/views.py b/tienda_ilusion/don_confiao/views.py
index 8a8af33..c4b7a5b 100644
--- a/tienda_ilusion/don_confiao/views.py
+++ b/tienda_ilusion/don_confiao/views.py
@@ -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')
@@ -22,18 +25,53 @@ 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)
\ No newline at end of file
+ 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)