chore: Delete numerous application modules, templates, static assets, documentation, and build files.

This commit is contained in:
2026-02-25 17:09:27 -03:00
parent 7ddaa2d1f9
commit 2fc4fafed7
562 changed files with 17 additions and 6810 deletions

0
products/__init__.py Normal file
View File

11
products/admin.py Normal file
View File

@@ -0,0 +1,11 @@
from django.contrib import admin
from products.models import Product, ProductComponent, UnitOfMeasure
admin.site.register(Product)
admin.site.register(ProductComponent)
admin.site.register(UnitOfMeasure)

11
products/api_views.py Normal file
View File

@@ -0,0 +1,11 @@
from rest_framework import viewsets, permissions
from .models import Product
from .serializers import ProductSerializer
class ProductViewSet(viewsets.ModelViewSet):
queryset = Product.objects.all()
serializer_class = ProductSerializer
permission_classes = [permissions.IsAuthenticatedOrReadOnly]
def get_queryset(self):
return Product.objects.all()

6
products/apps.py Normal file
View File

@@ -0,0 +1,6 @@
from django.apps import AppConfig
class ProductsConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'products'

View File

@@ -0,0 +1,27 @@
# Generated by Django 5.1.4 on 2024-12-10 01:18
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
('categories', '0002_rename_category_categories'),
]
operations = [
migrations.CreateModel(
name='Product',
fields=[
('id', models.AutoField(primary_key=True, serialize=False)),
('name', models.CharField(max_length=100)),
('description', models.TextField(blank=True, null=True)),
('price', models.DecimalField(decimal_places=2, max_digits=10)),
('active', models.BooleanField(default=True)),
('category', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='categories.categories')),
],
),
]

View File

@@ -0,0 +1,23 @@
# Generated by Django 5.1.4 on 2024-12-20 12:36
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('products', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='product',
name='image',
field=models.ImageField(blank=True, null=True, upload_to=''),
),
migrations.AddField(
model_name='product',
name='quantity',
field=models.IntegerField(default=0),
),
]

View File

@@ -0,0 +1,18 @@
# Generated by Django 5.1.4 on 2025-01-10 16:24
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('products', '0002_product_image_product_quantity'),
]
operations = [
migrations.AddField(
model_name='product',
name='cuisine',
field=models.BooleanField(default=False),
),
]

View File

@@ -0,0 +1,44 @@
# Generated by Django 5.1.4 on 2025-07-22 18:36
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('products', '0003_product_cuisine'),
]
operations = [
migrations.CreateModel(
name='UnitOfMeasure',
fields=[
('id', models.AutoField(primary_key=True, serialize=False)),
('name', models.CharField(help_text="Ex: 'Unidade', 'Kg', 'Litro'", max_length=50, unique=True)),
('abbreviation', models.CharField(help_text="Ex: 'un', 'kg', 'L'", max_length=10, unique=True)),
],
),
migrations.CreateModel(
name='ProductComponent',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('quantity_required', models.PositiveIntegerField(default=1, help_text='Quantidade deste componente necessária para o produto composto.')),
('component_product', models.ForeignKey(help_text='Um produto que é componente de outro produto.', on_delete=django.db.models.deletion.CASCADE, related_name='used_as_component_in', to='products.product')),
('composite_product', models.ForeignKey(help_text='O produto que é composto por outros produtos.', on_delete=django.db.models.deletion.CASCADE, related_name='composition_entries', to='products.product')),
],
options={
'unique_together': {('composite_product', 'component_product')},
},
),
migrations.AddField(
model_name='product',
name='components',
field=models.ManyToManyField(related_name='is_component_of', through='products.ProductComponent', to='products.product'),
),
migrations.AddField(
model_name='product',
name='unit_of_measure',
field=models.ForeignKey(blank=True, help_text='Unidade de medida para este produto.', null=True, on_delete=django.db.models.deletion.SET_NULL, to='products.unitofmeasure'),
),
]

