I am stuck with an issue.
I always has form.is_valid() being False and I don’t see what could be the validation errors…
I’ve simplified as much as I could the form…
Here is the code:
models.py:
def default_due_date():
return datetime.date.today() + timedelta(days=30)
class Invoice(models.Model):
number = models.CharField('Numéro de facture', max_length=10, null=True, blank=True)
label = models.CharField('libellé de facture', max_length=200, null=True,blank = True)
issuance_date = models.DateField('Date de facture', default=datetime.date.today)
due_date = models.DateField('Date d\'échéance', default=default_due_date)
PREVISION = 'PR'
FACTURE = 'FA'
AVOIR = 'AV'
INVOICE_TYPE_CHOICES = [
(PREVISION, 'Echéance'),
(FACTURE, 'Facture'),
(AVOIR, 'Avoir')
]
type = models.CharField('Type de facture',max_length=2,
choices=INVOICE_TYPE_CHOICES, default=PREVISION)
amount = models.DecimalField('Montant',max_digits=11, decimal_places=2, default=0)
is_issued = models.BooleanField('Emise', default=False)
is_paid = models.BooleanField('Réglée', default=False)
payment_date = models.DateField('Date de règlement', null=True,blank=True)
business = models.ForeignKey(Business, on_delete=models.CASCADE, verbose_name='Commande')
market = models.ForeignKey(Market, on_delete=models.RESTRICT, null=True,blank=True)
purchase_order = models.ForeignKey(Purchase_order, on_delete=models.RESTRICT, null=True,blank=True)
contact = models.ForeignKey(Contact, related_name = 'set_invoice_contact', on_delete=models.SET_NULL, \
null=True, blank=True, verbose_name='Suivi par')
addressee = models.ForeignKey(Contact, related_name="set_invoice_adressee" ,on_delete=models.SET_NULL, \
null=True, blank=True, verbose_name='Destinataire de la facture')
address =models.ForeignKey(Address, on_delete=models.SET_NULL, \
null=True, blank=True, verbose_name='Adresse de facturation')
def set_default_values(self):
business = Business.objects.get(pk= self.business.id)
self.address = business.default_address
self.addressee = business.default_addressee
self.contact = business.default_contact
def save(self,*args, **kwargs):
if not self.pk: #si l'échéance est nouvelle
self.set_default_values() #on initialise les valeurs de "contact" par défaut
super().save(*args,**kwargs)
class Meta:
ordering = ['issuance_date']
verbose_name = 'Facture'
def __str__(self):
return self.label
def get_absolute_url(self):
return reverse('invoice_detail', args=[str(self.id)])
forms.py
class BusinessForm(ModelForm):
def clean(self):
cleaned_data = super().clean()
name = cleaned_data.get('name')
account_exec = cleaned_data.get('account_exec')
amount = cleaned_data.get('amount')
ponderation = cleaned_data.get('ponderation')
contract_type = cleaned_data.get('contract_type')
if name or account_exec or amount :
if not (name and account_exec and amount and ponderation):
msg = 'Si un des champs d\'affaire est saisi, tous les champs doivent l\'être'
self.add_error('name', msg)
def init_amount_pondered(self):
if self.instance.pk:
# si le business existe, on affiche le montant pondéré
amount = self.instance.amount_pondered()
else:
# si le business n'existe pas (pas encore créé), on affiche 0
amount = 0
return amount
def init_amount_to_plan(self):
if self.instance.pk:
amount = self.instance.amount_to_plan()
else:
amount = 0
return amount
amount_pondered = forms.CharField(disabled=True)
amount_to_plan = forms.CharField(disabled = True)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# On initialise les montants calculés (montant pondéré et montant restant à planifier)
self.fields['amount_to_plan'].initial = self.init_amount_to_plan()
self.fields['amount_pondered'].initial = self.init_amount_pondered()
class Meta:
model = Business
fields = ['name', 'customer', 'account_exec', 'amount','ponderation','contract_type']
widgets = {'creation_date': DatePickerInput,'signature_date': DatePickerInput}
class BusinessDetailForm(BusinessForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# On initialise les montants calculés (montant pondéré et montant restant à planifier)
self.fields['amount_to_plan'].initial = self.init_amount_to_plan()
self.fields['amount_pondered'].initial = self.init_amount_pondered()
# On restreint la liste de choix de contacts à ceux qui sont rattachés au client en cours
self.fields['default_contact'] = forms.ModelChoiceField(\
queryset=Contact.objects.filter(customer=self.instance.customer))
# On restreint la liste de choix de contact à ceux qui sont rattachés au client en cours
self.fields['default_addressee'] = forms.ModelChoiceField(\
queryset=Contact.objects.filter(customer=self.instance.customer))
# On restreint la liste de choix de adresses à celles qui sont rattachées au client en cours
self.fields['default_address'] = forms.ModelChoiceField(\
queryset=Address.objects.filter(customer=self.instance.customer))
class Meta:
model = Business
fields = ['name', 'customer', 'customer_ref','account_exec', 'amount','ponderation','contract_type',
'default_contact','default_addressee','default_address']
widgets = {'creation_date': DatePickerInput,'signature_date': DatePickerInput}
class InvoiceForm(ModelForm):
def delete_invoice(self):
instance = self.instance
if instance.pk:
instance.delete()
return
def clean(self):
cleaned_data = super().clean()
label = cleaned_data.get('label')
issuance_date = cleaned_data.get('issuance_date')
due_date = cleaned_data.get('due_date')
amount = cleaned_data.get('amount')
if due_date < issuance_date:
msg = 'la date d\échéance ne peut pas être antérieure à la date s\'émission.'
self.add_error('due_date', msg)
if label or amount !=0 :
if not (label and amount!=0):
msg = 'Si un des champs d\'affaire est saisi, tous les champs doivent l\'être'
self.add_error('label', msg)
return
error_css_class = 'error'
required_css_class = 'required'
number = forms.CharField(disabled=True, required=False, label='N° de facture')
is_issued = forms.BooleanField(required = False, label='Emise')
is_paid = forms.BooleanField(required = False, label='Réglée')
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['number'].initial = self.instance.number
if self.fields['is_issued'] == True:
self.issuance_date = forms.DateField(disabled=True)
class Meta:
model = Invoice
#fields= ['business','label','is_issued','issuance_date','due_date','type','amount', 'is_paid','payment_date']
fields= ['business','label','is_issued','type','amount', 'is_paid',]
class InvoiceDetailForm(InvoiceForm):
business = forms.CharField(disabled=True)
payment_date = forms.CharField(required=False)
class Meta:
model = Invoice
fields= ['label','amount']
views.py:
class InvoiceDetailView(generic.DetailView):
model = Invoice
fields = '__all__'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
invoice = get_object_or_404(Invoice, pk=self.kwargs['pk'])
business = get_object_or_404(Business, pk=invoice.business.id)
context['invoice_form'] = InvoiceDetailForm(instance=invoice)
context['business_form'] = BusinessDetailForm(instance = business)
return context
def post(self, request, *args, **kwargs):
pk = self.kwargs['pk']
invoice = get_object_or_404(Invoice, pk=pk)
business = get_object_or_404(Business, pk=invoice.business.id)
business_form = BusinessDetailForm(instance = business)
invoice_form = InvoiceDetailForm(instance = invoice, prefix = 'invoice')
if 'update' in request.POST:
messages.warning(request, request.POST)
if invoice_form.is_valid():
invoice_form.save()
messages.success(request, 'Les données de Facture ont été mises à jour.')
else:
messages.warning(request, 'les données de Facture sont incorrectes.'+ str(invoice_form.errors))
return render(request, 'gestcom/invoice_detail.html',\
{'invoice_form':invoice_form, 'business_form':business_form})
HTML
{% extends "base_generic.html" %}
{% block content %}
<form method="post">
{% csrf_token %}
<div>
<h2>
<span>Facture n°: </span>
{% if invoice.number%}
<span> {{invoice.number}}</span>
{% else %}
<span> En attente</span>
{%endif%}
</h2>
</div>
<div>
<table>
</tr>
<th>Montant de l'échéance</th>
<td>{{invoice_form.amount}}</td>
</tr>
<tr>
<th>{{invoice_form.is_issued.label}}</th>
<td>{{invoice_form.is_issued}}</td>
<th>{{invoice_form.is_paid.label}}</th>
<td>{{invoice_form.is_paid}}</td>
</table>
<br>
<div class="container">
<div class="row">
<div class="col-lg-4">
<table>
<tr>
<th>{{business_form.customer_ref.label}}</th>
<td>{{business_form.customer_ref}}</td>
</tr>
</table>
</div>
</div>
</div>
</div>
<div>
<button id = "update" name="update" type="submit" class="btn btn-primary">
<i class='bx bx-save nav_icon'></i>
Sauvegarder
</button>
</div>
</form>
<div>
<strong>Erreurs de champ:</strong>
{% if form.errors %}
<div class="alert alert-danger">
<ul>
{% for field_errors in form.errors.values %}
{% for error in field_errors %}
<li>{{ error }}</li>
{% endfor %}
{% endfor %}
</ul>
</div>
{% endif %}
</div>
<div>
<strong>Erreurs "non form":</strong>
{% if form.non_field_errors %}
<div class="alert alert-danger">
<ul>
{% for error in form.non_field_errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
</div>
{% endif %}
</div>
<script src="https://code.jquery.com/jquery-1.12.4.min.js"></script>
<script type="text/javascript">
$(document).ready(function(){
$("#update").click(function(){
if ($("input[name*='DELETE']:checked").length > 0) {
if(confirm("Confirmez-vous vouloir supprimer les lignes sélectionnées?")){
// Récupérer l'URL dynamique en fonction de l'ID particulier
var url = "/gestcom/test/" + $("#themeId").val();
// Le code pour envoyer la requête POST avec l'URL dynamique
$.ajax({
type: "POST",
});
} else {
return false
}
}
});
});
</script>
{% endblock %}