refactor: Update project settings, URL configurations, client views, and remove requirements.txt.

This commit is contained in:
2026-02-23 18:44:06 -03:00
parent 2160998c23
commit 7ddaa2d1f9
129 changed files with 888 additions and 40 deletions

18
gestaoRaul/.gitignore vendored Normal file
View File

@@ -0,0 +1,18 @@
.venv/
__pycache__/
*.py[cod]
*$py.class
.env
db.sqlite3
media/
static/
.python-version
uv.lock
pyproject.toml
GUIA_POSTMAN_API.md
brain/
logs/
walkthrough.md
task.md
implementation_plan*.md
*.webp

0
gestaoRaul/README.md Normal file
View File

View File

@@ -0,0 +1,8 @@
from rest_framework import viewsets, permissions
from .models import Categories
from .serializers import CategoriesSerializer
class CategoriesViewSet(viewsets.ModelViewSet):
queryset = Categories.objects.all()
serializer_class = CategoriesSerializer
permission_classes = [permissions.IsAuthenticatedOrReadOnly]

View File

@@ -0,0 +1,7 @@
from rest_framework import serializers
from .models import Categories
class CategoriesSerializer(serializers.ModelSerializer):
class Meta:
model = Categories
fields = '__all__'

View File

@@ -0,0 +1,74 @@
from rest_framework import viewsets, permissions, status
from rest_framework.decorators import action
from rest_framework.response import Response
from .models import Client
from .serializers import ClientSerializer
from comandas.models import Comanda, ProductComanda
from comandas.serializers import ComandaSerializer
from payments.models import Payments, somar
from typePay.models import TypePay
class ClientViewSet(viewsets.ModelViewSet):
queryset = Client.objects.all()
serializer_class = ClientSerializer
permission_classes = [permissions.IsAuthenticated]
def get_queryset(self):
return Client.objects.all()
@action(detail=True, methods=['get'])
def fiados(self, request, pk=None):
client = self.get_object()
comandas = Comanda.objects.filter(client=client, status='FIADO')
serializer = ComandaSerializer(comandas, many=True)
return Response(serializer.data)
@action(detail=False, methods=['post'])
def pagar_fiados(self, request):
comanda_ids = request.data.get('ids', [])
if not comanda_ids:
return Response({'error': 'Nenhum ID de comanda fornecido.'}, status=status.HTTP_400_BAD_REQUEST)
results = []
for comanda_id in comanda_ids:
try:
comanda = Comanda.objects.get(id=comanda_id)
# Apenas processar se estiver como FIADO
if comanda.status != 'FIADO':
results.append({'id': comanda_id, 'status': 'erro', 'message': 'Status da comanda não é FIADO.'})
continue
# 1. Fechar a comanda
comanda.status = 'CLOSED'
comanda.save()
# 2. Gerar o pagamento (Inspirado no payDebt original)
try:
type_pay = TypePay.objects.get(id=1) # Assume ID 1 como padrão para recebimento de fiado
except TypePay.DoesNotExist:
type_pay, _ = TypePay.objects.get_or_create(id=1, defaults={'name': 'Dinheiro'})
consumo = ProductComanda.objects.filter(comanda=comanda)
valores = somar(consumo, comanda)
payment = Payments.objects.create(
value=valores['totalSemTaxa'], # Valor que falta pagar
type_pay=type_pay,
comanda=comanda,
client=comanda.client,
description='PAGAMENTO DE FIADO (via API)'
)
results.append({'id': comanda_id, 'status': 'sucesso', 'payment_id': payment.id})
except Comanda.DoesNotExist:
results.append({'id': comanda_id, 'status': 'erro', 'message': 'Comanda não encontrada.'})
except Exception as e:
results.append({'id': comanda_id, 'status': 'erro', 'message': str(e)})
return Response({
'success': True,
'message': f'{len(comanda_ids)} comandas processadas',
'results': results
}, status=status.HTTP_200_OK)

View File

@@ -0,0 +1,23 @@
from rest_framework import serializers
from .models import Client
from comandas.models import Comanda, ProductComanda
from payments.models import Payments, somar
from decimal import Decimal
class ClientSerializer(serializers.ModelSerializer):
debt = serializers.SerializerMethodField()
class Meta:
model = Client
fields = ['id', 'name', 'created_at', 'active', 'contact', 'debt']
def get_debt(self, obj):
comandas = Comanda.objects.filter(client=obj, status='FIADO')
total_debt = Decimal(0)
for comanda in comandas:
consumo = ProductComanda.objects.filter(comanda=comanda)
valores = somar(consumo, comanda)
total_debt += valores['totalSemTaxa']
return total_debt