View File

80
products/models.py Normal file
View File

@@ -0,0 +1,80 @@
from django.db import models
from django.contrib.auth.models import User
from categories.models import Categories
class UnitOfMeasure(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=50, unique=True, help_text="Ex: 'Unidade', 'Kg', 'Litro'")
abbreviation = models.CharField(max_length=10, unique=True, help_text="Ex: 'un', 'kg', 'L'")
def __str__(self):
return self.abbreviation
# Create your models here.
class Product(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=100)
description = models.TextField(null=True, blank=True)
image = models.ImageField(null=True, blank=True)
price = models.DecimalField(max_digits=10, decimal_places=2)
quantity = models.IntegerField(null=False, default=0)
category = models.ForeignKey(Categories, on_delete=models.CASCADE)
cuisine = models.BooleanField(default=False)
active = models.BooleanField(default=True)
unit_of_measure = models.ForeignKey(
UnitOfMeasure,
on_delete=models.SET_NULL, # Define como NULL se a unidade de medida for excluída
null=True,
blank=True,
help_text="Unidade de medida para este produto."
)
# Campo de composição (mantido da resposta anterior)
components = models.ManyToManyField(
'self',
through='ProductComponent',
through_fields=('composite_product', 'component_product'),
symmetrical=False,
related_name='is_component_of'
)
def __str__(self) -> str:
return f"{self.name}"
class ProductComponent(models.Model):
composite_product = models.ForeignKey(
Product,
on_delete=models.CASCADE,
related_name='composition_entries',
help_text="O produto que é composto por outros produtos."
)
component_product = models.ForeignKey(
Product,
on_delete=models.CASCADE,
related_name='used_as_component_in',
help_text="Um produto que é componente de outro produto."
)
quantity_required = models.PositiveIntegerField(
default=1,
help_text="Quantidade deste componente necessária para o produto composto."
)
class Meta:
unique_together = ('composite_product', 'component_product')
def __str__(self):
return (
f"{self.composite_product.name} requer "
f"{self.quantity_required} de {self.component_product.name}"
)

19
products/serializers.py Normal file
View File

@@ -0,0 +1,19 @@
from rest_framework import serializers
from .models import Product, UnitOfMeasure
class UnitOfMeasureSerializer(serializers.ModelSerializer):
class Meta:
model = UnitOfMeasure
fields = '__all__'
class ProductSerializer(serializers.ModelSerializer):
category_name = serializers.ReadOnlyField(source='category.name')
unit_of_measure_name = serializers.ReadOnlyField(source='unit_of_measure.name')
class Meta:
model = Product
fields = [
'id', 'name', 'description', 'image', 'price',
'quantity', 'category', 'category_name',
'cuisine', 'active', 'unit_of_measure', 'unit_of_measure_name'
]

View File

