remove: django.

This commit is contained in:
Mono Mono 2025-02-08 16:32:29 -05:00
parent 27cbeca6cb
commit 88b262e9cf
88 changed files with 0 additions and 3051 deletions

View File

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

View File

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

View File

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

View File

@ -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 nombre correo telefono
2 Alejandro Ayala mono@disroot.org 3232321
3 Mono Francisco pablo@onecluster.org 321312312
4 Pablo Bolivar alejo@onecluster.org 3243242

View File

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

View File

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

View File

@ -1 +0,0 @@
#!/usr/bin/env python3

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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 = [
]

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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 = [
]

View File

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

View File

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

View File

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

View File

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

View File

@ -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();
});
});

View File

@ -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 = '';
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1 +0,0 @@
#!/usr/bin/env python3

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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