View File

@@ -89,14 +89,12 @@ def payDebt(request):
typePayment = TypePay.objects.get(id=1)
consumo = ProductComanda.objects.filter(comanda=comanda_id)
value = somar(consumo,comanda)
print(value["totalSemTaxa"])
description = 'PAGAMENTO DE FIADO'
pagamento = Payments(value=value["totalSemTaxa"], comanda=comanda, type_pay=typePayment,description=description,client=comanda.client)
pagamento.save()
except Comanda.DoesNotExist:
return JsonResponse({'error': f'Comanda com ID {comanda_id} não encontrada'}, status=404)
# return redirect(f'/clients/viewClient/{comanda.client.id}')
return JsonResponse({
'success': True,

View File

@@ -0,0 +1,168 @@
from rest_framework import viewsets, permissions, status
from rest_framework.decorators import action
from rest_framework.response import Response
from django.utils import timezone
from .models import Comanda, ProductComanda, StockMovement, StockMovementType
from .serializers import ComandaSerializer, ProductComandaSerializer
from payments.models import Payments
from typePay.models import TypePay
from clients.models import Client
class ComandaViewSet(viewsets.ModelViewSet):
queryset = Comanda.objects.all()
serializer_class = ComandaSerializer
permission_classes = [permissions.IsAuthenticated]
def get_queryset(self):
return Comanda.objects.all()
@action(detail=True, methods=['post'])
def apagar(self, request, pk=None):
comanda = self.get_object()
# 1. Recuperar os itens para devolver ao estoque
itens = ProductComanda.objects.filter(comanda=comanda)
# Tipo de movimentação: Estorno/Cancelamento (ajustar conforme os tipos existentes)
# Se não houver um tipo específico, podemos buscar um genérico ou usar o de Venda com sinal invertido via sumTransactionStock
try:
typeMovement = StockMovementType.objects.get(name="Estorno - Comanda Apagada")
except StockMovementType.DoesNotExist:
# Fallback para um tipo existente se o de estorno não existir
typeMovement, _ = StockMovementType.objects.get_or_create(name="Ajuste de Estoque (Cancelamento)")
for item in itens:
StockMovement.sumTransactionStock(
product=item.product,
movement_type=typeMovement,
comanda=comanda,
user=request.user,
qtd=1,
obs=f"Estorno: Comanda {comanda.name} apagada/limpa via API"
)
# 2. Excluir os itens da comanda
itens.delete()
# 3. Mudar o status para CLOSED
comanda.status = 'CLOSED'
comanda.save()
@action(detail=True, methods=['post'])
def pagar(self, request, pk=None):
comanda = self.get_object()
# Dados do pagamento vindos no request
value = request.data.get('value')
type_pay_id = request.data.get('type_pay')
client_id = request.data.get('client')
description = request.data.get('description', f"Pagamento da comanda {comanda.name}")
if not value or not type_pay_id:
return Response(
{'error': 'Campos "value" e "type_pay" são obrigatórios.'},
status=status.HTTP_400_BAD_REQUEST
)
try:
type_pay = TypePay.objects.get(id=type_pay_id)
except TypePay.DoesNotExist:
return Response({'error': 'Tipo de pagamento não encontrado.'}, status=status.HTTP_400_BAD_REQUEST)
client = None
if client_id:
try:
client = Client.objects.get(id=client_id)
except Client.DoesNotExist:
return Response({'error': 'Cliente não encontrado.'}, status=status.HTTP_400_BAD_REQUEST)
# 1. Criar o registro de pagamento
payment = Payments.objects.create(
value=value,
type_pay=type_pay,
comanda=comanda,
client=client,
description=description
)
# 2. Fechar a comanda
comanda.status = 'CLOSED'
comanda.dt_close = timezone.now()
comanda.save()
return Response({
'status': 'Pagamento registrado e comanda fechada com sucesso.',
'payment_id': payment.id
}, status=status.HTTP_200_OK)
class ProductComandaViewSet(viewsets.ModelViewSet):
queryset = ProductComanda.objects.all()
serializer_class = ProductComandaSerializer
permission_classes = [permissions.IsAuthenticated]
def perform_create(self, serializer):
# Salva o item na comanda
product_comanda = serializer.save()
# Recupera os dados para a movimentação de estoque
product = product_comanda.product
comanda = product_comanda.comanda
# Tipo de movimentação: Venda - Comanda (como na view original)
try:
typeMovement = StockMovementType.objects.get(name="Venda - Comanda")
except StockMovementType.DoesNotExist:
typeMovement, _ = StockMovementType.objects.get_or_create(name="Saída de Estoque (API)")
StockMovement.subTransactionStock(
product=product,
movement_type=typeMovement,
comanda=comanda,
user=self.request.user,
qtd=1,
obs="Adicionado na comanda via API"
)
# 3. Criar Pedido (Order) automaticamente se for item de cozinha
# (Lógica inspirada no addProduct original)
if product.cuisine:
from orders.models import Order
Order.objects.create(
id_comanda=comanda,
id_product=product,
productComanda=product_comanda,
obs=self.request.data.get('obs', '')
)
def perform_update(self, serializer):
instance = serializer.save()
obs = self.request.data.get('obs')
if obs is not None:
order = instance.order_set.first()
if order:
order.obs = obs
order.save()
def perform_destroy(self, instance):
# Recupera os dados antes de deletar
product = instance.product
comanda = instance.comanda
# Tipo de movimentação: Estorno/Cancelamento
try:
typeMovement = StockMovementType.objects.get(name="Estorno - Item Removido")
except StockMovementType.DoesNotExist:
typeMovement, _ = StockMovementType.objects.get_or_create(name="Ajuste de Estoque (Cancelamento)")
# Realiza a devolução ao estoque
StockMovement.sumTransactionStock(
product=product,
movement_type=typeMovement,
comanda=comanda,
user=self.request.user,
qtd=1,
obs=f"Estorno: Item {product.name} removido da comanda {comanda.name} via API"
)
# Deleta o registro definitivamente
instance.delete()

View File

@@ -0,0 +1,28 @@
from rest_framework import serializers
from .models import Comanda, ProductComanda
class ProductComandaSerializer(serializers.ModelSerializer):
product_name = serializers.ReadOnlyField(source='product.name')
obs = serializers.SerializerMethodField()
class Meta:
model = ProductComanda
fields = ['id', 'comanda', 'data_time', 'product', 'product_name', 'applicant', 'obs']
def get_obs(self, obj):
order = obj.order_set.first()
return order.obs if order else ""
class ComandaSerializer(serializers.ModelSerializer):
mesa_name = serializers.ReadOnlyField(source='mesa.name')
client_name = serializers.ReadOnlyField(source='client.name')
user_name = serializers.ReadOnlyField(source='user.username')
items = ProductComandaSerializer(many=True, read_only=True, source='productcomanda_set')
class Meta:
model = Comanda
fields = [
'id', 'mesa', 'mesa_name', 'user', 'user_name',
'type_pay', 'dt_open', 'dt_close', 'client',
'client_name', 'name', 'status', 'items'
]

Binary file not shown.

View File

@@ -0,0 +1,31 @@
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from orders.api_views import OrderViewSet
from products.api_views import ProductViewSet
from clients.api_views import ClientViewSet
from mesas.api_views import MesaViewSet
from comandas.api_views import ComandaViewSet, ProductComandaViewSet
from categories.api_views import CategoriesViewSet
from typePay.api_views import TypePayViewSet
from payments.api_views import PaymentsViewSet
from rest_framework_simplejwt.views import (
TokenRefreshView,
)
from login.api_views import MyTokenObtainPairView
router = DefaultRouter()
router.register(r'orders', OrderViewSet, basename='order')
router.register(r'products', ProductViewSet, basename='product')
router.register(r'clients', ClientViewSet, basename='client')
router.register(r'mesas', MesaViewSet, basename='mesa')
router.register(r'comandas', ComandaViewSet, basename='comanda')
router.register(r'items-comanda', ProductComandaViewSet, basename='items-comanda')
router.register(r'categories', CategoriesViewSet, basename='category')
router.register(r'payment-types', TypePayViewSet, basename='payment-type')
router.register(r'payments', PaymentsViewSet, basename='payment')
urlpatterns = [
path('', include(router.urls)),
path('token/', MyTokenObtainPairView.as_view(), name='token_obtain_pair'),
path('token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
]

View File

@@ -12,6 +12,7 @@ https://docs.djangoproject.com/en/5.1/ref/settings/
import os
from pathlib import Path
from datetime import timedelta
from dotenv import load_dotenv
load_dotenv()
@@ -31,21 +32,12 @@ BASE_DIR = Path(__file__).resolve().parent.parent
# 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-mqcnrs5!hc6bj$&#0@@9d8^2@x#w&$qhk3vlr5_)3znd9h6&d8'
SECRET_KEY = os.getenv('SECRET_KEY', 'django-insecure-mqcnrs5!hc6bj$&#0@@9d8^2@x#w&$qhk3vlr5_)3znd9h6&d8')
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
DEBUG = os.getenv('DEBUG', 'True') == 'True'
ALLOWED_HOSTS = [
'192.168.0.150',
'192.168.1.150',
'10.72.150.166',
'localhost',
'127.0.0.1',
'rrbec.local.com',
'django_app',
'0.0.0.0'
]
ALLOWED_HOSTS = ['*']
# Application definition
@@ -70,10 +62,14 @@ INSTALLED_APPS = [
'login',
'django_extensions',
'pwa',
'rest_framework',
'corsheaders',
]
MIDDLEWARE = [
'corsheaders.middleware.CorsMiddleware',
'django.middleware.security.SecurityMiddleware',
'whitenoise.middleware.WhiteNoiseMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
@@ -106,24 +102,24 @@ WSGI_APPLICATION = 'gestaoRaul.wsgi.application'
# Database
# https://docs.djangoproject.com/en/5.1/ref/settings/#databases
# DATABASES = {
# 'default': {
# 'ENGINE': 'django.db.backends.sqlite3',
# 'NAME': BASE_DIR / 'db.sqlite3',
# }
# }
DATABASES = {
'default': {
'ENGINE': DB_ENGINE,
'NAME': DB_NAME,
'USER': DB_USER,
'PASSWORD': DB_PASSWORD,
'HOST': DB_HOST,
'PORT': DB_PORT,
}
}
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}
# DATABASES = {
# 'default': {
# 'ENGINE': DB_ENGINE,
# 'NAME': DB_NAME,
# 'USER': DB_USER,
# 'PASSWORD': DB_PASSWORD,
# 'HOST': DB_HOST,
# 'PORT': DB_PORT,
# }
# }
# Password validation
@@ -165,6 +161,16 @@ STATIC_URL = 'static/'
STATICFILES_DIRS = (os.path.join(BASE_DIR, 'templates/static'),)
STATIC_ROOT = os.path.join(BASE_DIR, 'static')
# WhiteNoise storage to compress and cache static files
STORAGES = {
"default": {
"BACKEND": "django.core.files.storage.FileSystemStorage",
},
"staticfiles": {
"BACKEND": "whitenoise.storage.CompressedManifestStaticFilesStorage",
},
}
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
MEDIA_URL = '/media/'
@@ -199,4 +205,23 @@ PWA_APP_SPLASH_SCREEN = {
"media": "(device-width: 320px) and (device-height: 568px) and (-webkit-device-pixel-ratio: 2)"
}
PWA_APP_DIR = "ltr"
PWA_APP_LANG = "pt-BR"
PWA_APP_LANG = "pt-BR"
# --- REST FRAMEWORK & CORS ---
CORS_ALLOW_ALL_ORIGINS = True # Para desenvolvimento
CORS_ALLOW_CREDENTIALS = True # Permite envio de cookies para SessionAuth
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.SessionAuthentication', # Prioridade para teste no navegador
'rest_framework_simplejwt.authentication.JWTAuthentication',
),
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.IsAuthenticated',
),
}
SIMPLE_JWT = {
'ACCESS_TOKEN_LIFETIME': timedelta(minutes=60),
'REFRESH_TOKEN_LIFETIME': timedelta(days=1),
}