@@ -0,0 +1,136 @@
{% extends "base.html" %}
{% load static %}
{% block 'head' %}
<link rel="stylesheet" href="{% static 'products/css/products.css' %}">
{% endblock %}
{% block 'title' %}
Produtos
{% endblock %}
{% block 'body' %}
<body>
<div class="grid-container">
<div class="grid-top">
<button class="btn-primary"
onclick="openModal()" id="openModal">Novo Produto</button>
<input onclick="listerSortTeable()" type="text" id="search-product" name="search-product" placeholder="Buscar Produto" hx-get="{% url 'searchProduct' %}" hx-trigger="keyup" hx-target="#product-list">
<a href="https://raulrockbar.blogspot.com/p/cardapio.html" target="_blank">Cardápio Digital</a>
</div>
<table id="product-list">
<thead> <tr>
<th style="text-align: left;" data-col-type="text">Produto</th>
<th style="text-align: left;width: 20%;" data-col-type="number">Preço</th>
<th class="hide-on-mobile" style="text-align: left;" data-col-type="number">Quantidade</th>
<th class="hide-on-mobile" style="text-align: left;" data-col-type="text">Categoria</th>
<th style="text-align: left;width: 20%;">Ações</th> </tr>
</thead>
<tbody> {% for product in products %}
<tr>
<td id="name-{{product.id}}" >{{product.name}}</td>
<td id="price-{{product.id}}" >R$ {{product.price}}</td>
{% if product.quantity > 20 %}
<td class="hide-on-mobile" id="quantity-{{product.id}}" >{{product.quantity}}</td>
{% else %}
<td class="hide-on-mobile" id="quantity-{{product.id}}" style="background-color: brown;" >{{product.quantity}}</td>
{% endif %}
<td hidden class="hide-on-mobile" id="image-{{product.id}}" >{{product.image}}</td>
<td class="hide-on-mobile" id="category-{{product.id}}" >{{product.category.name}}</td>
<td>
<div class="grid-buttons">
<img
src="{% static 'midia/icons/edit.svg' %}"
style="width: 35px; height: 35px; cursor: pointer;"
onclick="editProduct({{product.id}})"
>
</img>
<input type="hidden" id="h-category-{{product.id}}" value="{{ product.category.id }}">
<input type="hidden" id="description-{{product.id}}" value="{{ product.description }}">
<input type="hidden" id="cuisine-{{product.id}}" value="{{ product.cuisine }}">
<form hx-post="{% url 'onOffproduct' %}" hx-trigger="click" hx-target="#product-list">
{% csrf_token %}
<input type="hidden" name="id-product" id="id-{{product.id}}" value="{{ product.id }}">
{% if product.active == True %}
<button style="background-color: rgba(255, 0, 0, 0); padding: 0px;border: 0px;">
<span data-tooltip="Ativar ou Desativar Produto" data-flow="top">
<img
src="{% static 'midia/icons/toggle-on.svg' %}"
style="width: 35px; height: 35px; cursor: pointer;"
>
</img>
</span>
</button>
{% else %}
<button style="background-color: rgba(0, 128, 0, 0); padding: 0px;border: 0px;" >
<span data-tooltip="Ativar ou Desativar Produto" data-flow="top">
<img
src="{% static 'midia/icons/toggle-off.svg' %}"
style="width: 35px; height: 35px; cursor: pointer;"
>
</img>
<span>
</button>
{% endif %}
</form>
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</body>
<dialog id='Modal-create-product' >
<article id="modal-product" class="modal-product">
<form style="z-index: 1000; position: relative;" action="{% url 'create_product' %}" id="productForm" method="post" >
{% csrf_token %}
<h2>Cadastro de Produto</h2>
<!-- <div style="height: 200px;border-radius: 15px;">
<img id="image-product" src="" alt="">
</div> -->
<input type="text" id="productId" name="productId" hidden >
<input type="text" id="productName" name="name" required placeholder="Nome">
<input type="number" step="0.01" id="productPrice" name="price" required placeholder="Preço">
<input type="number" step="1" id="productqtd" name="qtd" placeholder="Quantidade">
<input type="text" id="url-image" name="image" required placeholder="URL da imagem">
<div>
<input type="checkbox" id="cuisine" name="cuisine" placeholder="Cozinha">Cozinha
</div>
<select id="select-categorie" name="select-categorie" >
{% for categorie in categories %}
<option value="{{categorie.id}}">{{categorie.name}}</option>
{% endfor %}
</select>
<textarea id="productDescription" name="description" rows="4" placeholder="Descrição"></textarea>
<footer>
<div style="display: flex;gap: 10px;">
<button class="btn-primary" id="save" type="submit">Salvar</button>
<button class="btn-primary" type="submit" onclick="closeModal()" type="button" id="edit" hx-post="{% url 'editProduct' 1 %}" hx-trigger="click" hx-target="#product-list" >Alterar</button>
<button class="btn-cancel" type="button" onclick="closeModal()">Cancelar</button>
</div>
</footer>
</form>
</article>
</dialog>
<script src="{% static 'products/js/products.js' %}"></script>
{% endblock %}

