Merge branch 'main' of ssh://gitea.onecluster.org:6666/OneTeam/don_confiao

This commit is contained in:
Rodia 2024-06-29 21:01:31 -05:00
commit a466d8b20a
13 changed files with 280 additions and 21 deletions

View File

@ -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)

View File

@ -0,0 +1,4 @@
"producto","unidad","precio","categorias"
"Aceite","Unidad", 50000,"Aceites&Alimentos"
"Café","Unidad", 14000,"Cafes&Alimentos"
"Arroz","Unidad", 7000,"Cafes&Alimentos"
1 producto unidad precio categorias
2 Aceite Unidad 50000 Aceites&Alimentos
3 Café Unidad 14000 Cafes&Alimentos
4 Arroz Unidad 7000 Cafes&Alimentos

View File

@ -0,0 +1,4 @@
"producto","unidad","precio","categorias"
"Aceite","Unidad", 50000,"Aceites&Alimentos"
"Café","Unidad", 15000,"Cafes&Alimentos"
"Arroz","Unidad", 6000,"Alimentos&Granos"
1 producto unidad precio categorias
2 Aceite Unidad 50000 Aceites&Alimentos
3 Café Unidad 15000 Cafes&Alimentos
4 Arroz Unidad 6000 Alimentos&Granos

View File

@ -0,0 +1,4 @@
from django import forms
class ImportProductsForm(forms.Form):
csv_file = forms.FileField()

View File

@ -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')),
],
),
]

View File

@ -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),
),
]

View File

@ -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),
),
]

View File

@ -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

View File

@ -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>

View File

@ -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>

View 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}
)

View File

@ -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")
]

View File

@ -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)