View File

@@ -30,6 +30,5 @@ urlpatterns = [
path('balcao/', include('balcao.urls')),
path('pedidos/', include('orders.urls')),
path('', include('pwa.urls')),
path('api/v1/', include('gestaoRaul.api_urls')),
]

View File

@@ -0,0 +1,5 @@
from rest_framework_simplejwt.views import TokenObtainPairView
from .serializers import MyTokenObtainPairSerializer
class MyTokenObtainPairView(TokenObtainPairView):
serializer_class = MyTokenObtainPairSerializer

View File

@@ -0,0 +1,27 @@
from rest_framework_simplejwt.serializers import TokenObtainPairSerializer
class MyTokenObtainPairSerializer(TokenObtainPairSerializer):
@classmethod
def get_token(cls, user):
token = super().get_token(user)
# Adicione campos personalizados ao token (payload) se desejar
token['username'] = user.username
token['groups'] = list(user.groups.values_list('name', flat=True))
return token
def validate(self, attrs):
data = super().validate(attrs)
# Adicione informações do usuário na resposta do JSON
data['user'] = {
'id': self.user.id,
'username': self.user.username,
'email': self.user.email,
'first_name': self.user.first_name,
'last_name': self.user.last_name,
'groups': list(self.user.groups.values_list('name', flat=True))
}
return data