3
products/tests.py Normal file
View File

@@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

14
products/urls.py Normal file
View File

@@ -0,0 +1,14 @@
from django.urls import path
from . import views
urlpatterns = [
path('', views.products, name='products'),
path('create_product', views.createProduct, name='create_product'),
path('onOffproduct', views.onOffProduct, name='onOffproduct'),
path('searchProduct', views.searchProduct, name='searchProduct'),
path('editProduct/<int:productId>/', views.editProduct, name='editProduct'),
path('createJson', views.createJson, name='createJson'),
]

115
products/views.py Normal file
View File

@@ -0,0 +1,115 @@
from django.shortcuts import render, redirect
from django.http import HttpResponse
import json
from django.db.models import Q, Count, F
from categories.models import Categories
from comandas.models import ProductComanda
from products.models import Product
from gestaoRaul.decorators import group_required
@group_required(groupName='Garçom')
def products(request):
protucts = Product.objects.all().order_by('-active', 'category')
categories = Categories.objects.all()
return render(request, 'products.html', {'products': protucts, 'categories': categories})
@group_required(groupName='Garçom')
def searchProduct(request):
product = request.GET.get("search-product")
products = Product.objects.filter(name__icontains=product).order_by('-active', 'category')
return render(request, "htmx_components/products/htmx_search_products.html", {"products": products})
@group_required(groupName='Gerente')
def createProduct(request):
name = request.POST.get('name')
description = request.POST.get('description')
price = request.POST.get('price')
category = Categories.objects.get(id = int(request.POST.get('select-categorie')))
product = Product(name=name, description=description, price=price, category=category)
product.save()
return redirect('/products')
@group_required(groupName='Gerente')
def onOffProduct(request):
id = request.POST.get('id-product')
product_id = int(id)
product = Product.objects.get(id=product_id)
product.active = not product.active
product.save()
products = Product.objects.all().order_by('-active', 'category')
return render(request, "htmx_components/products/htmx_search_products.html", {"products": products})
@group_required(groupName='Gerente')
def editProduct(request, productId):
product_id = int(request.POST.get('productId'))
# product_id = productId
product = Product.objects.get(id=product_id)
product.name = request.POST.get('name')
product.description = request.POST.get('description')
product.price = request.POST.get('price')
product.quantity = request.POST.get('qtd')
product.image = request.POST.get('image')
product.cuisine = True if request.POST.get('cuisine') else False
product.category = Categories.objects.get(id = int(request.POST.get('select-categorie')))
product.save()
product = request.GET.get("search-product")
if product == None:
product = ''
products = Product.objects.filter(name__icontains=product)
return render(request, "htmx_components/products/htmx_search_products.html", {"products": products})
def createJson(request):
produtos_mais_vendidos = list(ProductComanda.objects.values('product').annotate(
quantidade=Count('product'),
nome=F('product__name') ).order_by('-quantidade'))
products = Product.objects.filter(active=True).exclude(
category__name__in=['Insumos',
'Adicional',
'Bomboniere',
'Lojinha',
'Utensilios',
'Litros',
'Ingressos',
'Cigarros',
'Outros']
)
products_ordenados = []
for produto in produtos_mais_vendidos:
for p in products:
if p.name == produto['nome']:
products_ordenados.append(p)
product_list = []
for product in products_ordenados:
product_data = {
"id": product.id,
"name": product.name,
"description": product.description or "",
"price": float(product.price),
"category": product.category.name if product.category else "",
"image": str(product.image) if product.image else f"https://placehold.co/400x250/efc7b8/49291c?text={product.name.replace(' ', '+')}"
}
product_list.append(product_data)
# Retorna como JSON em texto simples
return HttpResponse(
json.dumps(product_list, indent=4, ensure_ascii=False),
content_type="application/json; charset=utf-8"
)