Merge pull request 'Habilitando la selección de método de pago en la compra' (#72) from chose_pay_method_on_purchase_#47 into main
Reviewed-on: OneTeam/don_confiao#72
This commit is contained in:
commit
2d86aba3e5
@ -14,7 +14,12 @@ class SaleView(viewsets.ModelViewSet):
|
|||||||
customer = Customer.objects.get(pk=data['customer'])
|
customer = Customer.objects.get(pk=data['customer'])
|
||||||
date = data['date']
|
date = data['date']
|
||||||
lines = data['saleline_set']
|
lines = data['saleline_set']
|
||||||
sale = Sale.objects.create(customer=customer, date=date)
|
payment_method = data['payment_method']
|
||||||
|
sale = Sale.objects.create(
|
||||||
|
customer=customer,
|
||||||
|
date=date,
|
||||||
|
payment_method=payment_method
|
||||||
|
)
|
||||||
|
|
||||||
for line in lines:
|
for line in lines:
|
||||||
product = Product.objects.get(pk=line['product'])
|
product = Product.objects.get(pk=line['product'])
|
||||||
|
@ -0,0 +1,58 @@
|
|||||||
|
<template>
|
||||||
|
<v-dialog v-model="dialog" max-width="400">
|
||||||
|
<v-card>
|
||||||
|
<v-card-title>Calcular Devuelta</v-card-title>
|
||||||
|
<v-card-text>
|
||||||
|
<v-text-field
|
||||||
|
v-model.number="purchase"
|
||||||
|
label="Total de la compra"
|
||||||
|
type="number"
|
||||||
|
prefix="$"
|
||||||
|
readonly
|
||||||
|
></v-text-field>
|
||||||
|
<v-text-field
|
||||||
|
v-model.number="money"
|
||||||
|
label="Dinero"
|
||||||
|
type="number"
|
||||||
|
prefix="$"
|
||||||
|
></v-text-field>
|
||||||
|
<v-text-field
|
||||||
|
v-model.number="change_cash"
|
||||||
|
label="Devuelta"
|
||||||
|
type="number"
|
||||||
|
prefix="$"
|
||||||
|
readonly
|
||||||
|
></v-text-field>
|
||||||
|
</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 {
|
||||||
|
props: {
|
||||||
|
total_purchase: {
|
||||||
|
type: Number,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
dialog: false,
|
||||||
|
money: null,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
purchase() {
|
||||||
|
return this.total_purchase
|
||||||
|
},
|
||||||
|
change_cash() {
|
||||||
|
return (this.money || 0) - this.total_purchase
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
@ -116,6 +116,17 @@
|
|||||||
readonly
|
readonly
|
||||||
persistent-placeholder="true"
|
persistent-placeholder="true"
|
||||||
></v-text-field>
|
></v-text-field>
|
||||||
|
<v-container v-if="calculateTotal > 0">
|
||||||
|
<v-select
|
||||||
|
:items="payment_methods"
|
||||||
|
v-model="purchase.payment_method"
|
||||||
|
item-title="text"
|
||||||
|
item-value="value"
|
||||||
|
label="Pago en"
|
||||||
|
></v-select>
|
||||||
|
<v-btn @click="openCasherModal" v-if="purchase.payment_method === 'CASH'">Calcular Devuelta</v-btn>
|
||||||
|
<CasherModal :total_purchase="calculateTotal" ref="casherModal"</CasherModal>
|
||||||
|
</v-container>
|
||||||
<v-btn @click="submit" color="green">Comprar</v-btn>
|
<v-btn @click="submit" color="green">Comprar</v-btn>
|
||||||
</v-form>
|
</v-form>
|
||||||
</v-container>
|
</v-container>
|
||||||
@ -123,11 +134,13 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import CustomerForm from './CreateCustomerModal.vue';
|
import CustomerForm from './CreateCustomerModal.vue';
|
||||||
|
import CasherModal from './CasherModal.vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'DonConfiao',
|
name: 'DonConfiao',
|
||||||
components: {
|
components: {
|
||||||
CustomerForm
|
CustomerForm,
|
||||||
|
CasherModal,
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
msg: String
|
msg: String
|
||||||
@ -137,10 +150,12 @@
|
|||||||
valid: false,
|
valid: false,
|
||||||
client_search: '',
|
client_search: '',
|
||||||
product_search: '',
|
product_search: '',
|
||||||
|
payment_methods: null,
|
||||||
purchase: {
|
purchase: {
|
||||||
date: this.getCurrentDate(),
|
date: this.getCurrentDate(),
|
||||||
customer: null,
|
customer: null,
|
||||||
notes: '',
|
notes: '',
|
||||||
|
payment_method: null,
|
||||||
saleline_set: [{product:'', unit_price: 0, quantity: 0, unit: ''}],
|
saleline_set: [{product:'', unit_price: 0, quantity: 0, unit: ''}],
|
||||||
},
|
},
|
||||||
rules: {
|
rules: {
|
||||||
@ -157,6 +172,7 @@
|
|||||||
created() {
|
created() {
|
||||||
this.fetchClients();
|
this.fetchClients();
|
||||||
this.fetchProducts();
|
this.fetchProducts();
|
||||||
|
this.fetchPaymentMethods();
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
group () {
|
group () {
|
||||||
@ -192,6 +208,9 @@
|
|||||||
openModal() {
|
openModal() {
|
||||||
this.$refs.customerModal.openModal();
|
this.$refs.customerModal.openModal();
|
||||||
},
|
},
|
||||||
|
openCasherModal() {
|
||||||
|
this.$refs.casherModal.dialog = true
|
||||||
|
},
|
||||||
getCurrentDate() {
|
getCurrentDate() {
|
||||||
const today = new Date();
|
const today = new Date();
|
||||||
const yyyy = today.getFullYear();
|
const yyyy = today.getFullYear();
|
||||||
@ -225,13 +244,22 @@
|
|||||||
fetch('/don_confiao/api/products/')
|
fetch('/don_confiao/api/products/')
|
||||||
.then(response => response.json())
|
.then(response => response.json())
|
||||||
.then(data => {
|
.then(data => {
|
||||||
console.log(data);
|
|
||||||
this.products = data;
|
this.products = data;
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
fetchPaymentMethods() {
|
||||||
|
fetch('/don_confiao/payment_methods/all/select_format')
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
this.payment_methods = data;
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error(error);
|
||||||
|
});
|
||||||
|
},
|
||||||
addLine() {
|
addLine() {
|
||||||
this.purchase.saleline_set.push({ product: '', unit_price: 0, quantity:0, measuring_unit: ''});
|
this.purchase.saleline_set.push({ product: '', unit_price: 0, quantity:0, measuring_unit: ''});
|
||||||
},
|
},
|
||||||
|
@ -23,6 +23,12 @@
|
|||||||
<v-list-item-subtitle v-if="purchase.customer">{{ purchase.customer.name }}</v-list-item-subtitle>
|
<v-list-item-subtitle v-if="purchase.customer">{{ purchase.customer.name }}</v-list-item-subtitle>
|
||||||
</v-list-item-content>
|
</v-list-item-content>
|
||||||
</v-list-item>
|
</v-list-item>
|
||||||
|
<v-list-item>
|
||||||
|
<v-list-item-content>
|
||||||
|
<v-list-item-title>Pagado en:</v-list-item-title>
|
||||||
|
<v-list-item-subtitle v-if="purchase.payment_method">{{ purchase.payment_method }}</v-list-item-subtitle>
|
||||||
|
</v-list-item-content>
|
||||||
|
</v-list-item>
|
||||||
<v-list-item>
|
<v-list-item>
|
||||||
<v-list-item-content>
|
<v-list-item-content>
|
||||||
<v-list-item-title>Total:</v-list-item-title>
|
<v-list-item-title>Total:</v-list-item-title>
|
||||||
|
@ -0,0 +1,18 @@
|
|||||||
|
# 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),
|
||||||
|
),
|
||||||
|
]
|
@ -6,6 +6,12 @@ from decimal import Decimal
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
|
|
||||||
|
class PaymentMethods(models.TextChoices):
|
||||||
|
CASH = 'CASH', _('Efectivo')
|
||||||
|
CONFIAR = 'CONFIAR', _('Confiar')
|
||||||
|
BANCOLOMBIA = 'BANCOLOMBIA', _('Bancolombia')
|
||||||
|
|
||||||
|
|
||||||
class Customer(models.Model):
|
class Customer(models.Model):
|
||||||
name = models.CharField(max_length=100, default=None, null=False, blank=False)
|
name = models.CharField(max_length=100, default=None, null=False, blank=False)
|
||||||
address = models.CharField(max_length=100, null=True, blank=True)
|
address = models.CharField(max_length=100, null=True, blank=True)
|
||||||
@ -61,6 +67,13 @@ class Sale(models.Model):
|
|||||||
date = models.DateField("Date")
|
date = models.DateField("Date")
|
||||||
phone = models.CharField(max_length=13, null=True, blank=True)
|
phone = models.CharField(max_length=13, null=True, blank=True)
|
||||||
description = models.CharField(max_length=255, 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
|
||||||
|
)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f"{self.date} {self.customer}"
|
return f"{self.date} {self.customer}"
|
||||||
@ -69,6 +82,10 @@ class Sale(models.Model):
|
|||||||
lines = self.saleline_set.all()
|
lines = self.saleline_set.all()
|
||||||
return sum([l.quantity * l.unit_price for l in lines])
|
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
|
@classmethod
|
||||||
def sale_header_csv(cls):
|
def sale_header_csv(cls):
|
||||||
sale_header_csv = [field.name for field in cls._meta.fields]
|
sale_header_csv = [field.name for field in cls._meta.fields]
|
||||||
@ -88,12 +105,6 @@ class SaleLine(models.Model):
|
|||||||
return f"{self.sale} - {self.product}"
|
return f"{self.sale} - {self.product}"
|
||||||
|
|
||||||
|
|
||||||
class PaymentMethods(models.TextChoices):
|
|
||||||
CASH = 'CASH', _('Cash')
|
|
||||||
CONFIAR = 'CONFIAR', _('Confiar')
|
|
||||||
BANCOLOMBIA = 'BANCOLOMBIA', _('Bancolombia')
|
|
||||||
|
|
||||||
|
|
||||||
class ReconciliationJarSummary():
|
class ReconciliationJarSummary():
|
||||||
def __init__(self, payments):
|
def __init__(self, payments):
|
||||||
self._validate_payments(payments)
|
self._validate_payments(payments)
|
||||||
|
@ -50,6 +50,7 @@ class TestAPI(APITestCase):
|
|||||||
data = {
|
data = {
|
||||||
'customer': self.customer.id,
|
'customer': self.customer.id,
|
||||||
'date': '2024-09-02',
|
'date': '2024-09-02',
|
||||||
|
'payment_method': 'CASH',
|
||||||
'saleline_set': [
|
'saleline_set': [
|
||||||
{'product': self.product.id, 'quantity': 2, 'unit_price': 3000},
|
{'product': self.product.id, 'quantity': 2, 'unit_price': 3000},
|
||||||
{'product': self.product.id, 'quantity': 3, 'unit_price': 5000}
|
{'product': self.product.id, 'quantity': 3, 'unit_price': 5000}
|
||||||
|
23
tienda_ilusion/don_confiao/tests/test_payment_methods.py
Normal file
23
tienda_ilusion/don_confiao/tests/test_payment_methods.py
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
from django.test import Client, TestCase
|
||||||
|
|
||||||
|
# from ..models import PaymentMethods
|
||||||
|
|
||||||
|
class TestPaymentMethods(TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
self.client = Client()
|
||||||
|
|
||||||
|
def test_keys_in_payment_methods_to_select(self):
|
||||||
|
response = self.client.get(
|
||||||
|
'/don_confiao/payment_methods/all/select_format'
|
||||||
|
)
|
||||||
|
methods = response.json()
|
||||||
|
for method in methods:
|
||||||
|
self.assertEqual(set(method.keys()), {'text', 'value'})
|
||||||
|
|
||||||
|
def test_basic_payment_methods_to_select(self):
|
||||||
|
methods = self.client.get(
|
||||||
|
'/don_confiao/payment_methods/all/select_format'
|
||||||
|
).json()
|
||||||
|
self.assertIn('CASH', [method.get('value') for method in methods])
|
||||||
|
self.assertIn('CONFIAR', [method.get('value') for method in methods])
|
||||||
|
self.assertIn('BANCOLOMBIA', [method.get('value') for method in methods])
|
@ -1,4 +1,6 @@
|
|||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
|
from django.core.exceptions import ValidationError
|
||||||
|
|
||||||
from ..models import Customer, Product, Sale, SaleLine
|
from ..models import Customer, Product, Sale, SaleLine
|
||||||
|
|
||||||
|
|
||||||
@ -24,6 +26,17 @@ class ConfiaoTest(TestCase):
|
|||||||
|
|
||||||
self.assertIsInstance(sale, Sale)
|
self.assertIsInstance(sale, Sale)
|
||||||
|
|
||||||
|
def test_can_create_sale_without_payment_method(self):
|
||||||
|
sale = Sale()
|
||||||
|
sale.customer = self.customer
|
||||||
|
sale.date = "2024-06-22"
|
||||||
|
sale.phone = '666666666'
|
||||||
|
sale.description = "Description"
|
||||||
|
sale.payment_method = ''
|
||||||
|
|
||||||
|
with self.assertRaises(ValidationError):
|
||||||
|
sale.full_clean()
|
||||||
|
|
||||||
def test_create_sale_line(self):
|
def test_create_sale_line(self):
|
||||||
sale = Sale()
|
sale = Sale()
|
||||||
sale.customer = self.customer
|
sale.customer = self.customer
|
||||||
|
@ -19,6 +19,7 @@ class PurchaseFormTest(TestCase):
|
|||||||
"csrfmiddlewaretoken": _csrf_token,
|
"csrfmiddlewaretoken": _csrf_token,
|
||||||
"customer": self.customer.id,
|
"customer": self.customer.id,
|
||||||
"date": "2024-08-03",
|
"date": "2024-08-03",
|
||||||
|
"payment_method": "CASH",
|
||||||
"phone": "sfasfd",
|
"phone": "sfasfd",
|
||||||
"description": "dasdadad",
|
"description": "dasdadad",
|
||||||
"saleline_set-TOTAL_FORMS": "1",
|
"saleline_set-TOTAL_FORMS": "1",
|
||||||
|
@ -27,5 +27,6 @@ urlpatterns = [
|
|||||||
path("cuadres", views.reconciliate_jar, name="reconciliations"),
|
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('api/', include(router.urls)),
|
path('api/', include(router.urls)),
|
||||||
]
|
]
|
||||||
|
@ -164,11 +164,20 @@ def purchase_json_summary(request, id):
|
|||||||
'name': purchase.customer.name,
|
'name': purchase.customer.name,
|
||||||
# 'phone': _mask_phone(purchase.customer.phone)
|
# 'phone': _mask_phone(purchase.customer.phone)
|
||||||
},
|
},
|
||||||
|
'payment_method': purchase.payment_method,
|
||||||
'set_lines': lines,
|
'set_lines': lines,
|
||||||
}
|
}
|
||||||
return JsonResponse(to_response, safe=False)
|
return JsonResponse(to_response, safe=False)
|
||||||
|
|
||||||
|
|
||||||
|
def payment_methods_to_select(request):
|
||||||
|
methods = [
|
||||||
|
{'text': choice[1], 'value': choice[0]}
|
||||||
|
for choice in PaymentMethods.choices
|
||||||
|
]
|
||||||
|
return JsonResponse(methods, 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