6
gestaoRaul/main.py Normal file
View File

@@ -0,0 +1,6 @@
def main():
print("Hello from gestaoraul!")
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,8 @@
from rest_framework import viewsets, permissions
from .models import Mesa
from .serializers import MesaSerializer
class MesaViewSet(viewsets.ModelViewSet):
queryset = Mesa.objects.all()
serializer_class = MesaSerializer
permission_classes = [permissions.IsAuthenticated]

View File

@@ -0,0 +1,7 @@
from rest_framework import serializers
from .models import Mesa
class MesaSerializer(serializers.ModelSerializer):
class Meta:
model = Mesa
fields = '__all__'

View File

@@ -0,0 +1,47 @@
from rest_framework import viewsets, permissions, status
from rest_framework.decorators import action
from rest_framework.response import Response
from .models import Order
from .serializers import OrderSerializer
from django.utils import timezone
class OrderViewSet(viewsets.ModelViewSet):
queryset = Order.objects.all()
serializer_field = OrderSerializer
permission_classes = [permissions.IsAuthenticated]
def get_queryset(self):
# Filtrar pedidos das últimas 15 horas como na view original
fifteen_hours_ago = timezone.now() - timezone.timedelta(hours=15)
return Order.objects.filter(queue__gte=fifteen_hours_ago).order_by('-queue')
def get_serializer_class(self):
return OrderSerializer
@action(detail=True, methods=['post'])
def preparing(self, request, pk=None):
order = self.get_object()
order.preparing = timezone.now()
order.save()
return Response(OrderSerializer(order).data)
@action(detail=True, methods=['post'])
def finish(self, request, pk=None):
order = self.get_object()
order.finished = timezone.now()
order.save()
return Response(OrderSerializer(order).data)
@action(detail=True, methods=['post'])
def deliver(self, request, pk=None):
order = self.get_object()
order.delivered = timezone.now()
order.save()
return Response(OrderSerializer(order).data)
@action(detail=True, methods=['post'])
def cancel(self, request, pk=None):
order = self.get_object()
order.canceled = timezone.now()
order.save()
return Response(OrderSerializer(order).data)

