remove: django.
This commit is contained in:
parent
27cbeca6cb
commit
88b262e9cf
@ -1,12 +0,0 @@
|
|||||||
from django.contrib import admin
|
|
||||||
from .models import (
|
|
||||||
Customer, Sale, SaleLine, Product, ProductCategory, Payment,
|
|
||||||
ReconciliationJar)
|
|
||||||
|
|
||||||
admin.site.register(Customer)
|
|
||||||
admin.site.register(Sale)
|
|
||||||
admin.site.register(SaleLine)
|
|
||||||
admin.site.register(Product)
|
|
||||||
admin.site.register(ProductCategory)
|
|
||||||
admin.site.register(Payment)
|
|
||||||
admin.site.register(ReconciliationJar)
|
|
@ -1,146 +0,0 @@
|
|||||||
from rest_framework import viewsets
|
|
||||||
from rest_framework.response import Response
|
|
||||||
from rest_framework.status import HTTP_400_BAD_REQUEST
|
|
||||||
from rest_framework.views import APIView
|
|
||||||
from rest_framework.pagination import PageNumberPagination
|
|
||||||
|
|
||||||
from .models import Sale, SaleLine, Customer, Product, ReconciliationJar, PaymentMethods, AdminCode
|
|
||||||
from .serializers import SaleSerializer, ProductSerializer, CustomerSerializer, ReconciliationJarSerializer, PaymentMethodSerializer, SaleForRenconciliationSerializer, SaleSummarySerializer
|
|
||||||
|
|
||||||
from decimal import Decimal
|
|
||||||
import json
|
|
||||||
|
|
||||||
|
|
||||||
class Pagination(PageNumberPagination):
|
|
||||||
page_size = 10
|
|
||||||
page_size_query_param = 'page_size'
|
|
||||||
|
|
||||||
|
|
||||||
class SaleView(viewsets.ModelViewSet):
|
|
||||||
queryset = Sale.objects.all()
|
|
||||||
serializer_class = SaleSerializer
|
|
||||||
|
|
||||||
def create(self, request):
|
|
||||||
data = request.data
|
|
||||||
customer = Customer.objects.get(pk=data['customer'])
|
|
||||||
date = data['date']
|
|
||||||
lines = data['saleline_set']
|
|
||||||
payment_method = data['payment_method']
|
|
||||||
sale = Sale.objects.create(
|
|
||||||
customer=customer,
|
|
||||||
date=date,
|
|
||||||
payment_method=payment_method
|
|
||||||
)
|
|
||||||
|
|
||||||
for line in lines:
|
|
||||||
product = Product.objects.get(pk=line['product'])
|
|
||||||
quantity = line['quantity']
|
|
||||||
unit_price = line['unit_price']
|
|
||||||
SaleLine.objects.create(
|
|
||||||
sale=sale,
|
|
||||||
product=product,
|
|
||||||
quantity=quantity,
|
|
||||||
unit_price=unit_price
|
|
||||||
)
|
|
||||||
|
|
||||||
return Response(
|
|
||||||
{'id': sale.id, 'message': 'Venta creada con exito'},
|
|
||||||
status=201
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class ProductView(viewsets.ModelViewSet):
|
|
||||||
queryset = Product.objects.all()
|
|
||||||
serializer_class = ProductSerializer
|
|
||||||
|
|
||||||
|
|
||||||
class CustomerView(viewsets.ModelViewSet):
|
|
||||||
queryset = Customer.objects.all()
|
|
||||||
serializer_class = CustomerSerializer
|
|
||||||
|
|
||||||
|
|
||||||
class ReconciliateJarView(APIView):
|
|
||||||
def post(self, request):
|
|
||||||
data = request.data
|
|
||||||
cash_purchases_id = data.get('cash_purchases')
|
|
||||||
serializer = ReconciliationJarSerializer(data=data)
|
|
||||||
if serializer.is_valid():
|
|
||||||
cash_purchases = Sale.objects.filter(pk__in=cash_purchases_id)
|
|
||||||
if not self._is_valid_total(cash_purchases, data.get('total_cash_purchases')):
|
|
||||||
return Response(
|
|
||||||
{'error': 'total_cash_purchases not equal to sum of all purchases.'},
|
|
||||||
status=HTTP_400_BAD_REQUEST
|
|
||||||
)
|
|
||||||
reconciliation = serializer.save()
|
|
||||||
other_purchases = self._get_other_purchases(data.get('other_totals'))
|
|
||||||
|
|
||||||
self._link_purchases(reconciliation, cash_purchases, other_purchases)
|
|
||||||
return Response({'id': reconciliation.id})
|
|
||||||
return Response(serializer.errors, status=HTTP_400_BAD_REQUEST)
|
|
||||||
|
|
||||||
def get(self, request):
|
|
||||||
reconciliations = ReconciliationJar.objects.all()
|
|
||||||
serializer = ReconciliationJarSerializer(reconciliations, many=True)
|
|
||||||
return Response(serializer.data)
|
|
||||||
|
|
||||||
def _is_valid_total(self, purchases, total):
|
|
||||||
calculated_total = sum(p.get_total() for p in purchases)
|
|
||||||
return calculated_total == Decimal(total)
|
|
||||||
|
|
||||||
def _get_other_purchases(self, other_totals):
|
|
||||||
if not other_totals:
|
|
||||||
return []
|
|
||||||
purchases = []
|
|
||||||
for method in other_totals:
|
|
||||||
purchases.extend(other_totals[method]['purchases'])
|
|
||||||
if purchases:
|
|
||||||
return Sale.objects.filter(pk__in=purchases)
|
|
||||||
return []
|
|
||||||
|
|
||||||
def _link_purchases(self, reconciliation, cash_purchases, other_purchases):
|
|
||||||
for purchase in cash_purchases:
|
|
||||||
purchase.reconciliation = reconciliation
|
|
||||||
purchase.clean()
|
|
||||||
purchase.save()
|
|
||||||
|
|
||||||
for purchase in other_purchases:
|
|
||||||
purchase.reconciliation = reconciliation
|
|
||||||
purchase.clean()
|
|
||||||
purchase.save()
|
|
||||||
|
|
||||||
|
|
||||||
class PaymentMethodView(APIView):
|
|
||||||
def get(self, request):
|
|
||||||
serializer = PaymentMethodSerializer(PaymentMethods.choices, many=True)
|
|
||||||
return Response(serializer.data)
|
|
||||||
|
|
||||||
|
|
||||||
class SalesForReconciliationView(APIView):
|
|
||||||
def get(self, request):
|
|
||||||
sales = Sale.objects.filter(reconciliation=None)
|
|
||||||
grouped_sales = {}
|
|
||||||
|
|
||||||
for sale in sales:
|
|
||||||
if sale.payment_method not in grouped_sales.keys():
|
|
||||||
grouped_sales[sale.payment_method] = []
|
|
||||||
serializer = SaleForRenconciliationSerializer(sale)
|
|
||||||
grouped_sales[sale.payment_method].append(serializer.data)
|
|
||||||
|
|
||||||
return Response(grouped_sales)
|
|
||||||
|
|
||||||
class SaleSummary(APIView):
|
|
||||||
def get(self, request, id):
|
|
||||||
sale = Sale.objects.get(pk=id)
|
|
||||||
serializer = SaleSummarySerializer(sale)
|
|
||||||
return Response(serializer.data)
|
|
||||||
|
|
||||||
class AdminCodeValidateView(APIView):
|
|
||||||
def get(self, request, code):
|
|
||||||
codes = AdminCode.objects.filter(value=code)
|
|
||||||
return Response({'validCode': bool(codes)})
|
|
||||||
|
|
||||||
|
|
||||||
class ReconciliateJarModelView(viewsets.ModelViewSet):
|
|
||||||
queryset = ReconciliationJar.objects.all().order_by('-date_time')
|
|
||||||
pagination_class = Pagination
|
|
||||||
serializer_class = ReconciliationJarSerializer
|
|
@ -1,6 +0,0 @@
|
|||||||
from django.apps import AppConfig
|
|
||||||
|
|
||||||
|
|
||||||
class DonConfiaoConfig(AppConfig):
|
|
||||||
default_auto_field = 'django.db.models.BigAutoField'
|
|
||||||
name = 'don_confiao'
|
|
@ -1,4 +0,0 @@
|
|||||||
nombre,correo,telefono
|
|
||||||
Alejandro Ayala,mono@disroot.org,3232321
|
|
||||||
Mono Francisco,pablo@onecluster.org,321312312
|
|
||||||
Pablo Bolivar,alejo@onecluster.org,3243242
|
|
|
@ -1,4 +0,0 @@
|
|||||||
"producto","unidad","precio","categorias"
|
|
||||||
"Aceite","Unidad", 50000,"Aceites&Alimentos"
|
|
||||||
"Café","Unidad", 14000,"Cafes&Alimentos"
|
|
||||||
"Arroz","Unidad", 7000,"Cafes&Alimentos"
|
|
|
@ -1,4 +0,0 @@
|
|||||||
"producto","unidad","precio","categorias"
|
|
||||||
"Aceite","Unidad", 50000,"Aceites&Alimentos"
|
|
||||||
"Café","Unidad", 15000,"Cafes&Alimentos"
|
|
||||||
"Arroz","Unidad", 6000,"Alimentos&Granos"
|
|
|
@ -1 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
@ -1,66 +0,0 @@
|
|||||||
from django import forms
|
|
||||||
from django.forms.models import inlineformset_factory
|
|
||||||
|
|
||||||
from django.forms.widgets import DateInput, DateTimeInput
|
|
||||||
|
|
||||||
from .models import Sale, SaleLine, PaymentMethods
|
|
||||||
|
|
||||||
readonly_number_widget = forms.NumberInput(attrs={'readonly': 'readonly'})
|
|
||||||
|
|
||||||
|
|
||||||
class ImportProductsForm(forms.Form):
|
|
||||||
csv_file = forms.FileField()
|
|
||||||
|
|
||||||
|
|
||||||
class ImportCustomersForm(forms.Form):
|
|
||||||
csv_file = forms.FileField()
|
|
||||||
|
|
||||||
|
|
||||||
class PurchaseForm(forms.ModelForm):
|
|
||||||
class Meta:
|
|
||||||
model = Sale
|
|
||||||
fields = [
|
|
||||||
"customer",
|
|
||||||
"date",
|
|
||||||
"phone",
|
|
||||||
"description",
|
|
||||||
]
|
|
||||||
widgets = {
|
|
||||||
'date': DateInput(attrs={'type': 'date'})
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class PurchaseLineForm(forms.ModelForm):
|
|
||||||
class Meta:
|
|
||||||
model = SaleLine
|
|
||||||
fields = [
|
|
||||||
"product",
|
|
||||||
"quantity",
|
|
||||||
"unit_price",
|
|
||||||
"description",
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class PurchaseSummaryForm(forms.Form):
|
|
||||||
quantity_lines = forms.IntegerField(
|
|
||||||
widget=readonly_number_widget
|
|
||||||
)
|
|
||||||
quantity_products = forms.IntegerField(
|
|
||||||
widget=readonly_number_widget
|
|
||||||
)
|
|
||||||
ammount = forms.DecimalField(
|
|
||||||
max_digits=10,
|
|
||||||
decimal_places=2,
|
|
||||||
widget=readonly_number_widget
|
|
||||||
)
|
|
||||||
payment_method = forms.ChoiceField(
|
|
||||||
choices=[(PaymentMethods.CASH, PaymentMethods.CASH)],
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
SaleLineFormSet = inlineformset_factory(
|
|
||||||
Sale,
|
|
||||||
SaleLine,
|
|
||||||
extra=1,
|
|
||||||
fields='__all__'
|
|
||||||
)
|
|
@ -1,20 +0,0 @@
|
|||||||
# Generated by Django 5.0.6 on 2024-06-22 15:25
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
initial = True
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.CreateModel(
|
|
||||||
name='Sale',
|
|
||||||
fields=[
|
|
||||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
]
|
|
@ -1,19 +0,0 @@
|
|||||||
# Generated by Django 5.0.7 on 2024-08-03 15:01
|
|
||||||
|
|
||||||
import django.db.models.deletion
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('don_confiao', '0001_initial'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='saleline',
|
|
||||||
name='product',
|
|
||||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='don_confiao.product'),
|
|
||||||
),
|
|
||||||
]
|
|
@ -1,37 +0,0 @@
|
|||||||
# Generated by Django 5.0.6 on 2024-06-22 15:33
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('don_confiao', '0001_initial'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='sale',
|
|
||||||
name='customer',
|
|
||||||
field=models.CharField(default='', max_length=100),
|
|
||||||
preserve_default=False,
|
|
||||||
),
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='sale',
|
|
||||||
name='date',
|
|
||||||
field=models.DateField(default='2024-06-22', verbose_name='Date'),
|
|
||||||
preserve_default=False,
|
|
||||||
),
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='sale',
|
|
||||||
name='description',
|
|
||||||
field=models.CharField(default='', max_length=255),
|
|
||||||
preserve_default=False,
|
|
||||||
),
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='sale',
|
|
||||||
name='phone',
|
|
||||||
field=models.CharField(default='', max_length=13),
|
|
||||||
preserve_default=False,
|
|
||||||
),
|
|
||||||
]
|
|
@ -1,19 +0,0 @@
|
|||||||
# Generated by Django 5.0.6 on 2024-06-22 15:50
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('don_confiao', '0002_sale_customer_sale_date_sale_description_sale_phone'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.CreateModel(
|
|
||||||
name='SaleLine',
|
|
||||||
fields=[
|
|
||||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
]
|
|
@ -1,18 +0,0 @@
|
|||||||
# Generated by Django 5.0.6 on 2024-06-22 16:00
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('don_confiao', '0003_saleline'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='saleline',
|
|
||||||
name='quantity',
|
|
||||||
field=models.IntegerField(null=True),
|
|
||||||
),
|
|
||||||
]
|
|
@ -1,20 +0,0 @@
|
|||||||
# Generated by Django 5.0.6 on 2024-06-22 16:05
|
|
||||||
|
|
||||||
import django.db.models.deletion
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('don_confiao', '0004_saleline_quantity'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='saleline',
|
|
||||||
name='sale',
|
|
||||||
field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to='don_confiao.sale'),
|
|
||||||
preserve_default=False,
|
|
||||||
),
|
|
||||||
]
|
|
@ -1,31 +0,0 @@
|
|||||||
# Generated by Django 5.0.6 on 2024-06-22 18:56
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('don_confiao', '0005_saleline_sale'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='saleline',
|
|
||||||
name='description',
|
|
||||||
field=models.CharField(default='', max_length=255),
|
|
||||||
preserve_default=False,
|
|
||||||
),
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='saleline',
|
|
||||||
name='product',
|
|
||||||
field=models.CharField(default='', max_length=100),
|
|
||||||
preserve_default=False,
|
|
||||||
),
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='saleline',
|
|
||||||
name='unit_price',
|
|
||||||
field=models.DecimalField(decimal_places=2, default=0.0, max_digits=9),
|
|
||||||
preserve_default=False,
|
|
||||||
),
|
|
||||||
]
|
|
@ -1,18 +0,0 @@
|
|||||||
# Generated by Django 5.0.6 on 2024-06-22 19:11
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('don_confiao', '0006_saleline_description_saleline_product_and_more'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='sale',
|
|
||||||
name='description',
|
|
||||||
field=models.CharField(blank=True, max_length=255, null=True),
|
|
||||||
),
|
|
||||||
]
|
|
@ -1,23 +0,0 @@
|
|||||||
# Generated by Django 5.0.6 on 2024-06-22 19:16
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('don_confiao', '0007_alter_sale_description'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='sale',
|
|
||||||
name='phone',
|
|
||||||
field=models.CharField(blank=True, max_length=13, null=True),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='saleline',
|
|
||||||
name='description',
|
|
||||||
field=models.CharField(blank=True, max_length=255, null=True),
|
|
||||||
),
|
|
||||||
]
|
|
@ -1,30 +0,0 @@
|
|||||||
# 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')),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
]
|
|
@ -1,18 +0,0 @@
|
|||||||
# 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),
|
|
||||||
),
|
|
||||||
]
|
|
@ -1,18 +0,0 @@
|
|||||||
# 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,23 +0,0 @@
|
|||||||
# Generated by Django 5.0.6 on 2024-07-13 14:50
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('don_confiao', '0011_alter_product_name'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.CreateModel(
|
|
||||||
name='Payment',
|
|
||||||
fields=[
|
|
||||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
|
||||||
('date_time', models.DateTimeField()),
|
|
||||||
('type_payment', models.CharField(choices=[('CASH', 'Cash'), ('CONFIAR', 'Confiar'), ('BANCOLOMBIA', 'Bancolombia')], default='CASH', max_length=30)),
|
|
||||||
('mount', models.DecimalField(decimal_places=2, max_digits=9)),
|
|
||||||
('description', models.CharField(blank=True, max_length=255, null=True)),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
]
|
|
@ -1,18 +0,0 @@
|
|||||||
# Generated by Django 5.0.6 on 2024-07-13 15:56
|
|
||||||
|
|
||||||
from django.db import migrations
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('don_confiao', '0012_payment'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.RenameField(
|
|
||||||
model_name='payment',
|
|
||||||
old_name='mount',
|
|
||||||
new_name='amount',
|
|
||||||
),
|
|
||||||
]
|
|
@ -1,27 +0,0 @@
|
|||||||
# Generated by Django 5.0.6 on 2024-07-13 16:35
|
|
||||||
|
|
||||||
import django.db.models.deletion
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('don_confiao', '0013_rename_mount_payment_amount'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.CreateModel(
|
|
||||||
name='ReconciliationJar',
|
|
||||||
fields=[
|
|
||||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
|
||||||
('date_time', models.DateTimeField()),
|
|
||||||
('description', models.CharField(blank=True, max_length=255, null=True)),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='payment',
|
|
||||||
name='reconciliation_jar',
|
|
||||||
field=models.ForeignKey(default=None, on_delete=django.db.models.deletion.RESTRICT, to='don_confiao.reconciliationjar'),
|
|
||||||
),
|
|
||||||
]
|
|
@ -1,19 +0,0 @@
|
|||||||
# Generated by Django 5.0.6 on 2024-07-13 16:36
|
|
||||||
|
|
||||||
import django.db.models.deletion
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('don_confiao', '0014_reconciliationjar_payment_reconciliation_jar'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='payment',
|
|
||||||
name='reconciliation_jar',
|
|
||||||
field=models.ForeignKey(default=None, null=True, on_delete=django.db.models.deletion.RESTRICT, to='don_confiao.reconciliationjar'),
|
|
||||||
),
|
|
||||||
]
|
|
@ -1,37 +0,0 @@
|
|||||||
# Generated by Django 5.0.6 on 2024-07-13 18:10
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('don_confiao', '0015_alter_payment_reconciliation_jar'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='reconciliationjar',
|
|
||||||
name='cash_discrepancy',
|
|
||||||
field=models.DecimalField(decimal_places=2, default=0, max_digits=9),
|
|
||||||
preserve_default=False,
|
|
||||||
),
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='reconciliationjar',
|
|
||||||
name='cash_float',
|
|
||||||
field=models.DecimalField(decimal_places=2, default=0, max_digits=9),
|
|
||||||
preserve_default=False,
|
|
||||||
),
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='reconciliationjar',
|
|
||||||
name='cash_taken',
|
|
||||||
field=models.DecimalField(decimal_places=2, default=0, max_digits=9),
|
|
||||||
preserve_default=False,
|
|
||||||
),
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='reconciliationjar',
|
|
||||||
name='reconciler',
|
|
||||||
field=models.CharField(default='Jorge', max_length=255),
|
|
||||||
preserve_default=False,
|
|
||||||
),
|
|
||||||
]
|
|
@ -1,18 +0,0 @@
|
|||||||
# Generated by Django 5.0.6 on 2024-07-13 18:15
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('don_confiao', '0016_reconciliationjar_cash_discrepancy_and_more'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='reconciliationjar',
|
|
||||||
name='draft',
|
|
||||||
field=models.BooleanField(default=False),
|
|
||||||
),
|
|
||||||
]
|
|
@ -1,18 +0,0 @@
|
|||||||
# Generated by Django 5.0.6 on 2024-07-13 18:19
|
|
||||||
|
|
||||||
from django.db import migrations
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('don_confiao', '0017_reconciliationjar_draft'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.RenameField(
|
|
||||||
model_name='reconciliationjar',
|
|
||||||
old_name='draft',
|
|
||||||
new_name='valid',
|
|
||||||
),
|
|
||||||
]
|
|
@ -1,18 +0,0 @@
|
|||||||
# Generated by Django 5.0.6 on 2024-07-13 19:56
|
|
||||||
|
|
||||||
from django.db import migrations
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('don_confiao', '0018_rename_draft_reconciliationjar_valid'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.RenameField(
|
|
||||||
model_name='reconciliationjar',
|
|
||||||
old_name='valid',
|
|
||||||
new_name='is_valid',
|
|
||||||
),
|
|
||||||
]
|
|
@ -1,17 +0,0 @@
|
|||||||
# Generated by Django 5.0.6 on 2024-07-13 20:13
|
|
||||||
|
|
||||||
from django.db import migrations
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('don_confiao', '0019_rename_valid_reconciliationjar_is_valid'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name='reconciliationjar',
|
|
||||||
name='cash_float',
|
|
||||||
),
|
|
||||||
]
|
|
@ -1,18 +0,0 @@
|
|||||||
# Generated by Django 5.0.6 on 2024-07-13 22:16
|
|
||||||
|
|
||||||
from django.db import migrations
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('don_confiao', '0020_remove_reconciliationjar_cash_float'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.RenameField(
|
|
||||||
model_name='reconciliationjar',
|
|
||||||
old_name='reconciler',
|
|
||||||
new_name='reconcilier',
|
|
||||||
),
|
|
||||||
]
|
|
@ -1,19 +0,0 @@
|
|||||||
# Generated by Django 5.0.6 on 2024-07-13 22:39
|
|
||||||
|
|
||||||
import django.db.models.deletion
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('don_confiao', '0021_rename_reconciler_reconciliationjar_reconcilier'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='payment',
|
|
||||||
name='reconciliation_jar',
|
|
||||||
field=models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.RESTRICT, to='don_confiao.reconciliationjar'),
|
|
||||||
),
|
|
||||||
]
|
|
@ -1,14 +0,0 @@
|
|||||||
# Generated by Django 5.0.7 on 2024-08-03 15:04
|
|
||||||
|
|
||||||
from django.db import migrations
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('don_confiao', '0002_alter_saleline_product'),
|
|
||||||
('don_confiao', '0022_alter_payment_reconciliation_jar'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
]
|
|
@ -1,18 +0,0 @@
|
|||||||
# Generated by Django 5.0.7 on 2024-08-03 15:14
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('don_confiao', '0023_merge_20240803_1504'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='product',
|
|
||||||
name='name',
|
|
||||||
field=models.CharField(max_length=100),
|
|
||||||
),
|
|
||||||
]
|
|
@ -1,21 +0,0 @@
|
|||||||
# Generated by Django 5.0.7 on 2024-08-03 15:40
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('don_confiao', '0024_alter_product_name'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.CreateModel(
|
|
||||||
name='Customer',
|
|
||||||
fields=[
|
|
||||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
|
||||||
('name', models.CharField(max_length=100)),
|
|
||||||
('address', models.CharField(max_length=100)),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
]
|
|
@ -1,19 +0,0 @@
|
|||||||
# Generated by Django 5.0.7 on 2024-08-03 15:55
|
|
||||||
|
|
||||||
import django.db.models.deletion
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('don_confiao', '0025_customer'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='sale',
|
|
||||||
name='customer',
|
|
||||||
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='don_confiao.customer'),
|
|
||||||
),
|
|
||||||
]
|
|
@ -1,18 +0,0 @@
|
|||||||
# Generated by Django 5.0.7 on 2024-08-03 16:32
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('don_confiao', '0026_alter_sale_customer'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='product',
|
|
||||||
name='name',
|
|
||||||
field=models.CharField(max_length=100, unique=True),
|
|
||||||
),
|
|
||||||
]
|
|
@ -1,18 +0,0 @@
|
|||||||
# Generated by Django 5.0.6 on 2024-08-17 14:22
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('don_confiao', '0027_alter_product_name'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='customer',
|
|
||||||
name='address',
|
|
||||||
field=models.CharField(blank=True, max_length=100, null=True),
|
|
||||||
),
|
|
||||||
]
|
|
@ -1,18 +0,0 @@
|
|||||||
# Generated by Django 5.0.6 on 2024-08-17 19:28
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('don_confiao', '0028_alter_customer_address'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='customer',
|
|
||||||
name='name',
|
|
||||||
field=models.CharField(default=None, max_length=100),
|
|
||||||
),
|
|
||||||
]
|
|
@ -1,22 +0,0 @@
|
|||||||
# Generated by Django 5.0.6 on 2024-08-17 21:00
|
|
||||||
|
|
||||||
import django.db.models.deletion
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('don_confiao', '0029_alter_customer_name'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.CreateModel(
|
|
||||||
name='PaymentSale',
|
|
||||||
fields=[
|
|
||||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
|
||||||
('payment', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='don_confiao.payment')),
|
|
||||||
('sale', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='don_confiao.sale')),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
]
|
|
@ -1,23 +0,0 @@
|
|||||||
# Generated by Django 5.0.6 on 2024-10-26 22:01
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('don_confiao', '0030_paymentsale'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.RenameField(
|
|
||||||
model_name='customer',
|
|
||||||
old_name='address',
|
|
||||||
new_name='email',
|
|
||||||
),
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='customer',
|
|
||||||
name='phone',
|
|
||||||
field=models.CharField(blank=True, max_length=100, null=True),
|
|
||||||
),
|
|
||||||
]
|
|
@ -1,18 +0,0 @@
|
|||||||
# Generated by Django 5.0.6 on 2024-10-26 22:21
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('don_confiao', '0031_rename_address_customer_email_customer_phone'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='customer',
|
|
||||||
name='address',
|
|
||||||
field=models.CharField(blank=True, max_length=100, null=True),
|
|
||||||
),
|
|
||||||
]
|
|
@ -1,18 +0,0 @@
|
|||||||
# Generated by Django 5.0.6 on 2024-11-09 17:55
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('don_confiao', '0032_customer_address'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='sale',
|
|
||||||
name='payment_method',
|
|
||||||
field=models.CharField(choices=[('CASH', 'Cash'), ('CONFIAR', 'Confiar'), ('BANCOLOMBIA', 'Bancolombia')], default='CASH', max_length=30),
|
|
||||||
),
|
|
||||||
]
|
|
@ -1,28 +0,0 @@
|
|||||||
# Generated by Django 5.0.6 on 2024-11-16 20:55
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('don_confiao', '0033_sale_payment_method'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='payment',
|
|
||||||
name='type_payment',
|
|
||||||
field=models.CharField(choices=[('CASH', 'Efectivo'), ('CONFIAR', 'Confiar'), ('BANCOLOMBIA', 'Bancolombia')], default='CASH', max_length=30),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='sale',
|
|
||||||
name='date',
|
|
||||||
field=models.DateTimeField(verbose_name='Date'),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='sale',
|
|
||||||
name='payment_method',
|
|
||||||
field=models.CharField(choices=[('CASH', 'Efectivo'), ('CONFIAR', 'Confiar'), ('BANCOLOMBIA', 'Bancolombia')], default='CASH', max_length=30),
|
|
||||||
),
|
|
||||||
]
|
|
@ -1,29 +0,0 @@
|
|||||||
# Generated by Django 5.0.6 on 2024-11-18 03:16
|
|
||||||
|
|
||||||
import django.db.models.deletion
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('don_confiao', '0033_sale_payment_method'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='sale',
|
|
||||||
name='reconciliation',
|
|
||||||
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.RESTRICT, related_name='Sales', to='don_confiao.reconciliationjar'),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='payment',
|
|
||||||
name='type_payment',
|
|
||||||
field=models.CharField(choices=[('CASH', 'Efectivo'), ('CONFIAR', 'Confiar'), ('BANCOLOMBIA', 'Bancolombia')], default='CASH', max_length=30),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='sale',
|
|
||||||
name='payment_method',
|
|
||||||
field=models.CharField(choices=[('CASH', 'Efectivo'), ('CONFIAR', 'Confiar'), ('BANCOLOMBIA', 'Bancolombia')], default='CASH', max_length=30),
|
|
||||||
),
|
|
||||||
]
|
|
@ -1,19 +0,0 @@
|
|||||||
# Generated by Django 5.0.6 on 2024-12-03 02:55
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('don_confiao', '0034_sale_reconciliation_alter_payment_type_payment_and_more'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='reconciliationjar',
|
|
||||||
name='total_cash_purchases',
|
|
||||||
field=models.DecimalField(decimal_places=2, default=0, max_digits=9),
|
|
||||||
preserve_default=False,
|
|
||||||
),
|
|
||||||
]
|
|
@ -1,14 +0,0 @@
|
|||||||
# Generated by Django 5.0.6 on 2024-12-28 22:12
|
|
||||||
|
|
||||||
from django.db import migrations
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('don_confiao', '0034_alter_payment_type_payment_alter_sale_date_and_more'),
|
|
||||||
('don_confiao', '0035_reconciliationjar_total_cash_purchases'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
]
|
|
@ -1,20 +0,0 @@
|
|||||||
# Generated by Django 5.0.6 on 2025-01-11 23:52
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('don_confiao', '0036_merge_20241228_2212'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.CreateModel(
|
|
||||||
name='AdminCode',
|
|
||||||
fields=[
|
|
||||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
|
||||||
('value', models.CharField(max_length=255)),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
]
|
|
@ -1,204 +0,0 @@
|
|||||||
from django.db import models
|
|
||||||
from django.utils.translation import gettext_lazy as _
|
|
||||||
from django.core.exceptions import ValidationError
|
|
||||||
|
|
||||||
from decimal import Decimal
|
|
||||||
from datetime import datetime
|
|
||||||
|
|
||||||
|
|
||||||
class PaymentMethods(models.TextChoices):
|
|
||||||
CASH = 'CASH', _('Efectivo')
|
|
||||||
CONFIAR = 'CONFIAR', _('Confiar')
|
|
||||||
BANCOLOMBIA = 'BANCOLOMBIA', _('Bancolombia')
|
|
||||||
|
|
||||||
|
|
||||||
class Customer(models.Model):
|
|
||||||
name = models.CharField(max_length=100, default=None, null=False, blank=False)
|
|
||||||
address = models.CharField(max_length=100, null=True, blank=True)
|
|
||||||
email = models.CharField(max_length=100, null=True, blank=True)
|
|
||||||
phone = models.CharField(max_length=100, null=True, blank=True)
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return self.name
|
|
||||||
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def to_list(cls):
|
|
||||||
products_list = []
|
|
||||||
all_products = cls.objects.all()
|
|
||||||
for product in all_products:
|
|
||||||
rproduct = {
|
|
||||||
"id": product.id,
|
|
||||||
"name": product.name,
|
|
||||||
"price_list": product.price,
|
|
||||||
"uom": product.measuring_unit,
|
|
||||||
"categories": [c.name for c in product.categories.all()]
|
|
||||||
}
|
|
||||||
products_list.append(rproduct)
|
|
||||||
return products_list
|
|
||||||
|
|
||||||
|
|
||||||
class ReconciliationJar(models.Model):
|
|
||||||
is_valid = models.BooleanField(default=False)
|
|
||||||
date_time = models.DateTimeField()
|
|
||||||
description = models.CharField(max_length=255, null=True, blank=True)
|
|
||||||
reconcilier = models.CharField(max_length=255, null=False, blank=False)
|
|
||||||
cash_taken = models.DecimalField(max_digits=9, decimal_places=2)
|
|
||||||
cash_discrepancy = models.DecimalField(max_digits=9, decimal_places=2)
|
|
||||||
total_cash_purchases = models.DecimalField(max_digits=9, decimal_places=2)
|
|
||||||
|
|
||||||
def clean(self):
|
|
||||||
self._validate_taken_ammount()
|
|
||||||
|
|
||||||
def add_payments(self, payments):
|
|
||||||
for payment in payments:
|
|
||||||
self.payment_set.add(payment)
|
|
||||||
self.is_valid = True
|
|
||||||
|
|
||||||
def _validate_taken_ammount(self):
|
|
||||||
ammount_cash = self.cash_taken + self.cash_discrepancy
|
|
||||||
if not self.total_cash_purchases == ammount_cash:
|
|
||||||
raise ValidationError(
|
|
||||||
{"cash_taken": _("The taken ammount has discrepancy.")}
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class Sale(models.Model):
|
|
||||||
customer = models.ForeignKey(Customer, on_delete=models.PROTECT)
|
|
||||||
date = models.DateTimeField("Date")
|
|
||||||
phone = models.CharField(max_length=13, null=True, blank=True)
|
|
||||||
description = models.CharField(max_length=255, null=True, blank=True)
|
|
||||||
payment_method = models.CharField(
|
|
||||||
max_length=30,
|
|
||||||
choices=PaymentMethods.choices,
|
|
||||||
default=PaymentMethods.CASH,
|
|
||||||
blank=False,
|
|
||||||
null=False
|
|
||||||
)
|
|
||||||
reconciliation = models.ForeignKey(
|
|
||||||
ReconciliationJar,
|
|
||||||
on_delete=models.RESTRICT,
|
|
||||||
related_name='Sales',
|
|
||||||
null=True
|
|
||||||
)
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return f"{self.date} {self.customer}"
|
|
||||||
|
|
||||||
def get_total(self):
|
|
||||||
lines = self.saleline_set.all()
|
|
||||||
return sum([l.quantity * l.unit_price for l in lines])
|
|
||||||
|
|
||||||
def clean(self):
|
|
||||||
if self.payment_method not in PaymentMethods.values:
|
|
||||||
raise ValidationError({'payment_method': "Invalid payment method"})
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def sale_header_csv(cls):
|
|
||||||
sale_header_csv = [field.name for field in cls._meta.fields]
|
|
||||||
|
|
||||||
return sale_header_csv
|
|
||||||
|
|
||||||
|
|
||||||
class SaleLine(models.Model):
|
|
||||||
|
|
||||||
sale = models.ForeignKey(Sale, on_delete=models.CASCADE)
|
|
||||||
product = models.ForeignKey(Product, null=False, blank=False, on_delete=models.CASCADE)
|
|
||||||
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)
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return f"{self.sale} - {self.product}"
|
|
||||||
|
|
||||||
|
|
||||||
class ReconciliationJarSummary():
|
|
||||||
def __init__(self, payments):
|
|
||||||
self._validate_payments(payments)
|
|
||||||
self._payments = payments
|
|
||||||
|
|
||||||
def _validate_payments(self, payments):
|
|
||||||
pass
|
|
||||||
|
|
||||||
@property
|
|
||||||
def total(self):
|
|
||||||
return sum([p.amount for p in self.payments])
|
|
||||||
|
|
||||||
@property
|
|
||||||
def payments(self):
|
|
||||||
return self._payments
|
|
||||||
|
|
||||||
|
|
||||||
class Payment(models.Model):
|
|
||||||
date_time = models.DateTimeField()
|
|
||||||
type_payment = models.CharField(
|
|
||||||
max_length=30,
|
|
||||||
choices=PaymentMethods.choices,
|
|
||||||
default=PaymentMethods.CASH
|
|
||||||
)
|
|
||||||
amount = models.DecimalField(max_digits=9, decimal_places=2)
|
|
||||||
reconciliation_jar = models.ForeignKey(
|
|
||||||
ReconciliationJar,
|
|
||||||
null=True,
|
|
||||||
default=None,
|
|
||||||
blank=True,
|
|
||||||
on_delete=models.RESTRICT
|
|
||||||
)
|
|
||||||
description = models.CharField(max_length=255, null=True, blank=True)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_reconciliation_jar_summary(cls):
|
|
||||||
return ReconciliationJarSummary(
|
|
||||||
cls.objects.filter(
|
|
||||||
type_payment=PaymentMethods.CASH,
|
|
||||||
reconciliation_jar=None
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def total_payment_from_sale(cls, payment_method, sale):
|
|
||||||
payment = cls()
|
|
||||||
payment.date_time = datetime.today()
|
|
||||||
payment.type_payment = payment_method
|
|
||||||
payment.amount = sale.get_total()
|
|
||||||
payment.clean()
|
|
||||||
payment.save()
|
|
||||||
|
|
||||||
payment_sale = PaymentSale()
|
|
||||||
payment_sale.payment = payment
|
|
||||||
payment_sale.sale = sale
|
|
||||||
payment_sale.clean()
|
|
||||||
payment_sale.save()
|
|
||||||
|
|
||||||
|
|
||||||
class PaymentSale(models.Model):
|
|
||||||
payment = models.ForeignKey(Payment, on_delete=models.CASCADE)
|
|
||||||
sale = models.ForeignKey(Sale, on_delete=models.CASCADE)
|
|
||||||
|
|
||||||
|
|
||||||
class AdminCode(models.Model):
|
|
||||||
value = models.CharField(max_length=255, null=False, blank=False)
|
|
@ -1,103 +0,0 @@
|
|||||||
from rest_framework import serializers
|
|
||||||
|
|
||||||
from .models import Sale, SaleLine, Product, Customer, ReconciliationJar
|
|
||||||
|
|
||||||
|
|
||||||
class SaleLineSerializer(serializers.ModelSerializer):
|
|
||||||
class Meta:
|
|
||||||
model = SaleLine
|
|
||||||
fields = ['id', 'sale', 'product', 'unit_price', 'quantity']
|
|
||||||
|
|
||||||
|
|
||||||
class SaleSerializer(serializers.ModelSerializer):
|
|
||||||
total = serializers.ReadOnlyField(source='get_total')
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = Sale
|
|
||||||
fields = ['id', 'customer', 'date', 'saleline_set',
|
|
||||||
'total', 'payment_method']
|
|
||||||
|
|
||||||
|
|
||||||
class ProductSerializer(serializers.ModelSerializer):
|
|
||||||
class Meta:
|
|
||||||
model = Product
|
|
||||||
fields = ['id', 'name', 'price', 'measuring_unit', 'categories']
|
|
||||||
|
|
||||||
|
|
||||||
class CustomerSerializer(serializers.ModelSerializer):
|
|
||||||
class Meta:
|
|
||||||
model = Customer
|
|
||||||
fields = ['id', 'name', 'address', 'email', 'phone']
|
|
||||||
|
|
||||||
|
|
||||||
class ReconciliationJarSerializer(serializers.ModelSerializer):
|
|
||||||
Sales = SaleSerializer(many=True, read_only=True)
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = ReconciliationJar
|
|
||||||
fields = [
|
|
||||||
'id',
|
|
||||||
'date_time',
|
|
||||||
'reconcilier',
|
|
||||||
'cash_taken',
|
|
||||||
'cash_discrepancy',
|
|
||||||
'total_cash_purchases',
|
|
||||||
'Sales',
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class PaymentMethodSerializer(serializers.Serializer):
|
|
||||||
text = serializers.CharField()
|
|
||||||
value = serializers.CharField()
|
|
||||||
|
|
||||||
def to_representation(self, instance):
|
|
||||||
return {
|
|
||||||
'text': instance[1],
|
|
||||||
'value': instance[0],
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class SaleForRenconciliationSerializer(serializers.Serializer):
|
|
||||||
id = serializers.IntegerField()
|
|
||||||
date = serializers.DateTimeField()
|
|
||||||
payment_method = serializers.CharField()
|
|
||||||
customer = serializers.SerializerMethodField()
|
|
||||||
total = serializers.SerializerMethodField()
|
|
||||||
|
|
||||||
def get_customer(self, sale):
|
|
||||||
return {
|
|
||||||
'id': sale.customer.id,
|
|
||||||
'name': sale.customer.name,
|
|
||||||
}
|
|
||||||
|
|
||||||
def get_total(self, sale):
|
|
||||||
return sale.get_total()
|
|
||||||
|
|
||||||
|
|
||||||
class ListCustomerSerializer(serializers.ModelSerializer):
|
|
||||||
class Meta:
|
|
||||||
model = Customer
|
|
||||||
fields = ['id', 'name']
|
|
||||||
|
|
||||||
|
|
||||||
class ListProductSerializer(serializers.ModelSerializer):
|
|
||||||
class Meta:
|
|
||||||
model = Product
|
|
||||||
fields = ['id', 'name']
|
|
||||||
|
|
||||||
|
|
||||||
class SummarySaleLineSerializer(serializers.ModelSerializer):
|
|
||||||
product = ListProductSerializer()
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = SaleLine
|
|
||||||
fields = ['product', 'quantity', 'unit_price', 'description']
|
|
||||||
|
|
||||||
|
|
||||||
class SaleSummarySerializer(serializers.ModelSerializer):
|
|
||||||
customer = ListCustomerSerializer()
|
|
||||||
lines = SummarySaleLineSerializer(many=True, source='saleline_set')
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = Sale
|
|
||||||
fields = ['id', 'date', 'customer', 'payment_method', 'lines']
|
|
@ -1,37 +0,0 @@
|
|||||||
nav#main_menu a {
|
|
||||||
text-decoration: none;
|
|
||||||
font-weight: bold;
|
|
||||||
color: white;
|
|
||||||
background-color: #178E79 ;
|
|
||||||
padding: 10px 20px;
|
|
||||||
min-width: 90%;
|
|
||||||
text-align: center;
|
|
||||||
border: solid #178E79 4px;
|
|
||||||
border-radius: 5px;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
nav#main_menu a:hover {
|
|
||||||
transition: ease-in-out 0.2s;
|
|
||||||
background-color: #7BDCB5;
|
|
||||||
color: #178E79;
|
|
||||||
}
|
|
||||||
|
|
||||||
nav#main_menu a:active {
|
|
||||||
background-color: #aaa;
|
|
||||||
color: #444;
|
|
||||||
}
|
|
||||||
|
|
||||||
nav#main_menu {
|
|
||||||
display: inline;
|
|
||||||
}
|
|
||||||
|
|
||||||
li{
|
|
||||||
width: 100%;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.page_title {
|
|
||||||
color: #04A1E4
|
|
||||||
}
|
|
Binary file not shown.
Before Width: | Height: | Size: 179 KiB |
@ -1,30 +0,0 @@
|
|||||||
document.addEventListener('DOMContentLoaded', function(){
|
|
||||||
var button = document.getElementById('add_line');
|
|
||||||
var formContainer = document.getElementById('formset-container');
|
|
||||||
var totalForms = document.getElementById('id_saleline_set-TOTAL_FORMS');
|
|
||||||
button.addEventListener('click', function(){
|
|
||||||
var newForm = formContainer.querySelector('.form-container').cloneNode(true);
|
|
||||||
var formCount = parseInt(totalForms.value);
|
|
||||||
var regex = new RegExp('__prefix__', 'g');
|
|
||||||
newForm.innerHTML = newForm.innerHTML.replace(regex, formCount);
|
|
||||||
|
|
||||||
var fields = newForm.querySelectorAll('[id^="id_saleline_set-"], [name^="saleline_set-"]');
|
|
||||||
fields.forEach(function(field) {
|
|
||||||
var oldId = field.id;
|
|
||||||
var oldName = field.name;
|
|
||||||
|
|
||||||
if (oldId) {
|
|
||||||
var newId = oldId.replace(/-\d+-/, '-' + formCount + '-');
|
|
||||||
field.id = newId;
|
|
||||||
}
|
|
||||||
if (oldName) {
|
|
||||||
var newName = oldName.replace(/-\d+-/, '-' + formCount + '-');
|
|
||||||
field.name = newName;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
formContainer.appendChild(newForm);
|
|
||||||
totalForms.value = formCount + 1;
|
|
||||||
setPriceListeners();
|
|
||||||
});
|
|
||||||
});
|
|
@ -1,22 +0,0 @@
|
|||||||
setPriceListeners();
|
|
||||||
|
|
||||||
function setPriceListeners() {
|
|
||||||
document.querySelectorAll('select[id^="id_saleline_set-"][id$="-product"]').forEach((input) => {
|
|
||||||
input.addEventListener('change', (e) => setLinePrice(e));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function setLinePrice(e) {
|
|
||||||
let input = e.target;
|
|
||||||
const idLine = input.id.split('-')[1];
|
|
||||||
const productId = input.value;
|
|
||||||
const priceInput = document.getElementById(`id_saleline_set-${idLine}-unit_price`);
|
|
||||||
|
|
||||||
const product = listProducts.find((product) => product.id == productId);
|
|
||||||
if (product) {
|
|
||||||
priceInput.value = product.price_list;
|
|
||||||
} else {
|
|
||||||
priceInput.value = '';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,50 +0,0 @@
|
|||||||
const quantity_lineRegexSelector = `[id^="${idPrefix}"][id$="${quantitySuffix}"]`;
|
|
||||||
const price_lineRegexSelector = `[id^="${idPrefix}"][id$="${priceSuffix}"]`;
|
|
||||||
|
|
||||||
function insertSubtotalField() {
|
|
||||||
// Selecciona la fila de precio unitario para añadir la fila del subtotal después de ella
|
|
||||||
const unitPriceRow = document.querySelector('input[id="id_saleline_set-0-unit_price"]').closest('tr');
|
|
||||||
|
|
||||||
// Crear una nueva fila para el subtotal
|
|
||||||
const subtotalRow = document.createElement('tr');
|
|
||||||
subtotalRow.innerHTML = `
|
|
||||||
<th><label for="id_saleline_set-0-subtotal">Subtotal:</label></th>
|
|
||||||
<td><input type="number" name="saleline_set-0-subtotal" id="id_saleline_set-0-subtotal" readonly></td>
|
|
||||||
`;
|
|
||||||
|
|
||||||
// Insertar la fila del subtotal después de la fila del precio unitario
|
|
||||||
unitPriceRow.after(subtotalRow);
|
|
||||||
}
|
|
||||||
|
|
||||||
function calculateSubtotal(id) {
|
|
||||||
const quantityElement = document.getElementById(`id_saleline_set-${id}-quantity`);
|
|
||||||
const unitPriceElement = document.getElementById(`id_saleline_set-${id}-unit_price`);
|
|
||||||
const subtotalElement = document.getElementById(`id_saleline_set-${id}-subtotal`);
|
|
||||||
|
|
||||||
const quantity = parseFloat(quantityElement.value) || 0;
|
|
||||||
const unitPrice = parseFloat(unitPriceElement.value) || 0;
|
|
||||||
const subtotal = quantity * unitPrice;
|
|
||||||
|
|
||||||
subtotalElement.value = subtotal.toFixed(2);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Inserta el campo subtotal al cargar la página
|
|
||||||
window.addEventListener('load', () => {
|
|
||||||
insertSubtotalField();
|
|
||||||
|
|
||||||
complete_form.addEventListener('change', function(event){
|
|
||||||
const quantityInputs = document.querySelectorAll(quantity_lineRegexSelector);
|
|
||||||
const ids = Array.prototype.map.call(quantityInputs, function(input) {
|
|
||||||
return input.id.match(/\d+/)[0];
|
|
||||||
});
|
|
||||||
|
|
||||||
ids.forEach(function(id) {
|
|
||||||
if (event.target.matches(quantity_lineRegexSelector)) {
|
|
||||||
calculateSubtotal(id);
|
|
||||||
}
|
|
||||||
if (event.target.matches(price_lineRegexSelector)) {
|
|
||||||
calculateSubtotal(id);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
});
|
|
||||||
});
|
|
@ -1,37 +0,0 @@
|
|||||||
const complete_form = document.getElementById('complete_form_purchase')
|
|
||||||
const quantityLines = document.getElementById('id_quantity_lines');
|
|
||||||
const quantityProducts = document.getElementById('id_quantity_products');
|
|
||||||
const ammountInput = document.getElementById('id_ammount');
|
|
||||||
const idPrefix = 'id_saleline_set-';
|
|
||||||
const quantitySuffix = '-quantity';
|
|
||||||
const priceSuffix = '-unit_price';
|
|
||||||
const quantityRegexSelector = `[id^="${idPrefix}"][id$="${quantitySuffix}"]`;
|
|
||||||
const priceRegexSelector = `[id^="${idPrefix}"][id$="${priceSuffix}"]`;
|
|
||||||
|
|
||||||
complete_form.addEventListener('change', function(event){
|
|
||||||
if (event.target.matches(quantityRegexSelector)) {
|
|
||||||
calculateSummary();
|
|
||||||
}
|
|
||||||
if (event.target.matches(priceRegexSelector)) {
|
|
||||||
calculateSummary();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
function calculateSummary() {
|
|
||||||
let quantity = 0;
|
|
||||||
let ammount = 0;
|
|
||||||
const quantityInputs = document.querySelectorAll(quantityRegexSelector);
|
|
||||||
const ids = Array.prototype.map.call(quantityInputs, function(input) {
|
|
||||||
return input.id.match(/\d+/)[0];
|
|
||||||
});
|
|
||||||
ids.forEach(function(id) {
|
|
||||||
let lineQuantity = document.getElementById(`${idPrefix}${id}${quantitySuffix}`)
|
|
||||||
let linePrice = document.getElementById(`${idPrefix}${id}${priceSuffix}`)
|
|
||||||
quantity += parseFloat(lineQuantity.value);
|
|
||||||
ammount += parseFloat(linePrice.value) * parseFloat(lineQuantity.value);
|
|
||||||
});
|
|
||||||
quantityProducts.value = quantity;
|
|
||||||
quantityLines.value = quantityInputs.length;
|
|
||||||
ammountInput.value = ammount;
|
|
||||||
}
|
|
@ -1,17 +0,0 @@
|
|||||||
{% load static %}
|
|
||||||
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en" class="h-full">
|
|
||||||
<head>
|
|
||||||
<title>Don Confiao - Tienda la Ilusión</title>
|
|
||||||
</head>
|
|
||||||
<body class="flex h-full w-full">
|
|
||||||
<div id="menu" class="h-full w-2/12 border bg-green-400 max-h-screen overflow-auto">
|
|
||||||
{% include 'don_confiao/menu.html' %}
|
|
||||||
</div>
|
|
||||||
<div id="content" class="w-10/12 h-screen max-h-full overflow-auto">
|
|
||||||
{% block content %} {% endblock %}
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
<script src="https://cdn.tailwindcss.com/"></script>
|
|
||||||
</html>
|
|
@ -1,13 +0,0 @@
|
|||||||
{% extends 'don_confiao/base.html' %}
|
|
||||||
{% block content %}
|
|
||||||
{% 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>
|
|
||||||
{% endblock %}
|
|
@ -1,13 +0,0 @@
|
|||||||
{% extends 'don_confiao/base.html' %}
|
|
||||||
{% block content %}
|
|
||||||
{% 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>
|
|
||||||
{% endblock %}
|
|
@ -1,8 +0,0 @@
|
|||||||
<h1>Tienda la Ilusión</h1>
|
|
||||||
<h2>Don Confiao</h2>
|
|
||||||
<ul>
|
|
||||||
<li><a href='./comprar'>Comprar</a></li>
|
|
||||||
<li><a href='./productos'>Productos</a></li>
|
|
||||||
<li><a href='./importar_productos'>Importar Productos</a></li>
|
|
||||||
<li><a href='./importar_terceros'>Importar Terceros</a></li>
|
|
||||||
</ul>
|
|
@ -1,17 +0,0 @@
|
|||||||
{% load static %}
|
|
||||||
<link rel="stylesheet" type="text/css" href="{% static 'css/main_menu.css' %}">
|
|
||||||
|
|
||||||
<div class="h-full flex flex-col justify-around shadow hover:shadow-lg">
|
|
||||||
<img class="w-full px-12" src="{% static 'img/recreo_logo.png' %}" alt="Recreo">
|
|
||||||
<nav id="main_menu">
|
|
||||||
<ul class="flex flex-col m-0 p-0 justify-center shadow hover:shadow-lg gap-y-12 items-center drop-shadow-lg">
|
|
||||||
<li><a href='/don_confiao/comprar' >Comprar</a></li>
|
|
||||||
<li><a href='/don_confiao/compras'>Compras</a></li>
|
|
||||||
<li><a href='/don_confiao/lista_productos'>Productos</a></li>
|
|
||||||
<li><a href='/don_confiao/importar_productos'>Importar Productos</a></li>
|
|
||||||
<li><a href='/don_confiao/importar_terceros'>Importar Terceros</a></li>
|
|
||||||
</ul>
|
|
||||||
</nav>
|
|
||||||
<p id="page_title" class="text-center decoration-solid font-mono font-bold text-lg page_title">Don Confiao - Tienda la Ilusión</p>
|
|
||||||
</div>
|
|
||||||
<script src="https://cdn.tailwindcss.com/"></script>
|
|
@ -1,14 +0,0 @@
|
|||||||
{% extends 'don_confiao/base.html' %}
|
|
||||||
{% block content %}
|
|
||||||
<form action="" method="get">
|
|
||||||
<label>Filtro por nombre:</label>
|
|
||||||
<input type="text" name="name" value="{{ request.GET.name }}">
|
|
||||||
<button type="submit">Filtrar</button>
|
|
||||||
</form>
|
|
||||||
<h1>Lista de productos</h1>
|
|
||||||
<ul>
|
|
||||||
{% for obj in object_list %}
|
|
||||||
<li>{{ obj.name }} ({{ obj.id }})</li>
|
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
|
||||||
{% endblock %}
|
|
@ -1,39 +0,0 @@
|
|||||||
{% extends 'don_confiao/base.html' %}
|
|
||||||
{% block content %}
|
|
||||||
{% load static %}
|
|
||||||
<script>
|
|
||||||
let listProducts = JSON.parse("{{ list_products|escapejs }}");
|
|
||||||
</script>
|
|
||||||
<div class="flex h-full">
|
|
||||||
<div class="h-full w-10/12 flex flex-col p-5">
|
|
||||||
<form id="complete_form_purchase" method="POST" class="h-10/12 w-full max-h-full overflow-auto">
|
|
||||||
{% csrf_token %}
|
|
||||||
{{ linea_formset.management_form }}
|
|
||||||
<div id="formset-container" class="w-full">
|
|
||||||
{% for form in linea_formset %}
|
|
||||||
<div class="form-container flex justify-center ">
|
|
||||||
<table class="w-3/4 my-5 shadow-inner" style="border: solid 1px #178E79;">
|
|
||||||
{{ form.as_table }}
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
<div class="h-2/12 flex justify-center">
|
|
||||||
<button id="add_line" type="button" class="bg-yellow-400 shadow hover:shadow-lg py-2 px-5 rounded-full font-bold hover:bg-violet-200 ease-in duration-150">Añadir Linea</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="h-full w-3/12 bg-green-400 p-5 shadow hover:shadow-lg flex flex-col gap-y-3 font-semibold justify-around">
|
|
||||||
<p id="sale_resume_title" class="text-center decoration-solid font-mono font-bold text-xl page_title">Resumen de Venta</p>
|
|
||||||
{{ sale_form }}
|
|
||||||
{{ summary_form }}
|
|
||||||
<button class="font-bold my-10 py-2 px-4 rounded-full bg-yellow-400 shadow hover:shadow-lg hover:bg-violet-200 ease-in duration-150" name="form" type="submit" >Comprar</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script src="https://cdn.tailwindcss.com/"></script>
|
|
||||||
<script src="{% static 'js/buy_general.js' %}"></script>
|
|
||||||
<script src="{% static 'js/add_line.js' %}"></script>
|
|
||||||
<script src="{% static 'js/sale_summary.js' %}"></script>
|
|
||||||
<script src="{% static 'js/calculate_subtotal_line.js' %}"></script>
|
|
||||||
{% endblock %}
|
|
@ -1,12 +0,0 @@
|
|||||||
{% extends 'don_confiao/base.html' %}
|
|
||||||
{% block content %}
|
|
||||||
|
|
||||||
<h1>Resumen de compra</h1>
|
|
||||||
<dl>
|
|
||||||
<dt>Date</dt> <dd>{{ purchase.date }}</dd>
|
|
||||||
<dt>ID</dt> <dd>{{ purchase.id }}</dd>
|
|
||||||
<dt>Customer</dt> <dd>{{ purchase.customer.name }}</dd>
|
|
||||||
<dt>Total</dt> <dd>{{ purchase.get_total }}</dd>
|
|
||||||
</dl>
|
|
||||||
|
|
||||||
{% endblock %}
|
|
@ -1,14 +0,0 @@
|
|||||||
{% extends 'don_confiao/base.html' %}
|
|
||||||
{% block content %}
|
|
||||||
|
|
||||||
{% if purchases %}
|
|
||||||
<ul>
|
|
||||||
{% for purchase in purchases %}
|
|
||||||
<li><a href="/don_confiao/resumen_compra/{{ purchase.id }}">{{ purchase.date }}, {{ purchase.customer }}</a></li>
|
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
|
||||||
{% else %}
|
|
||||||
<p>No hay Compras</p>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% endblock %}
|
|
@ -1,47 +0,0 @@
|
|||||||
[
|
|
||||||
{
|
|
||||||
"model": "don_confiao.customer",
|
|
||||||
"pk": 1,
|
|
||||||
"fields": {
|
|
||||||
"name": "Alejandro Fernandez",
|
|
||||||
"address": "Avenida Siempre Viva"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"model": "don_confiao.productcategory",
|
|
||||||
"pk": 1,
|
|
||||||
"fields": {
|
|
||||||
"name": "Unidad"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"model": "don_confiao.product",
|
|
||||||
"pk": 1,
|
|
||||||
"fields": {
|
|
||||||
"name": "Papaya",
|
|
||||||
"price": 2500,
|
|
||||||
"measuring_unit": "Unidad"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"model": "don_confiao.sale",
|
|
||||||
"pk": 1,
|
|
||||||
"fields": {
|
|
||||||
"customer": 1,
|
|
||||||
"date": "2024-08-31",
|
|
||||||
"phone": 312201103,
|
|
||||||
"description": "Primera Venta"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"model": "don_confiao.saleline",
|
|
||||||
"pk": 1,
|
|
||||||
"fields": {
|
|
||||||
"sale": 1,
|
|
||||||
"product": 1,
|
|
||||||
"quantity": 10,
|
|
||||||
"unit_price": 5000,
|
|
||||||
"description": "Primer Sale Line"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
@ -1 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
@ -1,41 +0,0 @@
|
|||||||
from django.test import TestCase, Client
|
|
||||||
|
|
||||||
from ..models import AdminCode
|
|
||||||
|
|
||||||
import json
|
|
||||||
|
|
||||||
|
|
||||||
class TestAdminCode(TestCase):
|
|
||||||
def setUp(self):
|
|
||||||
self.valid_code = 'some valid code'
|
|
||||||
admin_code = AdminCode()
|
|
||||||
admin_code.value = self.valid_code
|
|
||||||
admin_code.clean()
|
|
||||||
admin_code.save()
|
|
||||||
|
|
||||||
self.client = Client()
|
|
||||||
|
|
||||||
def test_validate_code(self):
|
|
||||||
url = '/don_confiao/api/admin_code/validate/' + self.valid_code
|
|
||||||
response = self.client.get(url)
|
|
||||||
self.assertEqual(response.status_code, 200)
|
|
||||||
|
|
||||||
content = json.loads(response.content.decode('utf-8'))
|
|
||||||
self.assertTrue(content['validCode'])
|
|
||||||
|
|
||||||
def test_invalid_code(self):
|
|
||||||
invalid_code = 'some invalid code'
|
|
||||||
url = '/don_confiao/api/admin_code/validate/' + invalid_code
|
|
||||||
|
|
||||||
response = self.client.get(url)
|
|
||||||
self.assertEqual(response.status_code, 200)
|
|
||||||
|
|
||||||
content = json.loads(response.content.decode('utf-8'))
|
|
||||||
self.assertFalse(content['validCode'])
|
|
||||||
|
|
||||||
def test_empty_code(self):
|
|
||||||
empty_code = ''
|
|
||||||
url = '/don_confiao/api/admin_code/validate/' + empty_code
|
|
||||||
|
|
||||||
response = self.client.get(url)
|
|
||||||
self.assertEqual(response.status_code, 404)
|
|
@ -1,59 +0,0 @@
|
|||||||
import json
|
|
||||||
from django.urls import reverse
|
|
||||||
from rest_framework import status
|
|
||||||
from rest_framework.test import APITestCase
|
|
||||||
from ..models import Sale, Product, Customer
|
|
||||||
|
|
||||||
|
|
||||||
class TestAPI(APITestCase):
|
|
||||||
def setUp(self):
|
|
||||||
self.product = Product.objects.create(
|
|
||||||
name='Panela',
|
|
||||||
price=5000,
|
|
||||||
measuring_unit='UNIT'
|
|
||||||
)
|
|
||||||
self.customer = Customer.objects.create(
|
|
||||||
name='Camilo'
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_create_sale(self):
|
|
||||||
response = self._create_sale()
|
|
||||||
content = json.loads(response.content.decode('utf-8'))
|
|
||||||
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
|
|
||||||
self.assertEqual(Sale.objects.count(), 1)
|
|
||||||
sale = Sale.objects.all()[0]
|
|
||||||
self.assertEqual(
|
|
||||||
sale.customer.name,
|
|
||||||
self.customer.name
|
|
||||||
)
|
|
||||||
self.assertEqual(
|
|
||||||
sale.id,
|
|
||||||
content['id']
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_get_products(self):
|
|
||||||
url = '/don_confiao/api/products/'
|
|
||||||
response = self.client.get(url)
|
|
||||||
json_response = json.loads(response.content.decode('utf-8'))
|
|
||||||
self.assertEqual(response.status_code, 200)
|
|
||||||
self.assertEqual(self.product.name, json_response[0]['name'])
|
|
||||||
|
|
||||||
def test_get_customers(self):
|
|
||||||
url = '/don_confiao/api/customers/'
|
|
||||||
response = self.client.get(url)
|
|
||||||
json_response = json.loads(response.content.decode('utf-8'))
|
|
||||||
self.assertEqual(response.status_code, 200)
|
|
||||||
self.assertEqual(self.customer.name, json_response[0]['name'])
|
|
||||||
|
|
||||||
def _create_sale(self):
|
|
||||||
url = '/don_confiao/api/sales/'
|
|
||||||
data = {
|
|
||||||
'customer': self.customer.id,
|
|
||||||
'date': '2024-09-02',
|
|
||||||
'payment_method': 'CASH',
|
|
||||||
'saleline_set': [
|
|
||||||
{'product': self.product.id, 'quantity': 2, 'unit_price': 3000},
|
|
||||||
{'product': self.product.id, 'quantity': 3, 'unit_price': 5000}
|
|
||||||
],
|
|
||||||
}
|
|
||||||
return self.client.post(url, data, format='json')
|
|
@ -1,22 +0,0 @@
|
|||||||
from django.test import Client, TestCase
|
|
||||||
from ..models import Product
|
|
||||||
|
|
||||||
|
|
||||||
class TestBuyForm(TestCase):
|
|
||||||
def setUp(self):
|
|
||||||
self.client = Client()
|
|
||||||
self.product = Product()
|
|
||||||
self.product.name = "Arroz"
|
|
||||||
self.product.price = 5000
|
|
||||||
self.product.save()
|
|
||||||
|
|
||||||
def test_buy_contains_products_list(self):
|
|
||||||
response = self.client.get('/don_confiao/comprar')
|
|
||||||
self.assertIn(
|
|
||||||
self.product.name,
|
|
||||||
response.context['list_products']
|
|
||||||
)
|
|
||||||
content = response.content.decode('utf-8')
|
|
||||||
self.assertIn('5000', content)
|
|
||||||
self.assertIn('Arroz', content)
|
|
||||||
self.assertIn(str(self.product.id), content)
|
|
@ -1,50 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
from django.test import Client, TestCase
|
|
||||||
from io import StringIO
|
|
||||||
import csv
|
|
||||||
|
|
||||||
|
|
||||||
class TestExportSales(TestCase):
|
|
||||||
fixtures = ['sales_fixture']
|
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
self.client = Client()
|
|
||||||
|
|
||||||
def test_export_sales(self):
|
|
||||||
sales_response = self._export_sales_csv()
|
|
||||||
filename = sales_response.headers[
|
|
||||||
'Content-Disposition'].split('; ')[1].strip('filename=').strip("'")
|
|
||||||
content = sales_response.content
|
|
||||||
content_str = content.decode('utf-8')
|
|
||||||
csv_file = StringIO(content_str)
|
|
||||||
header = next(csv.reader(csv_file))
|
|
||||||
|
|
||||||
self.assertGreater(len(content), 0)
|
|
||||||
self.assertEqual(filename, 'sales.csv')
|
|
||||||
self.assertEqual(sales_response.headers['Content-Type'], 'text/csv')
|
|
||||||
self.assertEqual(header, self._tryton_sale_header())
|
|
||||||
|
|
||||||
def _export_sales_csv(self):
|
|
||||||
return self.client.get("/don_confiao/exportar_ventas_para_tryton")
|
|
||||||
|
|
||||||
def _tryton_sale_header(self):
|
|
||||||
return [
|
|
||||||
"Tercero",
|
|
||||||
"Dirección de facturación",
|
|
||||||
"Dirección de envío",
|
|
||||||
"Descripción",
|
|
||||||
"Referencia",
|
|
||||||
"Fecha venta",
|
|
||||||
"Plazo de pago",
|
|
||||||
"Almacén",
|
|
||||||
"Moneda",
|
|
||||||
"Líneas/Producto",
|
|
||||||
"Líneas/Cantidad",
|
|
||||||
"Líneas/Precio unitario",
|
|
||||||
"Líneas/Unidad",
|
|
||||||
"Empresa",
|
|
||||||
"Tienda",
|
|
||||||
"Terminal de venta",
|
|
||||||
"Autorecogida",
|
|
||||||
"Comentario"
|
|
||||||
]
|
|
@ -1,264 +0,0 @@
|
|||||||
from django.test import TestCase, Client
|
|
||||||
from django.core.exceptions import ValidationError
|
|
||||||
from ..models import Sale, Product, SaleLine, Customer, ReconciliationJar
|
|
||||||
|
|
||||||
import json
|
|
||||||
|
|
||||||
|
|
||||||
class TestJarReconcliation(TestCase):
|
|
||||||
def setUp(self):
|
|
||||||
customer = Customer()
|
|
||||||
customer.name = 'Alejo Mono'
|
|
||||||
customer.save()
|
|
||||||
|
|
||||||
self.client = Client()
|
|
||||||
|
|
||||||
purchase = Sale()
|
|
||||||
purchase.customer = customer
|
|
||||||
purchase.date = "2024-07-30"
|
|
||||||
purchase.payment_method = 'CASH'
|
|
||||||
purchase.clean()
|
|
||||||
purchase.save()
|
|
||||||
|
|
||||||
product = Product()
|
|
||||||
product.name = "cafe"
|
|
||||||
product.price = "72500"
|
|
||||||
product.save()
|
|
||||||
|
|
||||||
line = SaleLine()
|
|
||||||
line.sale = purchase
|
|
||||||
line.product = product
|
|
||||||
line.quantity = "11"
|
|
||||||
line.unit_price = "72500"
|
|
||||||
line.save()
|
|
||||||
self.purchase = purchase
|
|
||||||
|
|
||||||
purchase2 = Sale()
|
|
||||||
purchase2.customer = customer
|
|
||||||
purchase2.date = "2024-07-30"
|
|
||||||
purchase.payment_method = 'CASH'
|
|
||||||
purchase2.clean()
|
|
||||||
purchase2.save()
|
|
||||||
|
|
||||||
line2 = SaleLine()
|
|
||||||
line2.sale = purchase2
|
|
||||||
line2.product = product
|
|
||||||
line2.quantity = "27"
|
|
||||||
line2.unit_price = "72500"
|
|
||||||
line2.save()
|
|
||||||
self.purchase2 = purchase2
|
|
||||||
|
|
||||||
purchase3 = Sale()
|
|
||||||
purchase3.customer = customer
|
|
||||||
purchase3.date = "2024-07-30"
|
|
||||||
purchase3.payment_method = 'CASH'
|
|
||||||
purchase3.clean()
|
|
||||||
purchase3.save()
|
|
||||||
|
|
||||||
line3 = SaleLine()
|
|
||||||
line3.sale = purchase3
|
|
||||||
line3.product = product
|
|
||||||
line3.quantity = "37"
|
|
||||||
line3.unit_price = "72500"
|
|
||||||
line3.save()
|
|
||||||
self.purchase3 = purchase3
|
|
||||||
|
|
||||||
purchase4 = Sale()
|
|
||||||
purchase4.customer = customer
|
|
||||||
purchase4.date = "2024-07-30"
|
|
||||||
purchase4.payment_method = 'CONFIAR'
|
|
||||||
purchase4.clean()
|
|
||||||
purchase4.save()
|
|
||||||
|
|
||||||
line4 = SaleLine()
|
|
||||||
line4.sale = purchase4
|
|
||||||
line4.product = product
|
|
||||||
line4.quantity = "47"
|
|
||||||
line4.unit_price = "72500"
|
|
||||||
line4.save()
|
|
||||||
self.purchase4 = purchase4
|
|
||||||
|
|
||||||
def test_create_reconciliation_jar(self):
|
|
||||||
reconciliation = self._create_simple_reconciliation()
|
|
||||||
self.assertTrue(isinstance(reconciliation, ReconciliationJar))
|
|
||||||
|
|
||||||
def test_get_purchases_for_reconciliation(self):
|
|
||||||
# link purchase to reconciliation to exclude from list
|
|
||||||
reconciliation = self._create_simple_reconciliation()
|
|
||||||
self.purchase3.reconciliation = reconciliation
|
|
||||||
self.purchase3.clean()
|
|
||||||
self.purchase3.save()
|
|
||||||
|
|
||||||
url = '/don_confiao/purchases/for_reconciliation'
|
|
||||||
response = self.client.get(url)
|
|
||||||
self.assertEqual(response.status_code, 200)
|
|
||||||
|
|
||||||
rawContent = response.content.decode('utf-8')
|
|
||||||
content = json.loads(rawContent)
|
|
||||||
|
|
||||||
self.assertIn('CASH', content.keys())
|
|
||||||
self.assertIn('CONFIAR', content.keys())
|
|
||||||
self.assertEqual(2, len(content.get('CASH')))
|
|
||||||
self.assertEqual(1, len(content.get('CONFIAR')))
|
|
||||||
self.assertNotIn(str(37*72500), rawContent)
|
|
||||||
self.assertIn(str(47*72500), rawContent)
|
|
||||||
|
|
||||||
def test_don_create_reconcialiation_with_bad_numbers(self):
|
|
||||||
reconciliation = ReconciliationJar()
|
|
||||||
reconciliation.date_time = "2024-07-30"
|
|
||||||
reconciliation.total_cash_purchases = 145000
|
|
||||||
reconciliation.cash_taken = 143000
|
|
||||||
reconciliation.cash_discrepancy = 1000
|
|
||||||
with self.assertRaises(ValidationError):
|
|
||||||
reconciliation.clean()
|
|
||||||
reconciliation.save()
|
|
||||||
|
|
||||||
def test_fail_create_reconciliation_with_wrong_total_purchases_purchases(self):
|
|
||||||
url = '/don_confiao/reconciliate_jar'
|
|
||||||
total_purchases = (11 * 72500) + (27 * 72500)
|
|
||||||
bad_total_purchases = total_purchases + 2
|
|
||||||
data = {
|
|
||||||
'date_time': '2024-12-02T21:07',
|
|
||||||
'reconcilier': 'carlos',
|
|
||||||
'total_cash_purchases': bad_total_purchases,
|
|
||||||
'cash_taken': total_purchases,
|
|
||||||
'cash_discrepancy': 0,
|
|
||||||
'cash_purchases': [
|
|
||||||
self.purchase.id,
|
|
||||||
self.purchase2.id,
|
|
||||||
self.purchase.id,
|
|
||||||
],
|
|
||||||
}
|
|
||||||
response = self.client.post(url, data=json.dumps(data).encode('utf-8'),
|
|
||||||
content_type='application/json')
|
|
||||||
rawContent = response.content.decode('utf-8')
|
|
||||||
content = json.loads(rawContent)
|
|
||||||
|
|
||||||
self.assertEqual(response.status_code, 400)
|
|
||||||
self.assertIn('error', content)
|
|
||||||
self.assertIn('total_cash_purchases', content['error'])
|
|
||||||
|
|
||||||
def test_create_reconciliation_with_purchases(self):
|
|
||||||
response = self._create_reconciliation_with_purchase()
|
|
||||||
|
|
||||||
rawContent = response.content.decode('utf-8')
|
|
||||||
content = json.loads(rawContent)
|
|
||||||
|
|
||||||
self.assertEqual(response.status_code, 200)
|
|
||||||
self.assertIn('id', content)
|
|
||||||
|
|
||||||
purchases = Sale.objects.filter(reconciliation_id=content['id'])
|
|
||||||
self.assertEqual(len(purchases), 2)
|
|
||||||
|
|
||||||
def test_create_reconciliation_with_purchases_and_other_totals(self):
|
|
||||||
url = '/don_confiao/reconciliate_jar'
|
|
||||||
total_purchases = (11 * 72500) + (27 * 72500)
|
|
||||||
data = {
|
|
||||||
'date_time': '2024-12-02T21:07',
|
|
||||||
'reconcilier': 'carlos',
|
|
||||||
'total_cash_purchases': total_purchases,
|
|
||||||
'cash_taken': total_purchases,
|
|
||||||
'cash_discrepancy': 0,
|
|
||||||
'cash_purchases': [
|
|
||||||
self.purchase.id,
|
|
||||||
self.purchase2.id,
|
|
||||||
],
|
|
||||||
'other_totals': {
|
|
||||||
'Confiar': {
|
|
||||||
'total': (47 * 72500) + 1,
|
|
||||||
'purchases': [self.purchase4.id],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
response = self.client.post(url, data=json.dumps(data).encode('utf-8'),
|
|
||||||
content_type='application/json')
|
|
||||||
|
|
||||||
rawContent = response.content.decode('utf-8')
|
|
||||||
content = json.loads(rawContent)
|
|
||||||
|
|
||||||
self.assertEqual(response.status_code, 200)
|
|
||||||
self.assertIn('id', content)
|
|
||||||
|
|
||||||
purchases = Sale.objects.filter(reconciliation_id=content['id'])
|
|
||||||
self.assertEqual(len(purchases), 3)
|
|
||||||
|
|
||||||
def test_list_reconciliations(self):
|
|
||||||
self._create_simple_reconciliation()
|
|
||||||
self._create_simple_reconciliation()
|
|
||||||
|
|
||||||
url = '/don_confiao/api/reconciliate_jar/'
|
|
||||||
|
|
||||||
response = self.client.get(url)
|
|
||||||
self.assertEqual(response.status_code, 200)
|
|
||||||
|
|
||||||
content = json.loads(response.content.decode('utf-8'))
|
|
||||||
self.assertEqual(2, content['count'])
|
|
||||||
self.assertEqual(2, len(content['results']))
|
|
||||||
self.assertEqual('2024-07-30T00:00:00Z',
|
|
||||||
content['results'][0]['date_time'])
|
|
||||||
|
|
||||||
def test_list_reconciliations_pagination(self):
|
|
||||||
self._create_simple_reconciliation()
|
|
||||||
self._create_simple_reconciliation()
|
|
||||||
|
|
||||||
url = '/don_confiao/api/reconciliate_jar/?page=2&page_size=1'
|
|
||||||
|
|
||||||
response = self.client.get(url)
|
|
||||||
self.assertEqual(response.status_code, 200)
|
|
||||||
|
|
||||||
content = json.loads(response.content.decode('utf-8'))
|
|
||||||
self.assertEqual(1, len(content['results']))
|
|
||||||
self.assertEqual('2024-07-30T00:00:00Z',
|
|
||||||
content['results'][0]['date_time'])
|
|
||||||
|
|
||||||
def test_get_single_reconciliation(self):
|
|
||||||
createResponse = self._create_reconciliation_with_purchase()
|
|
||||||
reconciliationId = json.loads(
|
|
||||||
createResponse.content.decode('utf-8')
|
|
||||||
)['id']
|
|
||||||
self.assertGreater(reconciliationId, 0)
|
|
||||||
|
|
||||||
url = f'/don_confiao/api/reconciliate_jar/{reconciliationId}/'
|
|
||||||
response = self.client.get(url, content_type='application/json')
|
|
||||||
content = json.loads(
|
|
||||||
response.content.decode('utf-8')
|
|
||||||
)
|
|
||||||
self.assertEqual(reconciliationId, content['id'])
|
|
||||||
self.assertGreater(len(content['Sales']), 0)
|
|
||||||
self.assertIn(
|
|
||||||
self.purchase.id,
|
|
||||||
[sale['id'] for sale in content['Sales']]
|
|
||||||
)
|
|
||||||
|
|
||||||
self.assertIn(
|
|
||||||
'CASH',
|
|
||||||
[sale['payment_method'] for sale in content['Sales']]
|
|
||||||
)
|
|
||||||
|
|
||||||
def _create_simple_reconciliation(self):
|
|
||||||
reconciliation = ReconciliationJar()
|
|
||||||
reconciliation.date_time = "2024-07-30"
|
|
||||||
reconciliation.total_cash_purchases = 0
|
|
||||||
reconciliation.cash_taken = 0
|
|
||||||
reconciliation.cash_discrepancy = 0
|
|
||||||
reconciliation.clean()
|
|
||||||
reconciliation.save()
|
|
||||||
return reconciliation
|
|
||||||
|
|
||||||
def _create_reconciliation_with_purchase(self):
|
|
||||||
url = '/don_confiao/reconciliate_jar'
|
|
||||||
total_purchases = (11 * 72500) + (27 * 72500)
|
|
||||||
data = {
|
|
||||||
'date_time': '2024-12-02T21:07',
|
|
||||||
'reconcilier': 'carlos',
|
|
||||||
'total_cash_purchases': total_purchases,
|
|
||||||
'cash_taken': total_purchases,
|
|
||||||
'cash_discrepancy': 0,
|
|
||||||
'cash_purchases': [
|
|
||||||
self.purchase.id,
|
|
||||||
self.purchase2.id,
|
|
||||||
self.purchase.id,
|
|
||||||
],
|
|
||||||
}
|
|
||||||
return self.client.post(url, data=json.dumps(data).encode('utf-8'),
|
|
||||||
content_type='application/json')
|
|
@ -1,21 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
from django.test import TestCase
|
|
||||||
from django.db.utils import IntegrityError
|
|
||||||
|
|
||||||
from ..models import Customer
|
|
||||||
|
|
||||||
|
|
||||||
class TestCustomer(TestCase):
|
|
||||||
|
|
||||||
def test_create_customer(self):
|
|
||||||
customer = Customer()
|
|
||||||
customer.name = "Don Confiado Gonzalez"
|
|
||||||
customer.address = "Pueblo Bonito"
|
|
||||||
customer.save()
|
|
||||||
|
|
||||||
self.assertIsInstance(customer, Customer)
|
|
||||||
|
|
||||||
def test_don_create_customer_without_name(self):
|
|
||||||
customer = Customer()
|
|
||||||
with self.assertRaises(IntegrityError):
|
|
||||||
customer.save()
|
|
@ -1,23 +0,0 @@
|
|||||||
from django.test import Client, TestCase
|
|
||||||
|
|
||||||
# from ..models import PaymentMethods
|
|
||||||
|
|
||||||
class TestPaymentMethods(TestCase):
|
|
||||||
def setUp(self):
|
|
||||||
self.client = Client()
|
|
||||||
|
|
||||||
def test_keys_in_payment_methods_to_select(self):
|
|
||||||
response = self.client.get(
|
|
||||||
'/don_confiao/payment_methods/all/select_format'
|
|
||||||
)
|
|
||||||
methods = response.json()
|
|
||||||
for method in methods:
|
|
||||||
self.assertEqual(set(method.keys()), {'text', 'value'})
|
|
||||||
|
|
||||||
def test_basic_payment_methods_to_select(self):
|
|
||||||
methods = self.client.get(
|
|
||||||
'/don_confiao/payment_methods/all/select_format'
|
|
||||||
).json()
|
|
||||||
self.assertIn('CASH', [method.get('value') for method in methods])
|
|
||||||
self.assertIn('CONFIAR', [method.get('value') for method in methods])
|
|
||||||
self.assertIn('BANCOLOMBIA', [method.get('value') for method in methods])
|
|
@ -1,104 +0,0 @@
|
|||||||
from django.test import Client, TestCase
|
|
||||||
from django.conf import settings
|
|
||||||
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}
|
|
||||||
)
|
|
@ -1,51 +0,0 @@
|
|||||||
from django.test import Client, TestCase
|
|
||||||
|
|
||||||
from ..models import Payment, Sale, Product, Customer
|
|
||||||
|
|
||||||
|
|
||||||
class TestPurchaseWithPayment(TestCase):
|
|
||||||
def setUp(self):
|
|
||||||
self.client = Client()
|
|
||||||
self.product = Product()
|
|
||||||
self.product.name = "Arroz"
|
|
||||||
self.product.price = 5000
|
|
||||||
self.product.save()
|
|
||||||
customer = Customer()
|
|
||||||
customer.name = "Noelba Lopez"
|
|
||||||
customer.save()
|
|
||||||
self.customer = customer
|
|
||||||
|
|
||||||
def test_generate_payment_when_it_has_payment(self):
|
|
||||||
quantity = 2
|
|
||||||
unit_price = 2500
|
|
||||||
total = 5000
|
|
||||||
self.client.post(
|
|
||||||
'/don_confiao/comprar',
|
|
||||||
{
|
|
||||||
"customer": str(self.customer.id),
|
|
||||||
"date": "2024-07-27",
|
|
||||||
"phone": "3010101000",
|
|
||||||
"description": "Venta de contado",
|
|
||||||
"saleline_set-TOTAL_FORMS": "1",
|
|
||||||
"saleline_set-INITIAL_FORMS": "0",
|
|
||||||
"saleline_set-MIN_NUM_FORMS": "0",
|
|
||||||
"saleline_set-MAX_NUM_FORMS": "1000",
|
|
||||||
"saleline_set-0-product": str(self.product.id),
|
|
||||||
"saleline_set-0-quantity": str(quantity),
|
|
||||||
"saleline_set-0-unit_price": str(unit_price),
|
|
||||||
"saleline_set-0-description": "Linea de Venta",
|
|
||||||
"saleline_set-0-sale": "",
|
|
||||||
"saleline_set-0-id": "",
|
|
||||||
"quantity_lines": "1",
|
|
||||||
"quantity_products": str(quantity),
|
|
||||||
"ammount": str(quantity * unit_price),
|
|
||||||
"payment_method": "CASH",
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
purchases = Sale.objects.all()
|
|
||||||
self.assertEqual(1, len(purchases))
|
|
||||||
payments = Payment.objects.all()
|
|
||||||
self.assertEqual(1, len(payments))
|
|
||||||
self.assertEqual(total, payments[0].amount)
|
|
||||||
self.assertEqual('CASH', payments[0].type_payment)
|
|
@ -1,53 +0,0 @@
|
|||||||
from django.test import TestCase, Client
|
|
||||||
from ..models import Sale, Product, SaleLine, Customer
|
|
||||||
|
|
||||||
|
|
||||||
class TestSummaryViewPurchase(TestCase):
|
|
||||||
def setUp(self):
|
|
||||||
customer = Customer()
|
|
||||||
customer.name = 'Alejo Mono'
|
|
||||||
customer.save()
|
|
||||||
|
|
||||||
self.client = Client()
|
|
||||||
purchase = Sale()
|
|
||||||
purchase.customer = customer
|
|
||||||
purchase.date = "2024-07-30"
|
|
||||||
purchase.clean()
|
|
||||||
purchase.save()
|
|
||||||
|
|
||||||
product = Product()
|
|
||||||
product.name = "cafe"
|
|
||||||
product.price = "72500"
|
|
||||||
product.save()
|
|
||||||
|
|
||||||
line = SaleLine()
|
|
||||||
line.sale = purchase
|
|
||||||
line.product = product
|
|
||||||
line.quantity = "11"
|
|
||||||
line.unit_price = "72500"
|
|
||||||
line.save()
|
|
||||||
self.purchase = purchase
|
|
||||||
|
|
||||||
def test_summary_has_customer(self):
|
|
||||||
url = "/don_confiao/resumen_compra/" + str(self.purchase.id)
|
|
||||||
response = self.client.get(url)
|
|
||||||
self.assertEqual(response.status_code, 200)
|
|
||||||
self.assertEqual(
|
|
||||||
response.context["purchase"].customer,
|
|
||||||
self.purchase.customer
|
|
||||||
)
|
|
||||||
self.assertIn('Alejo Mono', response.content.decode('utf-8'))
|
|
||||||
|
|
||||||
def test_json_summary(self):
|
|
||||||
url = f"/don_confiao/resumen_compra_json/{self.purchase.id}"
|
|
||||||
response = self.client.get(url)
|
|
||||||
content = response.content.decode('utf-8')
|
|
||||||
self.assertEqual(response.status_code, 200)
|
|
||||||
self.assertIn('Alejo Mono', content)
|
|
||||||
self.assertIn('cafe', content)
|
|
||||||
self.assertIn('72500', content)
|
|
||||||
self.assertIn('quantity', content)
|
|
||||||
self.assertIn('11', content)
|
|
||||||
self.assertIn('date', content)
|
|
||||||
self.assertIn(self.purchase.date, content)
|
|
||||||
self.assertIn('lines', content)
|
|
@ -1,96 +0,0 @@
|
|||||||
from django.test import TestCase
|
|
||||||
from django.core.exceptions import ValidationError
|
|
||||||
|
|
||||||
from ..models import Customer, Product, Sale, SaleLine
|
|
||||||
|
|
||||||
|
|
||||||
class ConfiaoTest(TestCase):
|
|
||||||
def setUp(self):
|
|
||||||
self.product = Product()
|
|
||||||
self.product.name = "Pepino"
|
|
||||||
self.product.price = 5000
|
|
||||||
self.product.save()
|
|
||||||
|
|
||||||
self.customer = Customer()
|
|
||||||
self.customer.name = "Don Confiao Gonzalez"
|
|
||||||
self.customer.address = "Patio Bonito"
|
|
||||||
self.customer.save()
|
|
||||||
|
|
||||||
def test_create_sale(self):
|
|
||||||
sale = Sale()
|
|
||||||
sale.customer = self.customer
|
|
||||||
sale.date = "2024-06-22 12:05:00"
|
|
||||||
sale.phone = '666666666'
|
|
||||||
sale.description = "Description"
|
|
||||||
sale.save()
|
|
||||||
|
|
||||||
self.assertIsInstance(sale, Sale)
|
|
||||||
|
|
||||||
def test_can_create_sale_without_payment_method(self):
|
|
||||||
sale = Sale()
|
|
||||||
sale.customer = self.customer
|
|
||||||
sale.date = "2024-06-22 12:05:00"
|
|
||||||
sale.phone = '666666666'
|
|
||||||
sale.description = "Description"
|
|
||||||
sale.payment_method = ''
|
|
||||||
|
|
||||||
with self.assertRaises(ValidationError):
|
|
||||||
sale.full_clean()
|
|
||||||
|
|
||||||
def test_create_sale_line(self):
|
|
||||||
sale = Sale()
|
|
||||||
sale.customer = self.customer
|
|
||||||
sale.date = "2024-06-22"
|
|
||||||
sale.phone = '666666666'
|
|
||||||
sale.description = "Description"
|
|
||||||
|
|
||||||
line = SaleLine()
|
|
||||||
line.sale = sale
|
|
||||||
line.product = self.product
|
|
||||||
line.quantity = 2
|
|
||||||
line.unit_price = 2500
|
|
||||||
line.amount = 5000
|
|
||||||
sale.save()
|
|
||||||
line.save()
|
|
||||||
self.assertEqual(SaleLine.objects.all()[0].quantity, 2)
|
|
||||||
|
|
||||||
def test_create_sale_with_lines(self):
|
|
||||||
sale = Sale()
|
|
||||||
sale.customer = self.customer
|
|
||||||
sale.date = "2024-06-22"
|
|
||||||
sale.phone = '666666666'
|
|
||||||
sale.description = "Description"
|
|
||||||
|
|
||||||
line1 = SaleLine()
|
|
||||||
line1.sale = sale
|
|
||||||
line1.product = self.product
|
|
||||||
line1.quantity = 2
|
|
||||||
line1.unit_price = 2500
|
|
||||||
line1.amount = 5000
|
|
||||||
|
|
||||||
line2 = SaleLine()
|
|
||||||
line2.sale = sale
|
|
||||||
line2.product = self.product
|
|
||||||
line2.quantity = 2
|
|
||||||
line2.unit_price = 2500
|
|
||||||
line2.amount = 5000
|
|
||||||
|
|
||||||
sale.save()
|
|
||||||
line1.save()
|
|
||||||
line2.save()
|
|
||||||
|
|
||||||
self.assertEqual(len(SaleLine.objects.all()), 2)
|
|
||||||
self.assertEqual(
|
|
||||||
Sale.objects.all()[0].saleline_set.all()[0].quantity,
|
|
||||||
2
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_allow_sale_without_description(self):
|
|
||||||
sale = Sale()
|
|
||||||
sale.customer = self.customer
|
|
||||||
sale.date = "2024-06-22"
|
|
||||||
sale.phone = '666666666'
|
|
||||||
sale.description = None
|
|
||||||
sale.save()
|
|
||||||
|
|
||||||
self.assertEqual(len(Sale.objects.all()), 1)
|
|
@ -1,45 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
from django.test import TestCase
|
|
||||||
from ..forms import PurchaseForm
|
|
||||||
from ..models import Customer
|
|
||||||
|
|
||||||
_csrf_token = \
|
|
||||||
"bVjBevJRavxRPFOlVgAWiyh9ceuiwPlyEcmbPZprNuCGHjFZRKZrBeunJvKTRgOx"
|
|
||||||
|
|
||||||
|
|
||||||
class PurchaseFormTest(TestCase):
|
|
||||||
def setUp(self):
|
|
||||||
self.customer = Customer()
|
|
||||||
self.customer.name = "Don Confiao Gonzalez"
|
|
||||||
self.customer.address = "Patio Bonito"
|
|
||||||
self.customer.save()
|
|
||||||
|
|
||||||
def test_add_purchase(self):
|
|
||||||
form_data = {
|
|
||||||
"csrfmiddlewaretoken": _csrf_token,
|
|
||||||
"customer": self.customer.id,
|
|
||||||
"date": "2024-08-03",
|
|
||||||
"payment_method": "CASH",
|
|
||||||
"phone": "sfasfd",
|
|
||||||
"description": "dasdadad",
|
|
||||||
"saleline_set-TOTAL_FORMS": "1",
|
|
||||||
"saleline_set-INITIAL_FORMS": "0",
|
|
||||||
"saleline_set-MIN_NUM_FORMS": "0",
|
|
||||||
"saleline_set-MAX_NUM_FORMS": "1000",
|
|
||||||
"saleline_set-0-product": "5",
|
|
||||||
"saleline_set-0-quantity": "1",
|
|
||||||
"saleline_set-0-unit_price": "500",
|
|
||||||
"saleline_set-0-description": "afasdfasdf",
|
|
||||||
"saleline_set-0-sale": "",
|
|
||||||
"saleline_set-0-id": "",
|
|
||||||
"quantity_lines": "1",
|
|
||||||
"quantity_products": "1",
|
|
||||||
"ammount": "500",
|
|
||||||
"form": ""
|
|
||||||
}
|
|
||||||
|
|
||||||
purchase_form = PurchaseForm(data=form_data)
|
|
||||||
purchase_form.is_valid()
|
|
||||||
|
|
||||||
# raise Exception(purchase_form)
|
|
||||||
self.assertTrue(purchase_form.is_valid())
|
|
@ -1,34 +0,0 @@
|
|||||||
from django.urls import path, include
|
|
||||||
from rest_framework.routers import DefaultRouter
|
|
||||||
|
|
||||||
from . import views
|
|
||||||
from . import api_views
|
|
||||||
|
|
||||||
app_name = 'don_confiao'
|
|
||||||
|
|
||||||
router = DefaultRouter()
|
|
||||||
router.register(r'sales', api_views.SaleView, basename='sale')
|
|
||||||
router.register(r'customers', api_views.CustomerView, basename='customer')
|
|
||||||
router.register(r'products', api_views.ProductView, basename='product')
|
|
||||||
router.register(r'reconciliate_jar', api_views.ReconciliateJarModelView,
|
|
||||||
basename='reconciliate_jar')
|
|
||||||
|
|
||||||
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("lista_productos", views.ProductListView.as_view(), name='product_list'),
|
|
||||||
path("importar_productos", views.import_products, name="import_products"),
|
|
||||||
path("importar_terceros", views.import_customers, name="import_customers"),
|
|
||||||
path("exportar_ventas_para_tryton",
|
|
||||||
views.exportar_ventas_para_tryton,
|
|
||||||
name="exportar_ventas_para_tryton"),
|
|
||||||
path("resumen_compra/<int:id>", views.purchase_summary, name="purchase_summary"),
|
|
||||||
path("resumen_compra_json/<int:id>", api_views.SaleSummary.as_view(), name="purchase_json_summary"),
|
|
||||||
path("payment_methods/all/select_format", api_views.PaymentMethodView.as_view(), name="payment_methods_to_select"),
|
|
||||||
path('purchases/for_reconciliation', api_views.SalesForReconciliationView.as_view(), name='sales_for_reconciliation'),
|
|
||||||
path('reconciliate_jar', api_views.ReconciliateJarView.as_view()),
|
|
||||||
path('api/admin_code/validate/<code>', api_views.AdminCodeValidateView.as_view()),
|
|
||||||
path('api/', include(router.urls)),
|
|
||||||
]
|
|
@ -1,239 +0,0 @@
|
|||||||
from django.shortcuts import render
|
|
||||||
from django.http import HttpResponse, HttpResponseRedirect, JsonResponse
|
|
||||||
from django.views.generic import ListView
|
|
||||||
from django.db import transaction
|
|
||||||
|
|
||||||
from .models import (
|
|
||||||
Sale, SaleLine, Product, Customer, ProductCategory, Payment, PaymentMethods, ReconciliationJar)
|
|
||||||
from .forms import (
|
|
||||||
ImportProductsForm,
|
|
||||||
ImportCustomersForm,
|
|
||||||
PurchaseForm,
|
|
||||||
SaleLineFormSet,
|
|
||||||
PurchaseSummaryForm)
|
|
||||||
|
|
||||||
import csv
|
|
||||||
import io
|
|
||||||
import json
|
|
||||||
from decimal import Decimal
|
|
||||||
|
|
||||||
|
|
||||||
class DecimalEncoder(json.JSONEncoder):
|
|
||||||
def default(self, obj):
|
|
||||||
if isinstance(obj, Decimal):
|
|
||||||
return float(obj)
|
|
||||||
return json.JSONEncoder.default(self, obj)
|
|
||||||
|
|
||||||
|
|
||||||
def index(request):
|
|
||||||
return render(request, 'don_confiao/index.html')
|
|
||||||
|
|
||||||
|
|
||||||
def buy(request):
|
|
||||||
if request.method == "POST":
|
|
||||||
sale_form = PurchaseForm(request.POST)
|
|
||||||
line_formset = SaleLineFormSet(request.POST)
|
|
||||||
sale_summary_form = PurchaseSummaryForm(request.POST)
|
|
||||||
forms_are_valid = all([
|
|
||||||
sale_form.is_valid(),
|
|
||||||
line_formset.is_valid(),
|
|
||||||
sale_summary_form.is_valid()
|
|
||||||
])
|
|
||||||
payment_method = request.POST.get('payment_method')
|
|
||||||
valid_payment_methods = [PaymentMethods.CASH]
|
|
||||||
valid_payment_method = payment_method in valid_payment_methods
|
|
||||||
if forms_are_valid:
|
|
||||||
with transaction.atomic():
|
|
||||||
sale = sale_form.save()
|
|
||||||
line_formset.instance = sale
|
|
||||||
line_formset.save()
|
|
||||||
Payment.total_payment_from_sale(
|
|
||||||
payment_method,
|
|
||||||
sale
|
|
||||||
)
|
|
||||||
return HttpResponseRedirect("compras")
|
|
||||||
else:
|
|
||||||
sale_form = PurchaseForm()
|
|
||||||
line_formset = SaleLineFormSet()
|
|
||||||
sale_summary_form = PurchaseSummaryForm()
|
|
||||||
return render(
|
|
||||||
request,
|
|
||||||
'don_confiao/purchase.html',
|
|
||||||
{
|
|
||||||
'sale_form': sale_form,
|
|
||||||
'linea_formset': line_formset,
|
|
||||||
'summary_form': sale_summary_form,
|
|
||||||
'list_products': json.dumps(Product.to_list(), cls=DecimalEncoder),
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def purchases(request):
|
|
||||||
purchases = Sale.objects.all()
|
|
||||||
context = {
|
|
||||||
"purchases": purchases,
|
|
||||||
}
|
|
||||||
return render(request, 'don_confiao/purchases.html', context)
|
|
||||||
|
|
||||||
|
|
||||||
def products(request):
|
|
||||||
return JsonResponse(Product.to_list(), 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 import_customers(request):
|
|
||||||
if request.method == "POST":
|
|
||||||
form = ImportCustomersForm(request.POST, request.FILES)
|
|
||||||
if form.is_valid():
|
|
||||||
handle_import_customers_file(request.FILES["csv_file"])
|
|
||||||
return HttpResponseRedirect("productos")
|
|
||||||
else:
|
|
||||||
form = ImportCustomersForm()
|
|
||||||
return render(
|
|
||||||
request,
|
|
||||||
"don_confiao/import_customers.html",
|
|
||||||
{'form': form}
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def reconciliations(request):
|
|
||||||
return HttpResponse('<h1>Reconciliaciones</h1>')
|
|
||||||
|
|
||||||
|
|
||||||
def purchase_summary(request, id):
|
|
||||||
purchase = Sale.objects.get(pk=id)
|
|
||||||
return render(
|
|
||||||
request,
|
|
||||||
"don_confiao/purchase_summary.html",
|
|
||||||
{
|
|
||||||
"purchase": purchase
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
def handle_import_customers_file(csv_file):
|
|
||||||
data = io.StringIO(csv_file.read().decode('utf-8'))
|
|
||||||
reader = csv.DictReader(data, quotechar='"')
|
|
||||||
for row in reader:
|
|
||||||
customer, created = Customer.objects.update_or_create(
|
|
||||||
name=row['nombre'],
|
|
||||||
defaults={
|
|
||||||
'email': row['correo'],
|
|
||||||
'phone': row['telefono']
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def exportar_ventas_para_tryton(request):
|
|
||||||
tryton_sales_header = [
|
|
||||||
"Tercero",
|
|
||||||
"Dirección de facturación",
|
|
||||||
"Dirección de envío",
|
|
||||||
"Descripción",
|
|
||||||
"Referencia",
|
|
||||||
"Fecha venta",
|
|
||||||
"Plazo de pago",
|
|
||||||
"Almacén",
|
|
||||||
"Moneda",
|
|
||||||
"Líneas/Producto",
|
|
||||||
"Líneas/Cantidad",
|
|
||||||
"Líneas/Precio unitario",
|
|
||||||
"Líneas/Unidad",
|
|
||||||
"Empresa",
|
|
||||||
"Tienda",
|
|
||||||
"Terminal de venta",
|
|
||||||
"Autorecogida",
|
|
||||||
"Comentario"
|
|
||||||
]
|
|
||||||
|
|
||||||
if request.method == "GET":
|
|
||||||
response = HttpResponse(content_type='text/csv')
|
|
||||||
response['Content-Disposition'] = "attachment; filename=sales.csv"
|
|
||||||
writer = csv.writer(response)
|
|
||||||
writer.writerow(tryton_sales_header)
|
|
||||||
|
|
||||||
sales = Sale.objects.all()
|
|
||||||
|
|
||||||
for sale in sales:
|
|
||||||
sale_lines = SaleLine.objects.filter(sale=sale.id)
|
|
||||||
if not sale_lines:
|
|
||||||
continue
|
|
||||||
lines = []
|
|
||||||
first_sale_line = sale_lines[0]
|
|
||||||
customer_info = [sale.customer.name] * 3 + [sale.description] * 2
|
|
||||||
first_line = customer_info + [
|
|
||||||
sale.date,
|
|
||||||
"Contado",
|
|
||||||
"Almacén",
|
|
||||||
"Peso colombiano",
|
|
||||||
first_sale_line.product.name,
|
|
||||||
first_sale_line.quantity,
|
|
||||||
"Unidad",
|
|
||||||
first_sale_line.unit_price,
|
|
||||||
"TIENDA LA ILUSIÓN",
|
|
||||||
"Tienda La Ilusion",
|
|
||||||
"La Ilusion",
|
|
||||||
True,
|
|
||||||
sale.description]
|
|
||||||
lines.append(first_line)
|
|
||||||
for line in sale_lines[1:]:
|
|
||||||
lines.append([""]*9+[
|
|
||||||
line.product.name,
|
|
||||||
line.quantity,
|
|
||||||
line.unit_price,
|
|
||||||
"Unidad"]+[""]*5)
|
|
||||||
for row in lines:
|
|
||||||
writer.writerow(row)
|
|
||||||
|
|
||||||
return response
|
|
||||||
|
|
||||||
|
|
||||||
class ProductListView(ListView):
|
|
||||||
model = Product
|
|
||||||
template_model = 'don_confiao/product_list.html'
|
|
||||||
|
|
||||||
def get_queryset(self):
|
|
||||||
name = self.request.GET.get('name')
|
|
||||||
if name:
|
|
||||||
return Product.objects.filter(name__icontains=name)
|
|
||||||
return Product.objects.all()
|
|
@ -1,22 +0,0 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
"""Django's command-line utility for administrative tasks."""
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
"""Run administrative tasks."""
|
|
||||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'tienda_ilusion.settings')
|
|
||||||
try:
|
|
||||||
from django.core.management import execute_from_command_line
|
|
||||||
except ImportError as exc:
|
|
||||||
raise ImportError(
|
|
||||||
"Couldn't import Django. Are you sure it's installed and "
|
|
||||||
"available on your PYTHONPATH environment variable? Did you "
|
|
||||||
"forget to activate a virtual environment?"
|
|
||||||
) from exc
|
|
||||||
execute_from_command_line(sys.argv)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
main()
|
|
@ -1,16 +0,0 @@
|
|||||||
"""
|
|
||||||
ASGI config for tienda_ilusion project.
|
|
||||||
|
|
||||||
It exposes the ASGI callable as a module-level variable named ``application``.
|
|
||||||
|
|
||||||
For more information on this file, see
|
|
||||||
https://docs.djangoproject.com/en/5.0/howto/deployment/asgi/
|
|
||||||
"""
|
|
||||||
|
|
||||||
import os
|
|
||||||
|
|
||||||
from django.core.asgi import get_asgi_application
|
|
||||||
|
|
||||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'tienda_ilusion.settings')
|
|
||||||
|
|
||||||
application = get_asgi_application()
|
|
@ -1,128 +0,0 @@
|
|||||||
"""
|
|
||||||
Django settings for tienda_ilusion project.
|
|
||||||
|
|
||||||
Generated by 'django-admin startproject' using Django 5.0.6.
|
|
||||||
|
|
||||||
For more information on this file, see
|
|
||||||
https://docs.djangoproject.com/en/5.0/topics/settings/
|
|
||||||
|
|
||||||
For the full list of settings and their values, see
|
|
||||||
https://docs.djangoproject.com/en/5.0/ref/settings/
|
|
||||||
"""
|
|
||||||
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
|
||||||
BASE_DIR = Path(__file__).resolve().parent.parent
|
|
||||||
|
|
||||||
|
|
||||||
# Quick-start development settings - unsuitable for production
|
|
||||||
# See https://docs.djangoproject.com/en/5.0/howto/deployment/checklist/
|
|
||||||
|
|
||||||
# SECURITY WARNING: keep the secret key used in production secret!
|
|
||||||
SECRET_KEY = 'django-insecure-zh6rinl@8y7g(cf781snisx2j%p^c#d&b2@@9cqe!v@4yv8x=v'
|
|
||||||
|
|
||||||
# SECURITY WARNING: don't run with debug turned on in production!
|
|
||||||
DEBUG = True
|
|
||||||
|
|
||||||
ALLOWED_HOSTS = []
|
|
||||||
|
|
||||||
|
|
||||||
# Application definition
|
|
||||||
|
|
||||||
INSTALLED_APPS = [
|
|
||||||
'don_confiao.apps.DonConfiaoConfig',
|
|
||||||
'django.contrib.admin',
|
|
||||||
'django.contrib.auth',
|
|
||||||
'django.contrib.contenttypes',
|
|
||||||
'django.contrib.sessions',
|
|
||||||
'django.contrib.messages',
|
|
||||||
'django.contrib.staticfiles',
|
|
||||||
'rest_framework',
|
|
||||||
# 'don_confiao'
|
|
||||||
]
|
|
||||||
|
|
||||||
MIDDLEWARE = [
|
|
||||||
'django.middleware.security.SecurityMiddleware',
|
|
||||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
|
||||||
'django.middleware.common.CommonMiddleware',
|
|
||||||
'django.middleware.csrf.CsrfViewMiddleware',
|
|
||||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
|
||||||
'django.contrib.messages.middleware.MessageMiddleware',
|
|
||||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
|
||||||
]
|
|
||||||
|
|
||||||
ROOT_URLCONF = 'tienda_ilusion.urls'
|
|
||||||
|
|
||||||
TEMPLATES = [
|
|
||||||
{
|
|
||||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
|
||||||
'DIRS': [],
|
|
||||||
'APP_DIRS': True,
|
|
||||||
'OPTIONS': {
|
|
||||||
'context_processors': [
|
|
||||||
'django.template.context_processors.debug',
|
|
||||||
'django.template.context_processors.request',
|
|
||||||
'django.contrib.auth.context_processors.auth',
|
|
||||||
'django.contrib.messages.context_processors.messages',
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
WSGI_APPLICATION = 'tienda_ilusion.wsgi.application'
|
|
||||||
|
|
||||||
|
|
||||||
# Database
|
|
||||||
# https://docs.djangoproject.com/en/5.0/ref/settings/#databases
|
|
||||||
|
|
||||||
DATABASES = {
|
|
||||||
'default': {
|
|
||||||
'ENGINE': 'django.db.backends.sqlite3',
|
|
||||||
'NAME': BASE_DIR / 'db.sqlite3',
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
# Password validation
|
|
||||||
# https://docs.djangoproject.com/en/5.0/ref/settings/#auth-password-validators
|
|
||||||
|
|
||||||
AUTH_PASSWORD_VALIDATORS = [
|
|
||||||
{
|
|
||||||
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
# Internationalization
|
|
||||||
# https://docs.djangoproject.com/en/5.0/topics/i18n/
|
|
||||||
|
|
||||||
LANGUAGE_CODE = 'en-us'
|
|
||||||
|
|
||||||
TIME_ZONE = 'UTC'
|
|
||||||
|
|
||||||
USE_I18N = True
|
|
||||||
|
|
||||||
USE_TZ = True
|
|
||||||
|
|
||||||
|
|
||||||
# Static files (CSS, JavaScript, Images)
|
|
||||||
# https://docs.djangoproject.com/en/5.0/howto/static-files/
|
|
||||||
|
|
||||||
STATIC_URL = 'static/'
|
|
||||||
|
|
||||||
# Default primary key field type
|
|
||||||
# https://docs.djangoproject.com/en/5.0/ref/settings/#default-auto-field
|
|
||||||
|
|
||||||
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
|
|
||||||
|
|
||||||
FIXTURE_DIRS = ['don_confiao/tests/Fixtures']
|
|
@ -1,26 +0,0 @@
|
|||||||
"""
|
|
||||||
URL configuration for tienda_ilusion project.
|
|
||||||
|
|
||||||
The `urlpatterns` list routes URLs to views. For more information please see:
|
|
||||||
https://docs.djangoproject.com/en/5.0/topics/http/urls/
|
|
||||||
Examples:
|
|
||||||
Function views
|
|
||||||
1. Add an import: from my_app import views
|
|
||||||
2. Add a URL to urlpatterns: path('', views.home, name='home')
|
|
||||||
Class-based views
|
|
||||||
1. Add an import: from other_app.views import Home
|
|
||||||
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
|
|
||||||
Including another URLconf
|
|
||||||
1. Import the include() function: from django.urls import include, path
|
|
||||||
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
|
|
||||||
"""
|
|
||||||
from django.contrib import admin
|
|
||||||
from django.urls import include, path
|
|
||||||
|
|
||||||
|
|
||||||
app_name = "don_confiao"
|
|
||||||
|
|
||||||
urlpatterns = [
|
|
||||||
path("don_confiao/", include("don_confiao.urls")),
|
|
||||||
path('admin/', admin.site.urls),
|
|
||||||
]
|
|
@ -1,16 +0,0 @@
|
|||||||
"""
|
|
||||||
WSGI config for tienda_ilusion project.
|
|
||||||
|
|
||||||
It exposes the WSGI callable as a module-level variable named ``application``.
|
|
||||||
|
|
||||||
For more information on this file, see
|
|
||||||
https://docs.djangoproject.com/en/5.0/howto/deployment/wsgi/
|
|
||||||
"""
|
|
||||||
|
|
||||||
import os
|
|
||||||
|
|
||||||
from django.core.wsgi import get_wsgi_application
|
|
||||||
|
|
||||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'tienda_ilusion.settings')
|
|
||||||
|
|
||||||
application = get_wsgi_application()
|
|
Loading…
Reference in New Issue
Block a user