Commit ef4e430e authored by Levy--Falk Hugo's avatar Levy--Falk Hugo

Release : 2.6.1

parents ea7ea032 c25a4981
...@@ -120,3 +120,33 @@ Don't forget to run migrations, several settings previously in the `preferences` ...@@ -120,3 +120,33 @@ Don't forget to run migrations, several settings previously in the `preferences`
in their own Payment models. in their own Payment models.
To have a closer look on how the payments works, please go to the wiki. To have a closer look on how the payments works, please go to the wiki.
## MR 182: Add role models
Adds the Role model.
You need to ensure that your database character set is utf-8.
```sql
ALTER DATABASE re2o CHARACTER SET utf8;
```
## MR 247: Fix des comptes mails
Fix several issues with email accounts, you need to collect the static files.
```bash
./manage.py collectstatic
```
## MR 203 Add custom invoices
The custom invoices are now stored in database. You need to migrate your database :
```bash
python3 manage.py migrate
```
On some database engines (postgreSQL) you also need to update the id sequences:
```bash
python3 manage.py sqlsequencereset cotisations | python3 manage.py dbshell
```
...@@ -338,6 +338,7 @@ class OptionalMachineSerializer(NamespacedHMSerializer): ...@@ -338,6 +338,7 @@ class OptionalMachineSerializer(NamespacedHMSerializer):
class OptionalTopologieSerializer(NamespacedHMSerializer): class OptionalTopologieSerializer(NamespacedHMSerializer):
"""Serialize `preferences.models.OptionalTopologie` objects. """Serialize `preferences.models.OptionalTopologie` objects.
""" """
class Meta: class Meta:
model = preferences.OptionalTopologie model = preferences.OptionalTopologie
fields = ('radius_general_policy', 'vlan_decision_ok', fields = ('radius_general_policy', 'vlan_decision_ok',
...@@ -469,10 +470,10 @@ class SwitchPortSerializer(NamespacedHMSerializer): ...@@ -469,10 +470,10 @@ class SwitchPortSerializer(NamespacedHMSerializer):
class Meta: class Meta:
model = topologie.Port model = topologie.Port
fields = ('switch', 'port', 'room', 'machine_interface', 'related', fields = ('switch', 'port', 'room', 'machine_interface', 'related',
'radius', 'vlan_force', 'details', 'api_url') 'custom_profile', 'state', 'details', 'api_url')
extra_kwargs = { extra_kwargs = {
'related': {'view_name': 'switchport-detail'}, 'related': {'view_name': 'switchport-detail'},
'api_url': {'view_name': 'switchport-detail'} 'api_url': {'view_name': 'switchport-detail'},
} }
...@@ -484,6 +485,18 @@ class RoomSerializer(NamespacedHMSerializer): ...@@ -484,6 +485,18 @@ class RoomSerializer(NamespacedHMSerializer):
fields = ('name', 'details', 'api_url') fields = ('name', 'details', 'api_url')
class PortProfileSerializer(NamespacedHMSerializer):
vlan_untagged = VlanSerializer(read_only=True)
class Meta:
model = topologie.PortProfile
fields = ('name', 'profil_default', 'vlan_untagged', 'vlan_tagged',
'radius_type', 'radius_mode', 'speed', 'mac_limit',
'flow_control', 'dhcp_snooping', 'dhcpv6_snooping',
'arp_protect', 'ra_guard', 'loop_protect', 'vlan_untagged',
'vlan_tagged')
# USERS # USERS
...@@ -534,11 +547,20 @@ class AdherentSerializer(NamespacedHMSerializer): ...@@ -534,11 +547,20 @@ class AdherentSerializer(NamespacedHMSerializer):
fields = ('name', 'surname', 'pseudo', 'email', 'local_email_redirect', fields = ('name', 'surname', 'pseudo', 'email', 'local_email_redirect',
'local_email_enabled', 'school', 'shell', 'comment', 'local_email_enabled', 'school', 'shell', 'comment',
'state', 'registered', 'telephone', 'room', 'solde', 'state', 'registered', 'telephone', 'room', 'solde',
'access', 'end_access', 'uid', 'api_url') 'access', 'end_access', 'uid', 'api_url','gid')
extra_kwargs = { extra_kwargs = {
'shell': {'view_name': 'shell-detail'} 'shell': {'view_name': 'shell-detail'}
} }
class HomeCreationSerializer(NamespacedHMSerializer):
"""Serialize 'users.models.User' minimal infos to create home
"""
uid = serializers.IntegerField(source='uid_number')
gid = serializers.IntegerField(source='gid_number')
class Meta:
model = users.User
fields = ('pseudo', 'uid', 'gid')
class ServiceUserSerializer(NamespacedHMSerializer): class ServiceUserSerializer(NamespacedHMSerializer):
"""Serialize `users.models.ServiceUser` objects. """Serialize `users.models.ServiceUser` objects.
...@@ -599,7 +621,7 @@ class WhitelistSerializer(NamespacedHMSerializer): ...@@ -599,7 +621,7 @@ class WhitelistSerializer(NamespacedHMSerializer):
class EMailAddressSerializer(NamespacedHMSerializer): class EMailAddressSerializer(NamespacedHMSerializer):
"""Serialize `users.models.EMailAddress` objects. """Serialize `users.models.EMailAddress` objects.
""" """
user = serializers.CharField(source='user.pseudo', read_only=True)
class Meta: class Meta:
model = users.EMailAddress model = users.EMailAddress
fields = ('user', 'local_part', 'complete_email_address', 'api_url') fields = ('user', 'local_part', 'complete_email_address', 'api_url')
...@@ -635,8 +657,41 @@ class LocalEmailUsersSerializer(NamespacedHMSerializer): ...@@ -635,8 +657,41 @@ class LocalEmailUsersSerializer(NamespacedHMSerializer):
class Meta: class Meta:
model = users.User model = users.User
fields = ('local_email_enabled', 'local_email_redirect', fields = ('local_email_enabled', 'local_email_redirect',
'email_address') 'email_address', 'email')
#Firewall
class FirewallPortListSerializer(serializers.ModelSerializer):
class Meta:
model = machines.OuverturePort
fields = ('begin', 'end', 'protocole', 'io', 'show_port')
class FirewallOuverturePortListSerializer(serializers.ModelSerializer):
tcp_ports_in = FirewallPortListSerializer(many=True, read_only=True)
udp_ports_in = FirewallPortListSerializer(many=True, read_only=True)
tcp_ports_out = FirewallPortListSerializer(many=True, read_only=True)
udp_ports_out = FirewallPortListSerializer(many=True, read_only=True)
class Meta:
model = machines.OuverturePortList
fields = ('tcp_ports_in', 'udp_ports_in', 'tcp_ports_out', 'udp_ports_out')
class SubnetPortsOpenSerializer(serializers.ModelSerializer):
ouverture_ports = FirewallOuverturePortListSerializer(read_only=True)
class Meta:
model = machines.IpType
fields = ('type', 'domaine_ip_start', 'domaine_ip_stop', 'complete_prefixv6', 'ouverture_ports')
class InterfacePortsOpenSerializer(serializers.ModelSerializer):
port_lists = FirewallOuverturePortListSerializer(read_only=True, many=True)
ipv4 = serializers.CharField(source='ipv4.ipv4', read_only=True)
ipv6 = Ipv6ListSerializer(many=True, read_only=True)
class Meta:
model = machines.Interface
fields = ('port_lists', 'ipv4', 'ipv6')
# DHCP # DHCP
...@@ -679,7 +734,7 @@ class NSRecordSerializer(NsSerializer): ...@@ -679,7 +734,7 @@ class NSRecordSerializer(NsSerializer):
"""Serialize `machines.models.Ns` objects with the data needed to """Serialize `machines.models.Ns` objects with the data needed to
generate a NS DNS record. generate a NS DNS record.
""" """
target = serializers.CharField(source='ns.name', read_only=True) target = serializers.CharField(source='ns', read_only=True)
class Meta(NsSerializer.Meta): class Meta(NsSerializer.Meta):
fields = ('target',) fields = ('target',)
...@@ -689,7 +744,7 @@ class MXRecordSerializer(MxSerializer): ...@@ -689,7 +744,7 @@ class MXRecordSerializer(MxSerializer):
"""Serialize `machines.models.Mx` objects with the data needed to """Serialize `machines.models.Mx` objects with the data needed to
generate a MX DNS record. generate a MX DNS record.
""" """
target = serializers.CharField(source='name.name', read_only=True) target = serializers.CharField(source='name', read_only=True)
class Meta(MxSerializer.Meta): class Meta(MxSerializer.Meta):
fields = ('target', 'priority') fields = ('target', 'priority')
...@@ -761,13 +816,12 @@ class CNAMERecordSerializer(serializers.ModelSerializer): ...@@ -761,13 +816,12 @@ class CNAMERecordSerializer(serializers.ModelSerializer):
"""Serialize `machines.models.Domain` objects with the data needed to """Serialize `machines.models.Domain` objects with the data needed to
generate a CNAME DNS record. generate a CNAME DNS record.
""" """
alias = serializers.CharField(source='cname.name', read_only=True) alias = serializers.CharField(source='cname', read_only=True)
hostname = serializers.CharField(source='name', read_only=True) hostname = serializers.CharField(source='name', read_only=True)
extension = serializers.CharField(source='extension.name', read_only=True)
class Meta: class Meta:
model = machines.Domain model = machines.Domain
fields = ('alias', 'hostname', 'extension') fields = ('alias', 'hostname')
class DNSZonesSerializer(serializers.ModelSerializer): class DNSZonesSerializer(serializers.ModelSerializer):
...@@ -792,6 +846,25 @@ class DNSZonesSerializer(serializers.ModelSerializer): ...@@ -792,6 +846,25 @@ class DNSZonesSerializer(serializers.ModelSerializer):
'aaaa_records', 'cname_records', 'sshfp_records') 'aaaa_records', 'cname_records', 'sshfp_records')
class DNSReverseZonesSerializer(serializers.ModelSerializer):
"""Serialize the data about DNS Zones.
"""
soa = SOARecordSerializer(source='extension.soa')
extension = serializers.CharField(source='extension.name', read_only=True)
cidrs = serializers.ListField(child=serializers.CharField(), source='ip_set_cidrs_as_str', read_only=True)
ns_records = NSRecordSerializer(many=True, source='extension.ns_set')
mx_records = MXRecordSerializer(many=True, source='extension.mx_set')
txt_records = TXTRecordSerializer(many=True, source='extension.txt_set')
ptr_records = ARecordSerializer(many=True, source='get_associated_ptr_records')
ptr_v6_records = AAAARecordSerializer(many=True, source='get_associated_ptr_v6_records')
class Meta:
model = machines.IpType
fields = ('type', 'extension', 'soa', 'ns_records', 'mx_records',
'txt_records', 'ptr_records', 'ptr_v6_records', 'cidrs',
'prefix_v6', 'prefix_v6_length')
# MAILING # MAILING
...@@ -799,7 +872,7 @@ class MailingMemberSerializer(UserSerializer): ...@@ -799,7 +872,7 @@ class MailingMemberSerializer(UserSerializer):
"""Serialize the data about a mailing member. """Serialize the data about a mailing member.
""" """
class Meta(UserSerializer.Meta): class Meta(UserSerializer.Meta):
fields = ('name', 'pseudo', 'email') fields = ('name', 'pseudo', 'get_mail')
class MailingSerializer(ClubSerializer): class MailingSerializer(ClubSerializer):
"""Serialize the data about a mailing. """Serialize the data about a mailing.
......
...@@ -81,10 +81,12 @@ router.register_viewset(r'topologie/modelswitch', views.ModelSwitchViewSet) ...@@ -81,10 +81,12 @@ router.register_viewset(r'topologie/modelswitch', views.ModelSwitchViewSet)
router.register_viewset(r'topologie/constructorswitch', views.ConstructorSwitchViewSet) router.register_viewset(r'topologie/constructorswitch', views.ConstructorSwitchViewSet)
router.register_viewset(r'topologie/switchbay', views.SwitchBayViewSet) router.register_viewset(r'topologie/switchbay', views.SwitchBayViewSet)
router.register_viewset(r'topologie/building', views.BuildingViewSet) router.register_viewset(r'topologie/building', views.BuildingViewSet)
router.register_viewset(r'topologie/switchport', views.SwitchPortViewSet, base_name='switchport') router.register(r'topologie/switchport', views.SwitchPortViewSet, base_name='switchport')
router.register_viewset(r'topologie/room', views.RoomViewSet) router.register_viewset(r'topologie/room', views.RoomViewSet)
router.register(r'topologie/portprofile', views.PortProfileViewSet)
# USERS # USERS
router.register_viewset(r'users/user', views.UserViewSet) router.register_viewset(r'users/user', views.UserViewSet)
router.register_viewset(r'users/homecreation', views.HomeCreationViewSet)
router.register_viewset(r'users/club', views.ClubViewSet) router.register_viewset(r'users/club', views.ClubViewSet)
router.register_viewset(r'users/adherent', views.AdherentViewSet) router.register_viewset(r'users/adherent', views.AdherentViewSet)
router.register_viewset(r'users/serviceuser', views.ServiceUserViewSet) router.register_viewset(r'users/serviceuser', views.ServiceUserViewSet)
...@@ -100,8 +102,12 @@ router.register_viewset(r'services/regen', views.ServiceRegenViewSet, base_name= ...@@ -100,8 +102,12 @@ router.register_viewset(r'services/regen', views.ServiceRegenViewSet, base_name=
router.register_view(r'dhcp/hostmacip', views.HostMacIpView), router.register_view(r'dhcp/hostmacip', views.HostMacIpView),
# LOCAL EMAILS # LOCAL EMAILS
router.register_view(r'localemail/users', views.LocalEmailUsersView), router.register_view(r'localemail/users', views.LocalEmailUsersView),
# Firewall
router.register_view(r'firewall/subnet-ports', views.SubnetPortsOpenView),
router.register_view(r'firewall/interface-ports', views.InterfacePortsOpenView),
# DNS # DNS
router.register_view(r'dns/zones', views.DNSZonesView), router.register_view(r'dns/zones', views.DNSZonesView),
router.register_view(r'dns/reverse-zones', views.DNSReverseZonesView),
# MAILING # MAILING
router.register_view(r'mailing/standard', views.StandardMailingView), router.register_view(r'mailing/standard', views.StandardMailingView),
router.register_view(r'mailing/club', views.ClubMailingView), router.register_view(r'mailing/club', views.ClubMailingView),
......
...@@ -403,6 +403,12 @@ class RoomViewSet(viewsets.ReadOnlyModelViewSet): ...@@ -403,6 +403,12 @@ class RoomViewSet(viewsets.ReadOnlyModelViewSet):
serializer_class = serializers.RoomSerializer serializer_class = serializers.RoomSerializer
class PortProfileViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `topologie.models.PortProfile` objects.
"""
queryset = topologie.PortProfile.objects.all()
serializer_class = serializers.PortProfileSerializer
# USER # USER
...@@ -412,6 +418,11 @@ class UserViewSet(viewsets.ReadOnlyModelViewSet): ...@@ -412,6 +418,11 @@ class UserViewSet(viewsets.ReadOnlyModelViewSet):
queryset = users.User.objects.all() queryset = users.User.objects.all()
serializer_class = serializers.UserSerializer serializer_class = serializers.UserSerializer
class HomeCreationViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes infos of `users.models.Users` objects to create homes.
"""
queryset = users.User.objects.all()
serializer_class = serializers.HomeCreationSerializer
class ClubViewSet(viewsets.ReadOnlyModelViewSet): class ClubViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `users.models.Club` objects. """Exposes list and details of `users.models.Club` objects.
...@@ -532,11 +543,21 @@ class HostMacIpView(generics.ListAPIView): ...@@ -532,11 +543,21 @@ class HostMacIpView(generics.ListAPIView):
serializer_class = serializers.HostMacIpSerializer serializer_class = serializers.HostMacIpSerializer
#Firewall
class SubnetPortsOpenView(generics.ListAPIView):
queryset = machines.IpType.objects.all()
serializer_class = serializers.SubnetPortsOpenSerializer
class InterfacePortsOpenView(generics.ListAPIView):
queryset = machines.Interface.objects.filter(port_lists__isnull=False).distinct()
serializer_class = serializers.InterfacePortsOpenSerializer
# DNS # DNS
class DNSZonesView(generics.ListAPIView): class DNSZonesView(generics.ListAPIView):
"""Exposes the detailed information about each extension (hostnames, """Exposes the detailed information about each extension (hostnames,
IPs, DNS records, etc.) in order to build the DNS zone files. IPs, DNS records, etc.) in order to build the DNS zone files.
""" """
queryset = (machines.Extension.objects queryset = (machines.Extension.objects
...@@ -549,6 +570,15 @@ class DNSZonesView(generics.ListAPIView): ...@@ -549,6 +570,15 @@ class DNSZonesView(generics.ListAPIView):
.all()) .all())
serializer_class = serializers.DNSZonesSerializer serializer_class = serializers.DNSZonesSerializer
class DNSReverseZonesView(generics.ListAPIView):
"""Exposes the detailed information about each extension (hostnames,
IPs, DNS records, etc.) in order to build the DNS zone files.
"""
queryset = (machines.IpType.objects.all())
serializer_class = serializers.DNSReverseZonesSerializer
# MAILING # MAILING
......
...@@ -42,4 +42,5 @@ def can_view(user): ...@@ -42,4 +42,5 @@ def can_view(user):
if can: if can:
return can, None return can, None
else: else:
return can, _("You don't have the rights to see this application.") return can, _("You don't have the right to view this application.")
...@@ -30,6 +30,7 @@ from django.contrib import admin ...@@ -30,6 +30,7 @@ from django.contrib import admin
from reversion.admin import VersionAdmin from reversion.admin import VersionAdmin
from .models import Facture, Article, Banque, Paiement, Cotisation, Vente from .models import Facture, Article, Banque, Paiement, Cotisation, Vente
from .models import CustomInvoice
class FactureAdmin(VersionAdmin): class FactureAdmin(VersionAdmin):
...@@ -37,6 +38,11 @@ class FactureAdmin(VersionAdmin): ...@@ -37,6 +38,11 @@ class FactureAdmin(VersionAdmin):
pass pass
class CustomInvoiceAdmin(VersionAdmin):
"""Admin class for custom invoices."""
pass
class VenteAdmin(VersionAdmin): class VenteAdmin(VersionAdmin):
"""Class admin d'une vente, tous les champs (facture related)""" """Class admin d'une vente, tous les champs (facture related)"""
pass pass
...@@ -69,3 +75,4 @@ admin.site.register(Banque, BanqueAdmin) ...@@ -69,3 +75,4 @@ admin.site.register(Banque, BanqueAdmin)
admin.site.register(Paiement, PaiementAdmin) admin.site.register(Paiement, PaiementAdmin)
admin.site.register(Vente, VenteAdmin) admin.site.register(Vente, VenteAdmin)
admin.site.register(Cotisation, CotisationAdmin) admin.site.register(Cotisation, CotisationAdmin)
admin.site.register(CustomInvoice, CustomInvoiceAdmin)
...@@ -40,13 +40,13 @@ from django import forms ...@@ -40,13 +40,13 @@ from django import forms
from django.db.models import Q from django.db.models import Q
from django.forms import ModelForm, Form from django.forms import ModelForm, Form
from django.core.validators import MinValueValidator from django.core.validators import MinValueValidator
from django.utils.translation import ugettext as _
from django.utils.translation import ugettext_lazy as _l from django.utils.translation import ugettext_lazy as _
from django.shortcuts import get_object_or_404 from django.shortcuts import get_object_or_404
from re2o.field_permissions import FieldPermissionFormMixin from re2o.field_permissions import FieldPermissionFormMixin
from re2o.mixins import FormRevMixin from re2o.mixins import FormRevMixin
from .models import Article, Paiement, Facture, Banque from .models import Article, Paiement, Facture, Banque, CustomInvoice
from .payment_methods import balance from .payment_methods import balance
...@@ -84,71 +84,36 @@ class FactureForm(FieldPermissionFormMixin, FormRevMixin, ModelForm): ...@@ -84,71 +84,36 @@ class FactureForm(FieldPermissionFormMixin, FormRevMixin, ModelForm):
return cleaned_data return cleaned_data
class SelectUserArticleForm(FormRevMixin, Form): class SelectArticleForm(FormRevMixin, Form):
""" """
Form used to select an article during the creation of an invoice for a Form used to select an article during the creation of an invoice for a
member. member.
""" """
article = forms.ModelChoiceField( article = forms.ModelChoiceField(
queryset=Article.objects.filter( queryset=Article.objects.none(),
Q(type_user='All') | Q(type_user='Adherent') label=_("Article"),
),
label=_l("Article"),
required=True required=True
) )
quantity = forms.IntegerField( quantity = forms.IntegerField(
label=_l("Quantity"), label=_("Quantity"),
validators=[MinValueValidator(1)], validators=[MinValueValidator(1)],
required=True required=True
) )
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
user = kwargs.pop('user') user = kwargs.pop('user')
super(SelectUserArticleForm, self).__init__(*args, **kwargs) target_user = kwargs.pop('target_user')
self.fields['article'].queryset = Article.find_allowed_articles(user) super(SelectArticleForm, self).__init__(*args, **kwargs)
self.fields['article'].queryset = Article.find_allowed_articles(user, target_user)
class SelectClubArticleForm(Form):
"""
Form used to select an article during the creation of an invoice for a
club.
"""
article = forms.ModelChoiceField(
queryset=Article.objects.filter(
Q(type_user='All') | Q(type_user='Club')
),
label=_l("Article"),
required=True
)
quantity = forms.IntegerField(
label=_l("Quantity"),
validators=[MinValueValidator(1)],
required=True
)
def __init__(self, user, *args, **kwargs):
super(SelectClubArticleForm, self).__init__(*args, **kwargs)
self.fields['article'].queryset = Article.find_allowed_articles(user)
# TODO : change Facture to Invoice class CustomInvoiceForm(FormRevMixin, ModelForm):
class NewFactureFormPdf(Form):
""" """
Form used to create a custom PDF invoice. Form used to create a custom invoice.
""" """
paid = forms.BooleanField(label=_l("Paid"), required=False) class Meta:
# TODO : change dest field to recipient model = CustomInvoice
dest = forms.CharField( fields = '__all__'
required=True,
max_length=255,
label=_l("Recipient")
)
# TODO : change chambre field to address
chambre = forms.CharField(
required=False,
max_length=10,
label=_l("Address")
)
class ArticleForm(FormRevMixin, ModelForm): class ArticleForm(FormRevMixin, ModelForm):
...@@ -172,7 +137,7 @@ class DelArticleForm(FormRevMixin, Form): ...@@ -172,7 +137,7 @@ class DelArticleForm(FormRevMixin, Form):
""" """
articles = forms.ModelMultipleChoiceField( articles = forms.ModelMultipleChoiceField(
queryset=Article.objects.none(), queryset=Article.objects.none(),
label=_l("Existing articles"), label=_("Available articles"),
widget=forms.CheckboxSelectMultiple widget=forms.CheckboxSelectMultiple
) )
...@@ -212,7 +177,7 @@ class DelPaiementForm(FormRevMixin, Form): ...@@ -212,7 +177,7 @@ class DelPaiementForm(FormRevMixin, Form):
# TODO : change paiement to payment # TODO : change paiement to payment
paiements = forms.ModelMultipleChoiceField( paiements = forms.ModelMultipleChoiceField(
queryset=Paiement.objects.none(), queryset=Paiement.objects.none(),
label=_l("Existing payment method"), label=_("Available payment methods"),
widget=forms.CheckboxSelectMultiple widget=forms.CheckboxSelectMultiple
) )
...@@ -250,7 +215,7 @@ class DelBanqueForm(FormRevMixin, Form): ...@@ -250,7 +215,7 @@ class DelBanqueForm(FormRevMixin, Form):
# TODO : change banque to bank # TODO : change banque to bank
banques = forms.ModelMultipleChoiceField( banques = forms.ModelMultipleChoiceField(
queryset=Banque.objects.none(), queryset=Banque.objects.none(),
label=_l("Existing banks"), label=_("Available banks"),
widget=forms.CheckboxSelectMultiple widget=forms.CheckboxSelectMultiple
) )
...@@ -269,21 +234,21 @@ class RechargeForm(FormRevMixin, Form): ...@@ -269,21 +234,21 @@ class RechargeForm(FormRevMixin, Form):
Form used to refill a user's balance Form used to refill a user's balance
""" """
value = forms.FloatField( value = forms.FloatField(
label=_l("Amount"), label=_("Amount"),
min_value=0.01, min_value=0.01,
validators=[] validators=[]
) )
payment = forms.ModelChoiceField( payment = forms.ModelChoiceField(
queryset=Paiement.objects.none(), queryset=Paiement.objects.none(),
label=_l("Payment method") label=_("Payment method")
) )
def __init__(self, *args, user=None, **kwargs): def __init__(self, *args<