View File

@@ -0,0 +1,31 @@
from rest_framework import serializers
from .models import Order
class OrderSerializer(serializers.ModelSerializer):
status = serializers.SerializerMethodField()
product_name = serializers.ReadOnlyField(source='id_product.name')
comanda_name = serializers.ReadOnlyField(source='id_comanda.name')
mesa_name = serializers.ReadOnlyField(source='id_comanda.mesa.name')
class Meta:
model = Order
fields = [
'id', 'id_product', 'product_name', 'id_comanda', 'comanda_name',
'mesa_name', 'obs', 'queue', 'preparing', 'finished',
'delivered', 'canceled', 'status'
]
extra_kwargs = {
'queue': {'read_only': True},
'status': {'read_only': True},
}
def get_status(self, obj):
if obj.delivered:
return 'Entregue'
if obj.finished:
return 'Pronto'
if obj.preparing:
return 'Preparando'
if obj.canceled:
return 'Cancelado'
return 'Em espera'

View File

@@ -0,0 +1,12 @@
from rest_framework import viewsets, permissions
from .models import Payments
from .serializers import PaymentsSerializer
class PaymentsViewSet(viewsets.ModelViewSet):
queryset = Payments.objects.all()
serializer_class = PaymentsSerializer
permission_classes = [permissions.IsAuthenticated]
def get_queryset(self):
# Opcionalmente filtrar pagamentos recentes
return Payments.objects.all().order_by('-datetime')

View File

@@ -0,0 +1,15 @@
from rest_framework import serializers
from .models import Payments
class PaymentsSerializer(serializers.ModelSerializer):
type_pay_name = serializers.ReadOnlyField(source='type_pay.name')
comanda_name = serializers.ReadOnlyField(source='comanda.name')
client_name = serializers.ReadOnlyField(source='client.name')
class Meta:
model = Payments
fields = [
'id', 'value', 'type_pay', 'type_pay_name',
'comanda', 'comanda_name', 'client', 'client_name',
'description', 'datetime'
]

Some files were not shown because too many files have changed in this diff Show More