Merge pull request 'Generando cuadre del tarro en vuetify' (#83) from streamline_reconciliation_jar_process_#69 into main
Reviewed-on: OneTeam/don_confiao#83
This commit is contained in:
commit
023beaa0ee
29
Rakefile
29
Rakefile
@ -10,7 +10,7 @@ namespace :live do
|
|||||||
compose('up', '--build', '-d', compose: DOCKER_COMPOSE)
|
compose('up', '--build', '-d', compose: DOCKER_COMPOSE)
|
||||||
end
|
end
|
||||||
|
|
||||||
desc 'monitorear salida'
|
desc 'monitorear salida'
|
||||||
task :tail do
|
task :tail do
|
||||||
compose('logs', '-f', 'django', compose: DOCKER_COMPOSE)
|
compose('logs', '-f', 'django', compose: DOCKER_COMPOSE)
|
||||||
end
|
end
|
||||||
@ -20,7 +20,12 @@ namespace :live do
|
|||||||
compose('logs', '-f', '-n 50', 'django', compose: DOCKER_COMPOSE)
|
compose('logs', '-f', '-n 50', 'django', compose: DOCKER_COMPOSE)
|
||||||
end
|
end
|
||||||
|
|
||||||
desc 'detener entorno'
|
desc 'iniciar entorno'
|
||||||
|
task :start do
|
||||||
|
compose('start', compose: DOCKER_COMPOSE)
|
||||||
|
end
|
||||||
|
|
||||||
|
desc 'bajar entorno'
|
||||||
task :down do
|
task :down do
|
||||||
compose('down', compose: DOCKER_COMPOSE)
|
compose('down', compose: DOCKER_COMPOSE)
|
||||||
end
|
end
|
||||||
@ -52,6 +57,26 @@ namespace :live do
|
|||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
desc 'Desarrollo'
|
||||||
|
namespace :dev do
|
||||||
|
|
||||||
|
desc 'correr test de django'
|
||||||
|
task :test do
|
||||||
|
compose('exec', 'django', 'python', '/app/manage.py', 'test', '/app/don_confiao')
|
||||||
|
end
|
||||||
|
|
||||||
|
desc 'crear migraciones'
|
||||||
|
task :makemigrations do
|
||||||
|
compose('exec', 'django', 'python', '/app/manage.py', 'makemigrations')
|
||||||
|
end
|
||||||
|
|
||||||
|
desc 'aplicar migraciones'
|
||||||
|
task :migrate do
|
||||||
|
compose('exec', 'django', 'python', '/app/manage.py', 'migrate')
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
def compose(*arg, compose: DOCKER_COMPOSE)
|
def compose(*arg, compose: DOCKER_COMPOSE)
|
||||||
sh "docker compose -f #{compose} #{arg.join(' ')}"
|
sh "docker compose -f #{compose} #{arg.join(' ')}"
|
||||||
end
|
end
|
||||||
|
@ -1,9 +1,13 @@
|
|||||||
from rest_framework import viewsets
|
from rest_framework import viewsets
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
|
from rest_framework.status import HTTP_400_BAD_REQUEST
|
||||||
|
from rest_framework.views import APIView
|
||||||
|
|
||||||
from .models import Sale, SaleLine, Customer, Product
|
from .models import Sale, SaleLine, Customer, Product, ReconciliationJar
|
||||||
from .serializers import SaleSerializer, ProductSerializer, CustomerSerializer
|
from .serializers import SaleSerializer, ProductSerializer, CustomerSerializer, ReconciliationJarSerializer
|
||||||
|
|
||||||
|
from decimal import Decimal
|
||||||
|
import json
|
||||||
|
|
||||||
class SaleView(viewsets.ModelViewSet):
|
class SaleView(viewsets.ModelViewSet):
|
||||||
queryset = Sale.objects.all()
|
queryset = Sale.objects.all()
|
||||||
@ -46,3 +50,53 @@ class ProductView(viewsets.ModelViewSet):
|
|||||||
class CustomerView(viewsets.ModelViewSet):
|
class CustomerView(viewsets.ModelViewSet):
|
||||||
queryset = Customer.objects.all()
|
queryset = Customer.objects.all()
|
||||||
serializer_class = CustomerSerializer
|
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()
|
||||||
|
@ -3,7 +3,7 @@ from django.forms.models import inlineformset_factory
|
|||||||
|
|
||||||
from django.forms.widgets import DateInput, DateTimeInput
|
from django.forms.widgets import DateInput, DateTimeInput
|
||||||
|
|
||||||
from .models import Sale, SaleLine, ReconciliationJar, PaymentMethods
|
from .models import Sale, SaleLine, PaymentMethods
|
||||||
|
|
||||||
readonly_number_widget = forms.NumberInput(attrs={'readonly': 'readonly'})
|
readonly_number_widget = forms.NumberInput(attrs={'readonly': 'readonly'})
|
||||||
|
|
||||||
@ -64,18 +64,3 @@ SaleLineFormSet = inlineformset_factory(
|
|||||||
extra=1,
|
extra=1,
|
||||||
fields='__all__'
|
fields='__all__'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class ReconciliationJarForm(forms.ModelForm):
|
|
||||||
class Meta:
|
|
||||||
model = ReconciliationJar
|
|
||||||
fields = [
|
|
||||||
'date_time',
|
|
||||||
'description',
|
|
||||||
'reconcilier',
|
|
||||||
'cash_taken',
|
|
||||||
'cash_discrepancy',
|
|
||||||
]
|
|
||||||
widgets = {
|
|
||||||
'date_time': DateTimeInput(attrs={'type': 'datetime-local'})
|
|
||||||
}
|
|
||||||
|
@ -0,0 +1,26 @@
|
|||||||
|
<template>
|
||||||
|
<span>{{ formattedValue }}</span>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
value: {
|
||||||
|
type: Number,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
locale: {
|
||||||
|
type: String,
|
||||||
|
default: 'es-CO',
|
||||||
|
},
|
||||||
|
currency: {
|
||||||
|
type: String,
|
||||||
|
default: 'COP',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
formattedValue() {
|
||||||
|
return new Intl.NumberFormat(this.locale, { style: 'currency', currency: this.currency }).format(this.value);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
@ -29,6 +29,7 @@
|
|||||||
menuItems: [
|
menuItems: [
|
||||||
{ title: 'Inicio', route: '/'},
|
{ title: 'Inicio', route: '/'},
|
||||||
{ title: 'Comprar', route:'/comprar'},
|
{ title: 'Comprar', route:'/comprar'},
|
||||||
|
{ title: 'Cuadrar tarro', route: '/cuadrar_tarro'}
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
watch: {
|
watch: {
|
||||||
|
@ -0,0 +1,213 @@
|
|||||||
|
<template>
|
||||||
|
<v-container>
|
||||||
|
<v-toolbar>
|
||||||
|
<v-toolbar-title> Cuadre del Tarro </v-toolbar-title>
|
||||||
|
</v-toolbar>
|
||||||
|
<v-card>
|
||||||
|
<v-card-text>
|
||||||
|
<v-form ref="taker" v-model="valid">
|
||||||
|
<v-text-field
|
||||||
|
v-model="reconciliation.date_time"
|
||||||
|
label="Fecha"
|
||||||
|
type="datetime-local"
|
||||||
|
:rules="[rules.required]"
|
||||||
|
required
|
||||||
|
readonly
|
||||||
|
></v-text-field>
|
||||||
|
<v-text-field
|
||||||
|
v-model="reconciliation.reconcilier"
|
||||||
|
label="Cajero"
|
||||||
|
:rules="[rules.required]"
|
||||||
|
required
|
||||||
|
></v-text-field>
|
||||||
|
<v-text-field
|
||||||
|
v-model="reconciliation.total_cash_purchases"
|
||||||
|
label="Total Ventas en efectivo"
|
||||||
|
:rules="[rules.required]"
|
||||||
|
prefix="$"
|
||||||
|
type="number"
|
||||||
|
readonly
|
||||||
|
></v-text-field>
|
||||||
|
<v-text-field
|
||||||
|
v-model="reconciliation.cash_taken"
|
||||||
|
label="Dinero Recogido"
|
||||||
|
:rules="[rules.required]"
|
||||||
|
prefix="$"
|
||||||
|
type="number"
|
||||||
|
></v-text-field>
|
||||||
|
<v-text-field
|
||||||
|
v-model="reconciliation.cash_discrepancy"
|
||||||
|
label="Descuadre"
|
||||||
|
:rules="[rules.integer]"
|
||||||
|
prefix="$"
|
||||||
|
type="number"
|
||||||
|
></v-text-field>
|
||||||
|
<v-btn @click="submit" color="green">Recoger Dinero</v-btn>
|
||||||
|
</v-form>
|
||||||
|
</v-card-text>
|
||||||
|
</v-card>
|
||||||
|
<v-tabs v-model="selectedTab">
|
||||||
|
<v-tab
|
||||||
|
v-for="(purchases, payment_method) in summary.purchases"
|
||||||
|
:key="payment_method"
|
||||||
|
:value="payment_method"
|
||||||
|
>
|
||||||
|
{{ payment_method }} <CurrencyText :value="totalByMethod(payment_method)"</CurrencyText>
|
||||||
|
</v-tab>
|
||||||
|
</v-tabs>
|
||||||
|
|
||||||
|
<v-tabs-window v-model="selectedTab">
|
||||||
|
<v-card>
|
||||||
|
<v-card-text>
|
||||||
|
<v-tabs-window-item
|
||||||
|
v-for="(purchases, payment_method) in summary.purchases"
|
||||||
|
:key="payment_method"
|
||||||
|
:value="payment_method"
|
||||||
|
>
|
||||||
|
<v-data-table-virtual
|
||||||
|
:headers="summary.headers"
|
||||||
|
:items="summary.purchases[payment_method]"
|
||||||
|
>
|
||||||
|
<template v-slot:item.id="{ item }">
|
||||||
|
<v-btn @click="openSummaryModal(item.id)">{{ item.id }}</v-btn>
|
||||||
|
</template>
|
||||||
|
<template v-slot:item.total="{ item }">
|
||||||
|
<CurrencyText :value="parseFloat(item.total)"></CurrencyText>
|
||||||
|
</template>
|
||||||
|
</v-data-table-virtual>
|
||||||
|
</v-tabs-window-item>
|
||||||
|
<SummaryPurchaseModal :id="selectedPurchaseId" ref="summaryModal" />
|
||||||
|
</v-card-text>
|
||||||
|
</v-card>
|
||||||
|
</v-tabs-window>
|
||||||
|
</v-container>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import CurrencyText from './CurrencyText.vue';
|
||||||
|
import SummaryPurchaseModal from './SummaryPurchaseModal.vue';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'ReconciliationJar',
|
||||||
|
props: {
|
||||||
|
msg: String,
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
SummaryPurchaseModal,
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
valid: null,
|
||||||
|
selectedPurchaseId: null,
|
||||||
|
selectedTab: 'CASH',
|
||||||
|
reconciliation: {
|
||||||
|
date_time: '',
|
||||||
|
total_cash_purchases: 0,
|
||||||
|
cash_taken: 0,
|
||||||
|
cash_discrepancy: 0,
|
||||||
|
other_totals: {
|
||||||
|
},
|
||||||
|
cash_purchases: [],
|
||||||
|
},
|
||||||
|
summary: {
|
||||||
|
headers: [
|
||||||
|
{title: 'Id', value: 'id'},
|
||||||
|
{title: 'Fecha', value: 'date'},
|
||||||
|
{title: 'Cliente', value: 'customer.name'},
|
||||||
|
{title: 'Total', value: 'total'},
|
||||||
|
],
|
||||||
|
purchases: {},
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
required: value => !!value || 'Requerido.',
|
||||||
|
integer: value => !!value || value === 0 || 'Requerido.',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.fetchPurchases();
|
||||||
|
this.reconciliation.date_time = this.getCurrentDate();
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
'reconciliation.cash_taken'() {
|
||||||
|
this.updateDiscrepancy();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
totalByMethod(method) {
|
||||||
|
if (method in this.summary.purchases) {
|
||||||
|
return this.summary.purchases[method].reduce((a, b) => a + parseFloat(b.total), 0);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
},
|
||||||
|
idsBymethod(method) {
|
||||||
|
if (method in this.summary.purchases) {
|
||||||
|
return this.summary.purchases[method].map(purchase => purchase.id)
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
},
|
||||||
|
processOtherMethods() {
|
||||||
|
for (const method of Object.keys(this.summary.purchases)) {
|
||||||
|
if (method !== 'CASH') {
|
||||||
|
this.reconciliation.other_totals[method] = {
|
||||||
|
total: this.totalByMethod(method),
|
||||||
|
purchases: this.idsBymethod(method),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
updateDiscrepancy() {
|
||||||
|
this.reconciliation.cash_discrepancy = (this.reconciliation.total_cash_purchases || 0 ) - (this.reconciliation.cash_taken || 0);
|
||||||
|
},
|
||||||
|
getCurrentDate() {
|
||||||
|
const today = new Date();
|
||||||
|
const gmtOffSet = -5;
|
||||||
|
const localDate = new Date(today.getTime() + (gmtOffSet * 60 * 60 * 1000));
|
||||||
|
// Formatear la fecha y hora en el formato YYYY-MM-DDTHH:MM
|
||||||
|
const formattedDate = localDate.toISOString().slice(0,16);
|
||||||
|
return formattedDate;
|
||||||
|
},
|
||||||
|
openSummaryModal(id) {
|
||||||
|
this.selectedPurchaseId = id;
|
||||||
|
this.$refs.summaryModal.dialog = true;
|
||||||
|
},
|
||||||
|
fetchPurchases() {
|
||||||
|
const endpoint = '/don_confiao/purchases/for_reconciliation';
|
||||||
|
fetch(endpoint)
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
this.summary.purchases = data;
|
||||||
|
this.reconciliation.cash_purchases = this.idsBymethod('CASH');
|
||||||
|
this.reconciliation.total_cash_purchases = this.totalByMethod('CASH');
|
||||||
|
this.processOtherMethods();
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error(error);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
async submit() {
|
||||||
|
this.$refs.taker.validate();
|
||||||
|
if (this.valid) {
|
||||||
|
try {
|
||||||
|
const response = await fetch('/don_confiao/reconciliate_jar', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify(this.reconciliation),
|
||||||
|
});
|
||||||
|
if (response.ok) {
|
||||||
|
const data = await response.json();
|
||||||
|
console.log('Cuadre enviado:', data);
|
||||||
|
this.$router.push({path: "/"});
|
||||||
|
} else {
|
||||||
|
console.error('Error al enviar el cuadre', response.statusText);
|
||||||
|
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error de red:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
@ -0,0 +1,30 @@
|
|||||||
|
<template>
|
||||||
|
<v-dialog v-model="dialog" max-width="400">
|
||||||
|
<v-card>
|
||||||
|
<v-card-text>
|
||||||
|
<SummaryPurchase :id="id"/>
|
||||||
|
</v-card-text>
|
||||||
|
<v-card-actions>
|
||||||
|
<v-spacer></v-spacer>
|
||||||
|
<v-btn text @click="dialog = false">Cerrar</v-btn>
|
||||||
|
</v-card-actions>
|
||||||
|
</v-card>
|
||||||
|
</v-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'SummaryPurchase Modal',
|
||||||
|
props: {
|
||||||
|
id: {
|
||||||
|
type: Number,
|
||||||
|
required: true,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
dialog: false,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
@ -0,0 +1,7 @@
|
|||||||
|
<template>
|
||||||
|
<ReconciliationJar />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
//
|
||||||
|
</script>
|
@ -0,0 +1,29 @@
|
|||||||
|
# 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),
|
||||||
|
),
|
||||||
|
]
|
@ -0,0 +1,19 @@
|
|||||||
|
# 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,
|
||||||
|
),
|
||||||
|
]
|
@ -0,0 +1,14 @@
|
|||||||
|
# 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 = [
|
||||||
|
]
|
@ -62,6 +62,31 @@ class Product(models.Model):
|
|||||||
return products_list
|
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):
|
class Sale(models.Model):
|
||||||
customer = models.ForeignKey(Customer, on_delete=models.PROTECT)
|
customer = models.ForeignKey(Customer, on_delete=models.PROTECT)
|
||||||
date = models.DateTimeField("Date")
|
date = models.DateTimeField("Date")
|
||||||
@ -74,6 +99,12 @@ class Sale(models.Model):
|
|||||||
blank=False,
|
blank=False,
|
||||||
null=False
|
null=False
|
||||||
)
|
)
|
||||||
|
reconciliation = models.ForeignKey(
|
||||||
|
ReconciliationJar,
|
||||||
|
on_delete=models.RESTRICT,
|
||||||
|
related_name='Sales',
|
||||||
|
null=True
|
||||||
|
)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f"{self.date} {self.customer}"
|
return f"{self.date} {self.customer}"
|
||||||
@ -122,38 +153,6 @@ class ReconciliationJarSummary():
|
|||||||
return self._payments
|
return self._payments
|
||||||
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
def clean(self):
|
|
||||||
if not self.is_valid:
|
|
||||||
payments = Payment.get_reconciliation_jar_summary().payments
|
|
||||||
else:
|
|
||||||
payments = self.payment_set.all()
|
|
||||||
|
|
||||||
payments_amount = Decimal(sum([p.amount for p in payments]))
|
|
||||||
reconciliation_ammount = Decimal(sum([
|
|
||||||
self.cash_taken,
|
|
||||||
self.cash_discrepancy,
|
|
||||||
]))
|
|
||||||
|
|
||||||
equal_ammounts = reconciliation_ammount.compare(payments_amount) == Decimal('0')
|
|
||||||
if not equal_ammounts:
|
|
||||||
raise ValidationError(
|
|
||||||
{"cash_taken": _("The taken ammount has discrepancy.")}
|
|
||||||
)
|
|
||||||
|
|
||||||
def add_payments(self, payments):
|
|
||||||
for payment in payments:
|
|
||||||
self.payment_set.add(payment)
|
|
||||||
self.is_valid = True
|
|
||||||
|
|
||||||
|
|
||||||
class Payment(models.Model):
|
class Payment(models.Model):
|
||||||
date_time = models.DateTimeField()
|
date_time = models.DateTimeField()
|
||||||
type_payment = models.CharField(
|
type_payment = models.CharField(
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
|
|
||||||
from .models import Sale, SaleLine, Product, Customer
|
from .models import Sale, SaleLine, Product, Customer, ReconciliationJar
|
||||||
|
|
||||||
|
|
||||||
class SaleLineSerializer(serializers.ModelSerializer):
|
class SaleLineSerializer(serializers.ModelSerializer):
|
||||||
@ -20,7 +20,21 @@ class ProductSerializer(serializers.ModelSerializer):
|
|||||||
model = Product
|
model = Product
|
||||||
fields = ['id', 'name', 'price', 'measuring_unit', 'categories']
|
fields = ['id', 'name', 'price', 'measuring_unit', 'categories']
|
||||||
|
|
||||||
|
|
||||||
class CustomerSerializer(serializers.ModelSerializer):
|
class CustomerSerializer(serializers.ModelSerializer):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Customer
|
model = Customer
|
||||||
fields = ['id', 'name', 'address', 'email', 'phone']
|
fields = ['id', 'name', 'address', 'email', 'phone']
|
||||||
|
|
||||||
|
|
||||||
|
class ReconciliationJarSerializer(serializers.ModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = ReconciliationJar
|
||||||
|
fields = [
|
||||||
|
'id',
|
||||||
|
'date_time',
|
||||||
|
'reconcilier',
|
||||||
|
'cash_taken',
|
||||||
|
'cash_discrepancy',
|
||||||
|
'total_cash_purchases',
|
||||||
|
]
|
||||||
|
@ -10,7 +10,6 @@
|
|||||||
<li><a href='/don_confiao/lista_productos'>Productos</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_productos'>Importar Productos</a></li>
|
||||||
<li><a href='/don_confiao/importar_terceros'>Importar Terceros</a></li>
|
<li><a href='/don_confiao/importar_terceros'>Importar Terceros</a></li>
|
||||||
<li><a href='/don_confiao/cuadrar_tarro'>Cuadrar tarro</a></li>
|
|
||||||
</ul>
|
</ul>
|
||||||
</nav>
|
</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>
|
<p id="page_title" class="text-center decoration-solid font-mono font-bold text-lg page_title">Don Confiao - Tienda la Ilusión</p>
|
||||||
|
@ -1,34 +0,0 @@
|
|||||||
{% extends 'don_confiao/base.html' %}
|
|
||||||
{% block content %}
|
|
||||||
|
|
||||||
{% if summary.total %}
|
|
||||||
<div class="reconciliate_jar summary" style="border: solid 1px brown; margin: 10px">
|
|
||||||
<h2>Pagos No reconciliados</h2>
|
|
||||||
<table style="border: solid 1px blue; margin: 10px">
|
|
||||||
<thead>
|
|
||||||
<tr><th>Fecha</th><th>Monto</th></tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{% for payment in summary.payments %}
|
|
||||||
<tr><td>{{ payment.date_time }}</td><td>{{ payment.amount }}</td></tr>
|
|
||||||
{% endfor %}
|
|
||||||
</tbody>
|
|
||||||
<tfoot>
|
|
||||||
<tr><th>Total</th><td>{{ summary.total }}</td></tr>
|
|
||||||
</tfoot>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
<form method="POST">
|
|
||||||
<table style="border: solid 1px blue; margin: 10px">
|
|
||||||
{% csrf_token %}
|
|
||||||
{{ form.as_table }}
|
|
||||||
</table>
|
|
||||||
<br/><button name="form" type="submit" >Recoger dinero</button>
|
|
||||||
</form>
|
|
||||||
{% else %}
|
|
||||||
<div class="reconciliate_jar information noform">
|
|
||||||
<h2>No hay pagos registrados.</h2>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% endblock %}
|
|
@ -1,88 +0,0 @@
|
|||||||
from django.test import TestCase
|
|
||||||
from django.core.exceptions import ValidationError
|
|
||||||
from ..models import Payment, ReconciliationJar
|
|
||||||
|
|
||||||
|
|
||||||
class TestBilling(TestCase):
|
|
||||||
|
|
||||||
def test_reconciliation_jar_summary(self):
|
|
||||||
cash_payment1, cash_payment2 = self._create_two_cash_payments()
|
|
||||||
jar_summary = Payment.get_reconciliation_jar_summary()
|
|
||||||
self.assertEqual(164000, jar_summary.total)
|
|
||||||
self.assertSetEqual(
|
|
||||||
{cash_payment1, cash_payment2},
|
|
||||||
set(jar_summary.payments)
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_reconciliation_jar_summary_use_only_cash(self):
|
|
||||||
cash_payment1, cash_payment2 = self._create_two_cash_payments()
|
|
||||||
|
|
||||||
confiar_payment = Payment()
|
|
||||||
confiar_payment.date_time = '2024-07-07 16:00:00'
|
|
||||||
confiar_payment.type_payment = 'CONFIAR'
|
|
||||||
confiar_payment.amount = 85000
|
|
||||||
confiar_payment.save()
|
|
||||||
|
|
||||||
bancolombia_payment = Payment()
|
|
||||||
bancolombia_payment.date_time = '2024-07-07 12:30:00'
|
|
||||||
bancolombia_payment.type_payment = 'BANCOLOMBIA'
|
|
||||||
bancolombia_payment.amount = 12000
|
|
||||||
bancolombia_payment.save()
|
|
||||||
|
|
||||||
jar_summary = Payment.get_reconciliation_jar_summary()
|
|
||||||
self.assertEqual(164000, jar_summary.total)
|
|
||||||
self.assertSetEqual(
|
|
||||||
{cash_payment1, cash_payment2},
|
|
||||||
set(jar_summary.payments)
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_fail_validate_reconciliation_jar_with_discrepancy_values(self):
|
|
||||||
cash_payment1, cash_payment2 = self._create_two_cash_payments()
|
|
||||||
|
|
||||||
jar_summary = Payment.get_reconciliation_jar_summary()
|
|
||||||
|
|
||||||
reconciliation_jar = ReconciliationJar()
|
|
||||||
reconciliation_jar.date_time = '2024-07-13 13:02:00'
|
|
||||||
reconciliation_jar.description = "test reconcialiation jar"
|
|
||||||
reconciliation_jar.reconcilier = 'Jorge'
|
|
||||||
reconciliation_jar.cash_float = 0
|
|
||||||
reconciliation_jar.cash_taken = 0
|
|
||||||
reconciliation_jar.cash_discrepancy = 0
|
|
||||||
reconciliation_jar.save()
|
|
||||||
|
|
||||||
reconciliation_jar.add_payments(jar_summary.payments)
|
|
||||||
with self.assertRaises(ValidationError):
|
|
||||||
reconciliation_jar.clean()
|
|
||||||
|
|
||||||
def test_validate_reconciliation_jar_with_cash_float(self):
|
|
||||||
cash_payment1, cash_payment2 = self._create_two_cash_payments()
|
|
||||||
jar_summary = Payment.get_reconciliation_jar_summary()
|
|
||||||
|
|
||||||
reconciliation_jar = ReconciliationJar()
|
|
||||||
reconciliation_jar.date_time = '2024-07-13 13:02:00'
|
|
||||||
reconciliation_jar.description = "test reconcialiation jar"
|
|
||||||
reconciliation_jar.reconcilier = 'Jorge'
|
|
||||||
reconciliation_jar.cash_taken = jar_summary.total
|
|
||||||
reconciliation_jar.cash_discrepancy = 0
|
|
||||||
reconciliation_jar.save()
|
|
||||||
|
|
||||||
reconciliation_jar.add_payments(jar_summary.payments)
|
|
||||||
reconciliation_jar.clean()
|
|
||||||
reconciliation_jar.save()
|
|
||||||
self.assertTrue(reconciliation_jar.is_valid)
|
|
||||||
|
|
||||||
def _create_two_cash_payments(self):
|
|
||||||
cash_payment1 = Payment()
|
|
||||||
cash_payment1.date_time = '2024-07-07 12:00:00'
|
|
||||||
cash_payment1.type_payment = 'CASH'
|
|
||||||
cash_payment1.amount = 132000
|
|
||||||
cash_payment1.description = 'Saldo en compra'
|
|
||||||
cash_payment1.save()
|
|
||||||
|
|
||||||
cash_payment2 = Payment()
|
|
||||||
cash_payment2.date_time = '2024-07-07 13:05:00'
|
|
||||||
cash_payment2.type_payment = 'CASH'
|
|
||||||
cash_payment2.amount = 32000
|
|
||||||
cash_payment2.save()
|
|
||||||
|
|
||||||
return [cash_payment1, cash_payment2]
|
|
208
tienda_ilusion/don_confiao/tests/test_jar_reconciliation.py
Normal file
208
tienda_ilusion/don_confiao/tests/test_jar_reconciliation.py
Normal file
@ -0,0 +1,208 @@
|
|||||||
|
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):
|
||||||
|
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,
|
||||||
|
],
|
||||||
|
}
|
||||||
|
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), 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 _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
|
@ -1,45 +0,0 @@
|
|||||||
from django.test import Client, TestCase
|
|
||||||
from django.contrib.auth.models import AnonymousUser, User
|
|
||||||
|
|
||||||
from ..models import Payment
|
|
||||||
|
|
||||||
|
|
||||||
class TestReconciliationJarClient(TestCase):
|
|
||||||
def setUp(self):
|
|
||||||
self.client = Client()
|
|
||||||
|
|
||||||
def test_get_summary_info_on_view(self):
|
|
||||||
self._generate_two_cash_payments()
|
|
||||||
response = self.client.get("/don_confiao/cuadrar_tarro")
|
|
||||||
self.assertEqual(response.status_code, 200)
|
|
||||||
self.assertEqual(response.context["summary"].total, 160000)
|
|
||||||
self.assertIn('160000', response.content.decode('utf-8'))
|
|
||||||
|
|
||||||
def test_create_reconciliation_jar(self):
|
|
||||||
self._generate_two_cash_payments()
|
|
||||||
response = self.client.post(
|
|
||||||
"/don_confiao/cuadrar_tarro",
|
|
||||||
{
|
|
||||||
"date_time": "2024-07-20T00:00",
|
|
||||||
"description": "Cuadre de prueba",
|
|
||||||
"reconcilier": "Jorge",
|
|
||||||
"cash_taken": "100000",
|
|
||||||
"cash_discrepancy": "60000",
|
|
||||||
}
|
|
||||||
)
|
|
||||||
self.assertRedirects(response, '/don_confiao/cuadres')
|
|
||||||
|
|
||||||
|
|
||||||
def _generate_two_cash_payments(self):
|
|
||||||
cash_payment1 = Payment()
|
|
||||||
cash_payment1.date_time = '2024-07-07 12:00:00'
|
|
||||||
cash_payment1.type_payment = 'CASH'
|
|
||||||
cash_payment1.amount = 130000
|
|
||||||
cash_payment1.description = 'Saldo en compra'
|
|
||||||
cash_payment1.save()
|
|
||||||
|
|
||||||
cash_payment2 = Payment()
|
|
||||||
cash_payment2.date_time = '2024-07-07 13:05:00'
|
|
||||||
cash_payment2.type_payment = 'CASH'
|
|
||||||
cash_payment2.amount = 30000
|
|
||||||
cash_payment2.save()
|
|
@ -23,10 +23,10 @@ urlpatterns = [
|
|||||||
path("exportar_ventas_para_tryton",
|
path("exportar_ventas_para_tryton",
|
||||||
views.exportar_ventas_para_tryton,
|
views.exportar_ventas_para_tryton,
|
||||||
name="exportar_ventas_para_tryton"),
|
name="exportar_ventas_para_tryton"),
|
||||||
path("cuadrar_tarro", views.reconciliate_jar, name="reconciliate_jar"),
|
|
||||||
path("cuadres", views.reconciliate_jar, name="reconciliations"),
|
|
||||||
path("resumen_compra/<int:id>", views.purchase_summary, name="purchase_summary"),
|
path("resumen_compra/<int:id>", views.purchase_summary, name="purchase_summary"),
|
||||||
path("resumen_compra_json/<int:id>", views.purchase_json_summary, name="purchase_json_summary"),
|
path("resumen_compra_json/<int:id>", views.purchase_json_summary, name="purchase_json_summary"),
|
||||||
path("payment_methods/all/select_format", views.payment_methods_to_select, name="payment_methods_to_select"),
|
path("payment_methods/all/select_format", views.payment_methods_to_select, name="payment_methods_to_select"),
|
||||||
|
path('purchases/for_reconciliation', views.sales_for_reconciliation, name='sales_for_reconciliation'),
|
||||||
|
path('reconciliate_jar', api_views.ReconciliateJarView.as_view()),
|
||||||
path('api/', include(router.urls)),
|
path('api/', include(router.urls)),
|
||||||
]
|
]
|
||||||
|
@ -4,13 +4,12 @@ from django.views.generic import ListView
|
|||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
|
|
||||||
from .models import (
|
from .models import (
|
||||||
Sale, SaleLine, Product, Customer, ProductCategory, Payment, PaymentMethods)
|
Sale, SaleLine, Product, Customer, ProductCategory, Payment, PaymentMethods, ReconciliationJar)
|
||||||
from .forms import (
|
from .forms import (
|
||||||
ImportProductsForm,
|
ImportProductsForm,
|
||||||
ImportCustomersForm,
|
ImportCustomersForm,
|
||||||
PurchaseForm,
|
PurchaseForm,
|
||||||
SaleLineFormSet,
|
SaleLineFormSet,
|
||||||
ReconciliationJarForm,
|
|
||||||
PurchaseSummaryForm)
|
PurchaseSummaryForm)
|
||||||
|
|
||||||
import csv
|
import csv
|
||||||
@ -95,6 +94,7 @@ def import_products(request):
|
|||||||
{'form': form}
|
{'form': form}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def import_customers(request):
|
def import_customers(request):
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
form = ImportCustomersForm(request.POST, request.FILES)
|
form = ImportCustomersForm(request.POST, request.FILES)
|
||||||
@ -109,24 +109,6 @@ def import_customers(request):
|
|||||||
{'form': form}
|
{'form': form}
|
||||||
)
|
)
|
||||||
|
|
||||||
def reconciliate_jar(request):
|
|
||||||
summary = Payment.get_reconciliation_jar_summary()
|
|
||||||
if request.method == 'POST':
|
|
||||||
form = ReconciliationJarForm(request.POST)
|
|
||||||
if form.is_valid():
|
|
||||||
reconciliation = form.save()
|
|
||||||
reconciliation.add_payments(summary.payments)
|
|
||||||
reconciliation.clean()
|
|
||||||
reconciliation.save()
|
|
||||||
return HttpResponseRedirect('cuadres')
|
|
||||||
else:
|
|
||||||
form = ReconciliationJarForm()
|
|
||||||
return render(
|
|
||||||
request,
|
|
||||||
"don_confiao/reconciliate_jar.html",
|
|
||||||
{'summary': summary, 'form': form}
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def reconciliations(request):
|
def reconciliations(request):
|
||||||
return HttpResponse('<h1>Reconciliaciones</h1>')
|
return HttpResponse('<h1>Reconciliaciones</h1>')
|
||||||
@ -178,6 +160,24 @@ def payment_methods_to_select(request):
|
|||||||
return JsonResponse(methods, safe=False)
|
return JsonResponse(methods, safe=False)
|
||||||
|
|
||||||
|
|
||||||
|
def sales_for_reconciliation(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] = []
|
||||||
|
grouped_sales[sale.payment_method].append({
|
||||||
|
'id': sale.id,
|
||||||
|
'date': sale.date,
|
||||||
|
'payment_method': sale.payment_method,
|
||||||
|
'customer': {
|
||||||
|
'id': sale.customer.id,
|
||||||
|
'name': sale.customer.name,
|
||||||
|
},
|
||||||
|
'total': sale.get_total(),
|
||||||
|
})
|
||||||
|
return JsonResponse(grouped_sales, safe=False)
|
||||||
|
|
||||||
def _mask_phone(phone):
|
def _mask_phone(phone):
|
||||||
digits = str(phone)[-3:] if phone else " " * 3
|
digits = str(phone)[-3:] if phone else " " * 3
|
||||||
return "X" * 7 + digits
|
return "X" * 7 + digits
|
||||||
|
Loading…
Reference in New Issue
Block a user