Hello,
I have a view which prepares different formsets (using a different relations between a “Business” model and a “Company” model, either “as_customer” or “as_beneficiary” to display businesses depending on the user choice. Using the same template, I get a a behaiviour I do not understand.
When displaying a queryset based on a “customer” relationship, the customer foreign key for the business is an input element with hidden type and the beneficiary element is a “regular” select field.
When displayinf the queryset based on the “beneficiary” relationship, the customer foreign ley is a regular input elemet and the “beneficiary” field is hidden!
Any clue of what is going on?
I would like to get both fields “customer” and “beneficiary” to be regular inputs in both cases (as_customer and as_beneficiary).
Here is an extract of the code:
Models involved are:
class Business(models.Model):
name = models.CharField('Affaire',max_length=200, null = True, blank=True)
customer = models.ForeignKey(Company,related_name = 'customer',on_delete=models.CASCADE,verbose_name='Client')
beneficiary = models.ForeignKey(Company,related_name = 'beneficiary', on_delete=models.CASCADE,verbose_name='Bénéficiaire',
null = True,blank = True)
customer_ref = models.CharField('Référence client', max_length=250,
null=True, blank=True)
framework_agreement = models.ForeignKey(Framework_agreement, on_delete=models.SET_NULL, verbose_name='Accord-cadre', null=True, blank=True)
po_market = models.ForeignKey(PO_market, on_delete=models.SET_NULL, verbose_name='Marché à bons de commande', null=True, blank=True)
default_contact = models.ForeignKey(Contact, related_name = 'set_business_contact', on_delete=models.SET_NULL, \
null=True, blank=True, verbose_name='Contact client par défaut:')
default_addressee = models.ForeignKey(Contact, related_name="set_business_adressee" ,on_delete=models.SET_NULL, \
null=True, blank=True, verbose_name='Destinataire par défaut:')
default_address =models.ForeignKey(Address, on_delete=models.SET_NULL, \
null=True, blank=True, verbose_name='Adresse par défaut:')
account_exec = models.ForeignKey(Account_exec,on_delete=models.SET_NULL, null = True, blank = True, verbose_name='Chargé d\'affaires')
amount = models.DecimalField('Montant',max_digits=9, decimal_places=2, null=True,blank=True)
ponderation = models.ForeignKey(BusinessStatus, on_delete=models.RESTRICT, default=1)
creation_date = models.DateField('Date de création', default=datetime.date.today)
last_modification_date = models.DateTimeField('Date de dernière modification',
auto_now= True,null = True, blank = True,)
def weighted_amount(self):
status = BusinessStatus.objects.get(pk=self.ponderation_id)
taux = status.weight
if taux:
amount = self.amount * taux / 100
else:
amount = 0
return amount
weighted_amount.short_description = "Montant pondéré"
def amount_to_plan(self):
invoices = self.invoice_set.all()
a = self.amount
for i in invoices:
if i.type == 'FA' or i.type == 'PR':
a -= i.amount
if i.type == 'AV':
a += i.amount
return a
amount_to_plan.short_description = "Facturation à planifier"
def billed_amount(self):
return self.invoice_set.filter(is_issued=True).aggregate(total=Sum('amount'))['total'] or 0
billed_amount.short_description = 'Total des montants facturés'
def collected_amount(self):
return self.invoice_set.filter(is_paid=True).aggregate(total=Sum('amount'))['total'] or 0
collected_amount.short_description = "Total des montants encaissés"
def amount_to_bill(self):
status = BusinessStatus.objects.get(pk=self.ponderation_id)
taux = status.weight
invoices = self.invoice_set.all()
amount_to_bill = self.amount
if taux == 100:
for i in invoices:
if i.is_issued == True:
amount_to_bill -= i.amount
return amount_to_bill
amount_to_bill.short_description = "Montant à facturer"
def status(self):
return BusinessStatus.objects.get(pk=self.ponderation_id).text()
def weight(self):
return BusinessStatus.objects.get(pk=self.ponderation_id).weight
def __str__(self):
"""String for representing the Model object (in Admin site etc.)"""
return self.name
def get_absolute_url(self):
return reverse('customer_detail_form', kwarg={'pk':self.pk})
class Meta:
ordering = ['ponderation','amount']
verbose_name = 'Affaire'
verbose_name_plural = "Affaires"
class Company(models.Model):
name = models.CharField(‘Nom’,max_length=200,)
SIRET = models.CharField(‘SIRET’,max_length=16, null = True, blank = True)
creation_date = models.DateField('Date de création',default=datetime.date.today,
null = True,blank = True)
last_modification_date = models.DateTimeField('Date de dernière modification',
auto_now= True,null = True, blank = True,)
class Meta:
ordering = ['name']
verbose_name = 'Client'
def total_business(self):
# Renvoie le montant total de chiffre d'affaire signé avec ce client
business=self.customer.all()
total_business = 0
for b in business:
b_ponderation = b.ponderation
if b_ponderation.weight == 100:
total_business += b.amount
return total_business
def total_to_bill(self):
# Renvoie le montant total à facturer à ce client
business=self.customer.all()
total_to_bill = 0
for b in business:
b_ponderation = b.ponderation
if b_ponderation.weight == 100:
total_to_bill += b.amount_to_bill()
return total_to_bill
def total_under_negotiation(self):
# Renvoie le montant pondéré des affaires en cours de négociation
business=self.customer.all()
total_under_negotiation = 0
for b in business:
b_ponderation = b.ponderation
if b_ponderation.weight != 100:
total_under_negotiation += b.weighted_amount()
return total_under_negotiation
def __str__(self):
"""Cette retourne une chaîne de caractère pour identifier l'instance de la classe d'objet."""
return self.name
def get_absolute_url(self):
return reverse('customer_detail_form', kwargs={'company_pk': self.pk})
def get_contacts_absolute_url(self):
return reverse('company_contacts_update', kwargs={'company_pk': self.pk})
def get_addresses_absolute_url(self):
return reverse('company_addresses_update', kwargs={'company_pk': self.pk})
Extract of the view building the formset:
if request.POST.get('flexRadioCustomerBeneficiary') == 'as_customer':
as_customer = True
as_customer_checked = 'checked'
as_beneficiary_checked = ''
previous_view = 'as_customer'
print('as_customer')
business_queryset = Business.objects.filter(customer=company).order_by(
Case(
When(ponderation__label="Terminée", then=Value(0)),
When(ponderation=0, then=Value(1)),
default='ponderation__weight',
output_field=IntegerField()
).desc(),
'-ponderation__weight'
)
BusinessFormSet = inlineformset_factory(Company, Business, fk_name='customer',extra=0, can_delete=False, form = BusinessForm)
business_formset = BusinessFormSet(instance=company, queryset=business_queryset, data=request.POST)
print('business formset avec request.POST')
else:
as_beneficiary = True
as_customer_checked = ''
as_beneficiary_checked = 'checked'
previous_view = 'as_beneficiary'
business_queryset = Business.objects.filter(beneficiary=company).order_by(
Case(
When(ponderation__label="Terminée", then=Value(0)),
When(ponderation=0, then=Value(1)),
default='ponderation__weight',
output_field=IntegerField()
).desc(),
'-ponderation__weight'
)
print('as_beneificiary')
BusinessFormSet = inlineformset_factory(Company, Business, fk_name='beneficiary',extra=0, can_delete=False, form = BusinessForm)
business_formset = BusinessFormSet(instance=company, queryset=business_queryset, data=request.POST)
And the code handling the Business form is:
class BusinessForm(ModelForm):
def clean(self):
print('clean business')
cleaned_data = super().clean()
name = cleaned_data.get('name')
account_exec = cleaned_data.get('account_exec')
amount = cleaned_data.get('amount')
if not(name):
msg = ('Veuillez compléter le nom de l\'affaire')
self.add_error('name', msg)
if not (account_exec):
msg = ('Veuillez choisir le reponsable de l\'affaire')
self.add_error('account_exec', msg)
if not (amount):
msg = ('Veuillez définir le montant de l\'affaire')
self.add_error('amount', msg)
print('montant manquant')
return cleaned_data
# fonctions d'initialisation des valeurs calculées
def init_calculated_fields(self):
if self.instance.pk:
self.fields['weighted_amount'].initial = self.instance.weighted_amount()
self.fields['amount_to_plan'].initial = self.instance.amount_to_plan()
else:
self.fields['weighted_amount'].initial = 0
self.fields['amount_to_plan'].initial = 0
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# On initialise les montants calculés
self.init_calculated_fields()
# Format d'affichage des champs
for _, value in self.fields.items():
value.widget.attrs['class'] = 'form-control text-primary'
field_attrs = ['name', 'framework_agreement', 'po_market', 'account_exec', 'ponderation', 'amount',
'weighted_amount', 'amount_to_plan']
for field_attr in field_attrs:
self.fields[field_attr].widget.attrs.update({'class': 'form-control text-primary w-50'})
if self.instance.pk:
if self.instance.amount_to_plan() > 0:
self.fields['amount_to_plan'].widget.attrs.update({'class': 'form-control text-danger w-50'})
self.fields['name'].widget.attrs.update({'class': 'form-control text-primary w-200'})
self.fields['name'].widget.attrs.update({'placeholder': 'Saisir une nouvelle affaire'})
self.fields['weighted_amount'].disabled = True
self.fields['amount_to_plan'].disabled = True
# Restreindre la liste de choix des po_market au client en cours
if self.instance.pk:
customer = self.instance.customer
self.fields['framework_agreement'].queryset = Framework_agreement.objects.filter(company=customer)
self.fields['po_market'].queryset = PO_market.objects.filter(company=customer)
else:
self.fields['framework_agreement'].queryset = Framework_agreement.objects.none()
self.fields['po_market'].queryset = PO_market.objects.none()
# On vérifie si l'affaire est déjà associée à un framework agreement
if self.instance.framework_agreement:
# Si oui, on réordonne les choix pour mettre le framework agreement associé en premier
framework_agreement = self.instance.framework_agreement
self.fields['framework_agreement'].initial = framework_agreement.id
if self.instance.po_market:
# Si oui, on réordonne les choix pour mettre le marché à BC associé en premier
po_market = self.instance.po_market
self.fields['po_market'].initial = po_market.id
weighted_amount = forms.DecimalField(disabled=True, label="Montant pondéré")
amount_to_plan = forms.DecimalField(disabled = True, label="Montant à planifier")
class Meta:
model = Business
fields = ['name', 'customer', 'beneficiary','framework_agreement','po_market','account_exec', 'amount','ponderation',]
widgets = {
'creation_date': DatePickerInput,
'signature_date': DatePickerInput
}
Here is the template block rendering the businesses:
<div class="row line-spacing mx-2">
<div class="col-md-3">
<div class="d-flex align-items-center">
<i class="no-display">{{ business_form.id }}</i>
{% if business_form.instance.id %}
<a href="/salesforecast/businesses/{{business_form.instance.id}}" title="Gérer les échéances prévisionnelles de facturation">
<i class="bx bxs-detail nav_icon"></i>
</a>
{% endif %}
<b>{{ business_form.name }}</b>
</div>
<div class="fieldWrapper">{{ business_form.name.errors }}</div>
</div>
<div class="col-md-3">
<div class="d-flex align-items-center">
<label for="{{ business_form.customer.id_for_label }}">{{ business_form.customer.label }}</label>
{{ business_form.customer }}
</div>
<div class="fieldWrapper">{{ business_form.customer.errors }}</div>
</div>
<div class="col-md-3">
<div class="d-flex align-items-center">
<label for="{{ business_form.beneficiary.id_for_label }}">{{ business_form.beneficiary.label }}</label>
{{ business_form.beneficiary }}
</div>
<div class="fieldWrapper">{{ business_form.beneficiary.errors }}</div>
</div>
<div class="col-md-3">
<div class="d-flex align-items-center">
<label for="{{ business_form.account_exec.id_for_label }}">Responsable</label>
{{ business_form.account_exec }}
</div>
<div class="fieldWrapper">{{ business_form.account_exec.errors }}</div>
</div>
</div>
<div class="row line-spacing mx-2">
<div class="col-md-3 ">
<label for="{{business_form.amount.id_for_label}}">{{business_form.amount.label}}</label>
{{ business_form.amount }}<div class="fieldWrapper">{{business_form.amount.errors}}</div>
</div>
<div class="col-md-3">
<label for="{{business_form.ponderation.id_for_label}}">{{business_form.ponderation.label}}</label>
{{ business_form.ponderation }}<div class="fieldWrapper">{{business_form.ponderation.errors}}</div>
</div>
{%if business_form.weighted_amount.value != 0 %}
<div class="col-md-3">
<label for="{{business_form.weighted_amount.id_for_label}}">{{business_form.weighted_amount.label}}</label>
{{ business_form.weighted_amount }}
</div>
{%endif%}
{%if business_form.amount_to_plan.value != 0 %}
<div class="col-md-3">
<label for="{{business_form.amount_to_plan.id_for_label}}">{{business_form.amount_to_plan.label}}</label>
{{ business_form.amount_to_plan }}
</div>
{%endif%}
</div>
<div class="row line-spacing mx-2">
<div class="col-md-1 d-flex align-items-center">
<label for="{{business_form.framework_agreement.id_for_label}}">Accord-cadre</label>
</div>
<div class="col-md-5 d-flex align-items-center">
{{business_form.framework_agreement}}<div class="fieldWrapper">{{business_form.framework_agreement.errors}}</div>
{% if business_form.instance.id %}
<!-- icones d'ajout et modification d'accord-cadre -->
<a href="{% url 'cru_framework_agreement' business_pk=business_form.instance.id framework_agreement_pk=0%}?origin={{origin}}"
title="Ajouter un accord-cadre et y associer cette affaire" class="d-inline-block">
<i class='bx bxs-plus-circle nav_icon'></i>
</a>
{% if business_form.framework_agreement.value %}
<a href="{% url 'cru_framework_agreement' business_pk=business_form.instance.id framework_agreement_pk=business_form.framework_agreement.value %}?origin={{origin}}"
title="Modifier l'accord-cadre" class="d-inline-block">
<i class='bx bxs-pencil nav_icon'></i>
</a>
{% endif %}
{% endif %}
</div>
<div class="col-md-2 d-flex align-items-center">
<label for="{{business_form.po_market.id_for_label}}">Marché à bons de commande</label>
</div>
<div class="col-md-4 d-flex align-items-center">
{{business_form.po_market}}<div class="fieldWrapper">{{business_form.po_market.errors}}</div>
{% if business_form.instance.id %}
<!-- icones d'ajout et modification de marché à bon de commande -->
<a href="{% url 'cru_po_market' business_pk=business_form.instance.id po_market_pk=0%}?origin={{origin}}"
title="Ajouter un marché à bons de commande et y associer cette affaire" class="d-inline-block" >
<i class='bx bxs-plus-circle nav_icon'></i>
</a>
{% if business_form.po_market.value %}
<a href="{% url 'cru_po_market' business_pk=business_form.instance.id po_market_pk=business_form.po_market.value %}?origin={{origin}}"
title="Modifier le marché à bons de commande" class="d-inline-block">
<i class='bx bxs-pencil nav_icon'></i>
</a>
{% endif %}
{% endif %}
</div>
<div class="line"></div>
</div>