misc: apply double-quote-string-fixer (#79788)

This commit is contained in:
Valentin Deniaud 2023-08-16 11:52:16 +02:00
parent 5b36982e20
commit f1941a3ad8
24 changed files with 300 additions and 300 deletions

6
debian/config.py vendored
View File

@ -14,7 +14,7 @@ STATIC_ROOT = os.path.join(VAR_DIR, 'collected-static')
MEDIA_ROOT = os.path.join(VAR_DIR, 'media') MEDIA_ROOT = os.path.join(VAR_DIR, 'media')
ETC_DIR = '/etc/barbacompta/' ETC_DIR = '/etc/barbacompta/'
with open("/etc/barbacompta/secret") as fd: with open('/etc/barbacompta/secret') as fd:
SECRET_KEY = fd.read() SECRET_KEY = fd.read()
DEBUG = False DEBUG = False
@ -29,8 +29,8 @@ if os.path.exists('/etc/barbacompta/idp-metadata.xml'):
'first_name': '{attributes[first_name][0]}', 'first_name': '{attributes[first_name][0]}',
'last_name': '{attributes[last_name][0]}', 'last_name': '{attributes[last_name][0]}',
} }
MELLON_SUPERUSER_MAPPING = {"is_superuser": ("true",)} MELLON_SUPERUSER_MAPPING = {'is_superuser': ('true',)}
MELLON_USERNAME_TEMPLATE = "{attributes[username][0]}" MELLON_USERNAME_TEMPLATE = '{attributes[username][0]}'
MELLON_PUBLIC_KEYS = ['/etc/barbacompta/saml.crt'] MELLON_PUBLIC_KEYS = ['/etc/barbacompta/saml.crt']
MELLON_PRIVATE_KEY = '/etc/barbacompta/saml.key' MELLON_PRIVATE_KEY = '/etc/barbacompta/saml.key'
MELLON_IDENTITY_PROVIDERS = [{'METADATA': '/etc/barbacompta/idp-metadata.xml'}] MELLON_IDENTITY_PROVIDERS = [{'METADATA': '/etc/barbacompta/idp-metadata.xml'}]

View File

@ -32,8 +32,8 @@ def export_as_csv(modeladmin, request, queryset):
if not request.user.is_staff: if not request.user.is_staff:
raise PermissionDenied raise PermissionDenied
opts = modeladmin.model._meta opts = modeladmin.model._meta
response = HttpResponse(content_type="text/csv") response = HttpResponse(content_type='text/csv')
response["Content-Disposition"] = "attachment; filename=%s.csv" % str(opts).replace(".", "_") response['Content-Disposition'] = 'attachment; filename=%s.csv' % str(opts).replace('.', '_')
writer = csv.writer(response) writer = csv.writer(response)
field_names = [field.name for field in opts.fields] field_names = [field.name for field in opts.fields]
m2m_field_names = [m2m_field.name for m2m_field in opts.many_to_many] m2m_field_names = [m2m_field.name for m2m_field in opts.many_to_many]
@ -44,13 +44,13 @@ def export_as_csv(modeladmin, request, queryset):
values = [str(getattr(obj, field)) for field in field_names] values = [str(getattr(obj, field)) for field in field_names]
for m2m_field in m2m_field_names: for m2m_field in m2m_field_names:
value = getattr(obj, m2m_field) value = getattr(obj, m2m_field)
value = ",".join(map(str, value.all())) value = ','.join(map(str, value.all()))
values.append(str(value)) values.append(str(value))
writer.writerow(map(lambda x: str.encode(x, "utf8"), values)) writer.writerow(map(lambda x: str.encode(x, 'utf8'), values))
return response return response
export_as_csv.short_description = "Export selected objects as csv file" export_as_csv.short_description = 'Export selected objects as csv file'
def export_references_as_fodt(modeladmin, request, queryset): def export_references_as_fodt(modeladmin, request, queryset):
@ -73,7 +73,7 @@ def export_references_as_fodt(modeladmin, request, queryset):
return response return response
export_references_as_fodt.short_description = _("Export selected contracts as fodt file") export_references_as_fodt.short_description = _('Export selected contracts as fodt file')
def export_invoices_as_zip(modeladmin, request, queryset): def export_invoices_as_zip(modeladmin, request, queryset):
@ -88,4 +88,4 @@ def export_invoices_as_zip(modeladmin, request, queryset):
return response return response
export_invoices_as_zip.short_description = _("Export selected invoices") export_invoices_as_zip.short_description = _('Export selected invoices')

View File

@ -33,19 +33,19 @@ User = get_user_model()
class EOGestionAdminSite(admin.AdminSite): class EOGestionAdminSite(admin.AdminSite):
@never_cache @never_cache
def login(self, request, extra_context=None): def login(self, request, extra_context=None):
if "mellon" in settings.INSTALLED_APPS: if 'mellon' in settings.INSTALLED_APPS:
next_url = request.GET.get(REDIRECT_FIELD_NAME, settings.LOGIN_REDIRECT_URL or "/") next_url = request.GET.get(REDIRECT_FIELD_NAME, settings.LOGIN_REDIRECT_URL or '/')
query = urlencode({REDIRECT_FIELD_NAME: next_url}) query = urlencode({REDIRECT_FIELD_NAME: next_url})
url = f"/accounts/mellon/login/?{query}" url = f'/accounts/mellon/login/?{query}'
return HttpResponseRedirect(url) return HttpResponseRedirect(url)
return super().login(request, extra_context=extra_context) return super().login(request, extra_context=extra_context)
@never_cache @never_cache
def logout(self, request, extra_context=None): def logout(self, request, extra_context=None):
if "mellon" in settings.INSTALLED_APPS: if 'mellon' in settings.INSTALLED_APPS:
next_url = request.GET.get(REDIRECT_FIELD_NAME, settings.LOGIN_REDIRECT_URL or "/") next_url = request.GET.get(REDIRECT_FIELD_NAME, settings.LOGIN_REDIRECT_URL or '/')
query = urlencode({REDIRECT_FIELD_NAME: next_url}) query = urlencode({REDIRECT_FIELD_NAME: next_url})
url = f"/accounts/mellon/logout/?{query}" url = f'/accounts/mellon/logout/?{query}'
return HttpResponseRedirect(url) return HttpResponseRedirect(url)
return super().logout(request, extra_context=extra_context) return super().logout(request, extra_context=extra_context)

View File

@ -85,7 +85,7 @@ class LigneBanquePopAdmin(admin.ModelAdmin):
def get_queryset(self, request): def get_queryset(self, request):
qs = super().get_queryset(request) qs = super().get_queryset(request)
qs = qs.prefetch_related("payments") qs = qs.prefetch_related('payments')
return qs return qs
def has_add_permission(self, request): def has_add_permission(self, request):

View File

@ -29,7 +29,7 @@ class Command(BaseCommand):
] ]
def add_arguments(self, parser): def add_arguments(self, parser):
parser.add_argument("args", nargs="+") parser.add_argument('args', nargs='+')
def to_date(self, str): def to_date(self, str):
try: try:
@ -38,13 +38,13 @@ class Command(BaseCommand):
return datetime.strptime(str, '%d/%m/%Y').date() return datetime.strptime(str, '%d/%m/%Y').date()
def make_csv_reader(self, csv_file): def make_csv_reader(self, csv_file):
for delimiter in [";", "\t"]: for delimiter in [';', '\t']:
csv_file.seek(0) csv_file.seek(0)
csv_lines = csv.reader(csv_file, delimiter=delimiter) csv_lines = csv.reader(csv_file, delimiter=delimiter)
first_line = next(csv_lines) first_line = next(csv_lines)
if first_line == self.HEADER: if first_line == self.HEADER:
return csv_lines return csv_lines
raise CommandError("Invalid CSV file header") raise CommandError('Invalid CSV file header')
def load_one_file(self, csv_file_path, csv_lines): def load_one_file(self, csv_file_path, csv_lines):
counter = 0 counter = 0

View File

@ -14,8 +14,8 @@ class Command(BaseCommand):
can_import_django_settings = True can_import_django_settings = True
output_transaction = True output_transaction = True
requires_system_checks = True requires_system_checks = True
args = "<csv_file> <csv_file>..." args = '<csv_file> <csv_file>...'
help = "Charge le solde courant" help = 'Charge le solde courant'
@transaction.atomic @transaction.atomic
def handle(self, compte, montant, **options): def handle(self, compte, montant, **options):

View File

@ -26,7 +26,7 @@ def solde(t=None):
if t is None: if t is None:
t = date.today() t = date.today()
try: try:
s = SoldeBanquePop.objects.latest("date") s = SoldeBanquePop.objects.latest('date')
except SoldeBanquePop.DoesNotExist: except SoldeBanquePop.DoesNotExist:
return 0 return 0
m = s.montant m = s.montant
@ -50,10 +50,10 @@ class Commentaire(models.Model):
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE) content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
object_id = models.PositiveIntegerField() object_id = models.PositiveIntegerField()
creation = models.DateTimeField(auto_now_add=True) creation = models.DateTimeField(auto_now_add=True)
content_object = GenericForeignKey("content_type", "object_id") content_object = GenericForeignKey('content_type', 'object_id')
def __str__(self): def __str__(self):
return "Commentaire créé le %s" % self.creation return 'Commentaire créé le %s' % self.creation
class LigneBanquePop(models.Model): class LigneBanquePop(models.Model):
@ -78,7 +78,7 @@ class LigneBanquePop(models.Model):
] ]
def __str__(self): def __str__(self):
return "%(date_valeur)s %(libelle)s %(montant)s" % self.__dict__ return '%(date_valeur)s %(libelle)s %(montant)s' % self.__dict__
def montant_non_affecte(self): def montant_non_affecte(self):
return self.montant - sum(x.montant_affecte for x in self.payments.all()) return self.montant - sum(x.montant_affecte for x in self.payments.all())

View File

@ -34,7 +34,7 @@ def month_before(date):
return date.replace(month=date.month - 1) return date.replace(month=date.month - 1)
@register.inclusion_tag("eo_banque/finances.html") @register.inclusion_tag('eo_banque/finances.html')
@cache @cache
def finances(month=3): def finances(month=3):
dates = [date.today().replace(day=1)] dates = [date.today().replace(day=1)]
@ -82,8 +82,8 @@ def finances(month=3):
@register.inclusion_tag('eo_banque/total.html', takes_context=True) @register.inclusion_tag('eo_banque/total.html', takes_context=True)
def total(context): def total(context):
qs = context["cl"].get_queryset(context["request"]) qs = context['cl'].get_queryset(context['request'])
ls = [ligne[0] for ligne in qs.values_list("montant")] ls = [ligne[0] for ligne in qs.values_list('montant')]
credit = sum(montant for montant in ls if montant > 0) credit = sum(montant for montant in ls if montant > 0)
debit = sum(montant for montant in ls if montant < 0) debit = sum(montant for montant in ls if montant < 0)
total = sum(ls) total = sum(ls)
@ -91,14 +91,14 @@ def total(context):
def iso_year_start(iso_year): def iso_year_start(iso_year):
"The gregorian calendar date of the first day of the given ISO year" 'The gregorian calendar date of the first day of the given ISO year'
fourth_jan = date(iso_year, 1, 4) fourth_jan = date(iso_year, 1, 4)
delta = timedelta(fourth_jan.isoweekday() - 1) delta = timedelta(fourth_jan.isoweekday() - 1)
return fourth_jan - delta return fourth_jan - delta
def iso_to_gregorian(iso_year, iso_week, iso_day): def iso_to_gregorian(iso_year, iso_week, iso_day):
"Gregorian calendar date for the given ISO year, week and day" 'Gregorian calendar date for the given ISO year, week and day'
year_start = iso_year_start(iso_year) year_start = iso_year_start(iso_year)
return year_start + timedelta(iso_day - 1, 0, 0, 0, 0, 0, iso_week - 1) return year_start + timedelta(iso_day - 1, 0, 0, 0, 0, 0, iso_week - 1)
@ -123,16 +123,16 @@ def week_start_and_end(year, month):
] ]
@register.inclusion_tag("admin/date_hierarchy.html") @register.inclusion_tag('admin/date_hierarchy.html')
def eo_banque_date_hierarchy(cl): def eo_banque_date_hierarchy(cl):
if cl.date_hierarchy: if cl.date_hierarchy:
field_name = cl.date_hierarchy field_name = cl.date_hierarchy
year_field = "%s__year" % field_name year_field = '%s__year' % field_name
month_field = "%s__month" % field_name month_field = '%s__month' % field_name
day_field = "%s__day" % field_name day_field = '%s__day' % field_name
field_generic = "%s__" % field_name field_generic = '%s__' % field_name
field_gte = "%s__gte" % field_name field_gte = '%s__gte' % field_name
field_lte = "%s__lte" % field_name field_lte = '%s__lte' % field_name
year_lookup = cl.params.get(year_field) year_lookup = cl.params.get(year_field)
month_lookup = cl.params.get(month_field) month_lookup = cl.params.get(month_field)
day_lookup = cl.params.get(day_field) day_lookup = cl.params.get(day_field)

View File

@ -75,11 +75,11 @@ class PaymentInlineFormset(BaseInlineFormSet):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
for form in self.forms: for form in self.forms:
if form.instance.id is None: if form.instance.id is None:
field = form.fields["ligne_banque_pop"] field = form.fields['ligne_banque_pop']
if not hasattr(field, "parent_instance"): if not hasattr(field, 'parent_instance'):
field.queryset = models.encaissements_avec_solde_non_affecte() field.queryset = models.encaissements_avec_solde_non_affecte()
field = form.fields["facture"] field = form.fields['facture']
if not hasattr(field, "parent_instance"): if not hasattr(field, 'parent_instance'):
field.queryset = models.Facture.objects.avec_solde() field.queryset = models.Facture.objects.avec_solde()
@ -96,7 +96,7 @@ class LigneInline(SelectRelatedMixin, SortableInlineAdminMixin, admin.TabularInl
form = forms.LigneForm form = forms.LigneForm
model = models.Ligne model = models.Ligne
original = False original = False
verbose_name_plural = "Lignes de facture (vous pouvez les réordonner par drag&drop)" verbose_name_plural = 'Lignes de facture (vous pouvez les réordonner par drag&drop)'
formfield_overrides = { formfield_overrides = {
TextField: { TextField: {
'widget': Textarea(attrs={'cols': 30, 'rows': 1}), 'widget': Textarea(attrs={'cols': 30, 'rows': 1}),
@ -156,7 +156,7 @@ def show_client(obj):
url = reverse('admin:eo_facture_client_change', args=[obj.client.id]) url = reverse('admin:eo_facture_client_change', args=[obj.client.id])
return format_html('<a href="{0}">{1}</a>', url, obj.client) return format_html('<a href="{0}">{1}</a>', url, obj.client)
else: else:
return "" return ''
show_client.short_description = 'Client' show_client.short_description = 'Client'
@ -169,7 +169,7 @@ class ContratAdmin(LookupAllowed, admin.ModelAdmin):
PrestationInline, PrestationInline,
] ]
list_display = [ list_display = [
"__str__", '__str__',
show_client, show_client,
'column_montant', 'column_montant',
'pourcentage_facture', 'pourcentage_facture',
@ -226,7 +226,7 @@ class ContratAdmin(LookupAllowed, admin.ModelAdmin):
'opts': self.model._meta, 'opts': self.model._meta,
} }
return render(request, "admin/eo_facture/contrat/duplicate.html", context=context) return render(request, 'admin/eo_facture/contrat/duplicate.html', context=context)
def facturer_echeance(self, request, object_id): def facturer_echeance(self, request, object_id):
if request.method != 'POST': if request.method != 'POST':
@ -284,7 +284,7 @@ def index(facture):
return format_html('{0}', ordinal(facture.index())) return format_html('{0}', ordinal(facture.index()))
index.short_description = "Ordre" index.short_description = 'Ordre'
class FactureAdmin(LookupAllowed, admin.ModelAdmin): class FactureAdmin(LookupAllowed, admin.ModelAdmin):
@ -333,7 +333,7 @@ class FactureAdmin(LookupAllowed, admin.ModelAdmin):
def column_code(self, obj): def column_code(self, obj):
if obj.montant < 0: if obj.montant < 0:
return "Avoir %s" % obj.code() return 'Avoir %s' % obj.code()
else: else:
return obj.code() return obj.code()
@ -364,16 +364,16 @@ class FactureAdmin(LookupAllowed, admin.ModelAdmin):
from django.db import connection from django.db import connection
qs = super().get_queryset(request) qs = super().get_queryset(request)
qs = qs.prefetch_related("lignes__facture__client") qs = qs.prefetch_related('lignes__facture__client')
qs = qs.prefetch_related("payments__ligne_banque_pop") qs = qs.prefetch_related('payments__ligne_banque_pop')
qs = qs.prefetch_related("contrat__factures") qs = qs.prefetch_related('contrat__factures')
if connection.vendor == "postgresql": if connection.vendor == 'postgresql':
qs = qs.extra({"year": "EXTRACT(year FROM emission)"}) qs = qs.extra({'year': 'EXTRACT(year FROM emission)'})
elif connection.vendor == "sqlite": elif connection.vendor == 'sqlite':
qs = qs.extra({"year": "strftime('%Y', emission)"}) qs = qs.extra({'year': "strftime('%Y', emission)"})
else: else:
qs = qs.extra({"year": "YEAR(emission)"}) qs = qs.extra({'year': 'YEAR(emission)'})
qs = qs.order_by("-year", "-proforma", "-ordre") qs = qs.order_by('-year', '-proforma', '-ordre')
return qs return qs
def save_model(self, request, obj, form, change): def save_model(self, request, obj, form, change):
@ -383,7 +383,7 @@ class FactureAdmin(LookupAllowed, admin.ModelAdmin):
def add_simple(self, request): def add_simple(self, request):
context = {} context = {}
if request.method == "POST": if request.method == 'POST':
form = forms.RapidFactureForm(request=request, data=request.POST) form = forms.RapidFactureForm(request=request, data=request.POST)
if form.is_valid(): if form.is_valid():
return http.HttpResponseRedirect( return http.HttpResponseRedirect(
@ -398,11 +398,11 @@ class FactureAdmin(LookupAllowed, admin.ModelAdmin):
'opts': self.model._meta, 'opts': self.model._meta,
} }
return render(request, "admin/eo_facture/facture/add_simple.html", context=context) return render(request, 'admin/eo_facture/facture/add_simple.html', context=context)
def sheet(self, request): def sheet(self, request):
workbook = ods.Workbook() workbook = ods.Workbook()
ws = workbook.add_sheet("Factures") ws = workbook.add_sheet('Factures')
for i, header_name in enumerate( for i, header_name in enumerate(
['Référence', 'Nom client', 'Date', 'Montant HT', 'Montant TTC', 'TVA'] ['Référence', 'Nom client', 'Date', 'Montant HT', 'Montant TTC', 'TVA']
): ):
@ -447,9 +447,9 @@ class FactureAdmin(LookupAllowed, admin.ModelAdmin):
name='eo_facture_facture_send_to_chorus', name='eo_facture_facture_send_to_chorus',
), ),
url( url(
r"^sheet/$", r'^sheet/$',
self.admin_site.admin_view(self.sheet), self.admin_site.admin_view(self.sheet),
name="eo_facture_facture_sheet", name='eo_facture_facture_sheet',
), ),
url( url(
r'^(.+)/cancel/', r'^(.+)/cancel/',

View File

@ -31,8 +31,8 @@ class PercentagePerYear(list):
class EuroField(models.DecimalField): class EuroField(models.DecimalField):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
kwargs["max_digits"] = 8 kwargs['max_digits'] = 8
kwargs["decimal_places"] = 2 kwargs['decimal_places'] = 2
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
@ -51,22 +51,22 @@ def check_percentage_per_year(value):
years = [a for a, b in value] years = [a for a, b in value]
percentages = [b for a, b in value] percentages = [b for a, b in value]
# ordered # ordered
assert years == sorted(years), "years are not ordered" assert years == sorted(years), 'years are not ordered'
# sum equals 100 # sum equals 100
assert sum(percentages) == 1, "percentage does not sum to 100" assert sum(percentages) == 1, 'percentage does not sum to 100'
# no duplicate year # no duplicate year
assert len(years) == len(set(years)), "years are not unique" assert len(years) == len(set(years)), 'years are not unique'
# consecutive # consecutive
assert years == list(range(years[0], years[0] + len(years))), 'years are not consecutive' assert years == list(range(years[0], years[0] + len(years))), 'years are not consecutive'
def parse_percentage_per_year(value): def parse_percentage_per_year(value):
msg = _("field must be numeric values separated by commas") msg = _('field must be numeric values separated by commas')
values = value.split(",") values = value.split(',')
decimals = [] decimals = []
for value in values: for value in values:
try: try:
year, decimal = value.split(":") year, decimal = value.split(':')
except ValueError: except ValueError:
raise ValidationError(msg) raise ValidationError(msg)
try: try:
@ -92,7 +92,7 @@ class PercentagePerYearFormField(forms.Field):
if isinstance(PercentagePerYear, value): if isinstance(PercentagePerYear, value):
return value return value
if not isinstance(str, value): if not isinstance(str, value):
raise ValidationError(self.default_error_messages["invalid"]) raise ValidationError(self.default_error_messages['invalid'])
return parse_percentage_per_year(value) return parse_percentage_per_year(value)
@ -114,7 +114,7 @@ class PercentagePerYearField(models.Field):
default_validators = [check_percentage_per_year] default_validators = [check_percentage_per_year]
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
kwargs["max_length"] = 64 kwargs['max_length'] = 64
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
def from_db_value(self, value, expression, connection): def from_db_value(self, value, expression, connection):
@ -125,12 +125,12 @@ class PercentagePerYearField(models.Field):
return value return value
if value is not None: if value is not None:
percentage_per_year = [] percentage_per_year = []
pairs = value.split(",") pairs = value.split(',')
for pair in pairs: for pair in pairs:
try: try:
year, percentage = map(value.__class__.strip, pair.split(":")) year, percentage = map(value.__class__.strip, pair.split(':'))
except ValueError: except ValueError:
raise ValidationError(PercentagePerYearFormField.default_error_messages["invalid"]) raise ValidationError(PercentagePerYearFormField.default_error_messages['invalid'])
year = int(year) year = int(year)
percentage = Decimal(percentage) / Decimal(100) percentage = Decimal(percentage) / Decimal(100)
percentage_per_year.append((year, percentage)) percentage_per_year.append((year, percentage))
@ -150,10 +150,10 @@ class PercentagePerYearField(models.Field):
return super().get_prep_value(value) return super().get_prep_value(value)
def get_internal_type(self): def get_internal_type(self):
return "CharField" return 'CharField'
def formfield(self, **kwargs): def formfield(self, **kwargs):
defaults = {"form_class": PercentagePerYearFormField} defaults = {'form_class': PercentagePerYearFormField}
defaults.update(kwargs) defaults.update(kwargs)
return super().formfield(**kwargs) return super().formfield(**kwargs)

View File

@ -26,7 +26,7 @@ from django.db.transaction import atomic
from . import models from . import models
pourcentages = [(Decimal(i) / 100, "%s %%" % i) for i in range(0, 101, 5)] pourcentages = [(Decimal(i) / 100, '%s %%' % i) for i in range(0, 101, 5)]
class RapidFactureForm(forms.Form): class RapidFactureForm(forms.Form):
@ -51,7 +51,7 @@ class RapidFactureForm(forms.Form):
except ValidationError as e: except ValidationError as e:
raise forms.ValidationError('clean facture %s' % facture.code(), *e.args) raise forms.ValidationError('clean facture %s' % facture.code(), *e.args)
facture.save() facture.save()
self.cleaned_data["facture"] = facture self.cleaned_data['facture'] = facture
lignes = [] lignes = []
errors = [] errors = []
if solde: if solde:
@ -73,8 +73,8 @@ class RapidFactureForm(forms.Form):
try: try:
ligne.clean() ligne.clean()
except ValidationError as e: except ValidationError as e:
error = "Il y a un problème avec la ligne « %s »: " % prestation.intitule error = 'Il y a un problème avec la ligne « %s »: ' % prestation.intitule
error += "; ".join(map(lambda x: x.rstrip("."), e.messages)) error += '; '.join(map(lambda x: x.rstrip('.'), e.messages))
errors.append(error) errors.append(error)
if errors: if errors:
raise forms.ValidationError(errors) raise forms.ValidationError(errors)
@ -87,7 +87,7 @@ class RapidFactureForm(forms.Form):
class DuplicateContractForm(forms.Form): class DuplicateContractForm(forms.Form):
contrat = forms.ModelChoiceField(queryset=models.Contrat.objects.all()) contrat = forms.ModelChoiceField(queryset=models.Contrat.objects.all())
new_intitule = forms.CharField(max_length=150, label="Nouvel intitulé") new_intitule = forms.CharField(max_length=150, label='Nouvel intitulé')
def __init__(self, request=None, *args, **kwargs): def __init__(self, request=None, *args, **kwargs):
self.request = request self.request = request
@ -109,7 +109,7 @@ class DuplicateContractForm(forms.Form):
except ValidationError as e: except ValidationError as e:
raise forms.ValidationError('clean contrat %s' % contrat.intitule, *e.args) raise forms.ValidationError('clean contrat %s' % contrat.intitule, *e.args)
new_contrat.save() new_contrat.save()
self.cleaned_data["new_contrat"] = new_contrat self.cleaned_data['new_contrat'] = new_contrat
for prestation in contrat.prestations.all(): for prestation in contrat.prestations.all():
new_prestation = prestation.duplicate() new_prestation = prestation.duplicate()
new_prestation.contrat = new_contrat new_prestation.contrat = new_contrat
@ -121,14 +121,14 @@ class LigneForm(forms.ModelForm):
class Meta: class Meta:
fields = '__all__' fields = '__all__'
model = models.Ligne model = models.Ligne
localized_fields = ("quantite", "prix_unitaire_ht", "taux_tva") localized_fields = ('quantite', 'prix_unitaire_ht', 'taux_tva')
class PrestationForm(forms.ModelForm): class PrestationForm(forms.ModelForm):
class Meta: class Meta:
fields = '__all__' fields = '__all__'
model = models.Prestation model = models.Prestation
localized_fields = ("quantite", "prix_unitaire_ht") localized_fields = ('quantite', 'prix_unitaire_ht')
class FactureForm(forms.ModelForm): class FactureForm(forms.ModelForm):
@ -189,17 +189,17 @@ class FactureForm(forms.ModelForm):
class Meta: class Meta:
fields = '__all__' fields = '__all__'
model = models.Facture model = models.Facture
localized_fields = ("taux_tva",) localized_fields = ('taux_tva',)
class ClientForm(forms.ModelForm): class ClientForm(forms.ModelForm):
class Meta: class Meta:
fields = '__all__' fields = '__all__'
model = models.Client model = models.Client
localized_fields = ("tva",) localized_fields = ('tva',)
widgets = { widgets = {
"adresse": widgets.AdminTextareaWidget(attrs={"rows": 4}), 'adresse': widgets.AdminTextareaWidget(attrs={'rows': 4}),
"contacts": widgets.AdminTextareaWidget(attrs={"rows": 4}), 'contacts': widgets.AdminTextareaWidget(attrs={'rows': 4}),
} }
@ -215,7 +215,7 @@ class ContratForm(forms.ModelForm):
class Meta: class Meta:
fields = '__all__' fields = '__all__'
model = models.Contrat model = models.Contrat
localized_fields = ("tva", "montant_sous_traite") localized_fields = ('tva', 'montant_sous_traite')
class Media: class Media:
js = ['facture.js'] js = ['facture.js']
@ -225,4 +225,4 @@ class PaymentForm(forms.ModelForm):
class Meta: class Meta:
fields = '__all__' fields = '__all__'
model = models.Payment model = models.Payment
localized_fields = ("montant_affecte",) localized_fields = ('montant_affecte',)

View File

@ -42,13 +42,13 @@ from . import facturx, fields, taggit, validators
User = get_user_model() User = get_user_model()
validate_telephone = RegexValidator(r"[. 0-9]*") validate_telephone = RegexValidator(r'[. 0-9]*')
DEFAULT_TVA = getattr(settings, "TVA", "20") DEFAULT_TVA = getattr(settings, 'TVA', '20')
def accounting_rounding(amount): def accounting_rounding(amount):
return amount.quantize(Decimal("0.01"), ROUND_HALF_UP) return amount.quantize(Decimal('0.01'), ROUND_HALF_UP)
def today(): def today():
@ -122,11 +122,11 @@ class Client(models.Model):
def one_hundred_percent_this_year(): def one_hundred_percent_this_year():
return "%s:100" % now().date().year return '%s:100' % now().date().year
class Contrat(models.Model): class Contrat(models.Model):
client = models.ForeignKey(Client, related_name="contrats", on_delete=models.CASCADE, null=True) client = models.ForeignKey(Client, related_name='contrats', on_delete=models.CASCADE, null=True)
intitule = models.CharField(max_length=150) intitule = models.CharField(max_length=150)
description = models.TextField(blank=True) description = models.TextField(blank=True)
public_description = models.TextField( public_description = models.TextField(
@ -372,7 +372,7 @@ class Prestation(models.Model):
ordering = ('contrat', 'id') ordering = ('contrat', 'id')
DELAI_PAIEMENT = getattr(settings, "DELAI_PAIEMENT", 45) DELAI_PAIEMENT = getattr(settings, 'DELAI_PAIEMENT', 45)
def today_plus_delai(): def today_plus_delai():
@ -416,7 +416,7 @@ class Facture(models.Model):
) )
intitule = models.CharField(max_length=150, verbose_name='Intitulé', blank=True) intitule = models.CharField(max_length=150, verbose_name='Intitulé', blank=True)
notes = models.TextField(blank=True) notes = models.TextField(blank=True)
emission = models.DateField(verbose_name="Émission", default=today, db_index=True) emission = models.DateField(verbose_name='Émission', default=today, db_index=True)
echeance = models.DateField(verbose_name=echeance_verbose_name, default=today_plus_delai) echeance = models.DateField(verbose_name=echeance_verbose_name, default=today_plus_delai)
taux_tva = models.DecimalField( taux_tva = models.DecimalField(
max_digits=4, decimal_places=2, default=Decimal(DEFAULT_TVA), blank=True, null=True max_digits=4, decimal_places=2, default=Decimal(DEFAULT_TVA), blank=True, null=True
@ -426,7 +426,7 @@ class Facture(models.Model):
verbose_name='Montant sous-traité', verbose_name='Montant sous-traité',
help_text='indiquer une somme pas un pourcentage, hors-taxe', help_text='indiquer une somme pas un pourcentage, hors-taxe',
) )
paid = models.BooleanField(blank=True, verbose_name="Soldée", default=False, db_index=True) paid = models.BooleanField(blank=True, verbose_name='Soldée', default=False, db_index=True)
creator = models.ForeignKey(User, verbose_name='Créateur', on_delete=models.CASCADE) creator = models.ForeignKey(User, verbose_name='Créateur', on_delete=models.CASCADE)
account_on_previous_period = models.BooleanField( account_on_previous_period = models.BooleanField(
verbose_name='Mettre cette facture sur l\'exercice précédent', default=False verbose_name='Mettre cette facture sur l\'exercice précédent', default=False
@ -459,9 +459,9 @@ class Facture(models.Model):
ctx = {} ctx = {}
ctx.update(self.__dict__) ctx.update(self.__dict__)
ctx.update({'year': self.emission.year}) ctx.update({'year': self.emission.year})
if ctx["ordre"] is None: if ctx['ordre'] is None:
return "Ordre is missing" return 'Ordre is missing'
format = getattr(settings, "FACTURE_CODE_FORMAT", "F%(year)s%(ordre)04d") format = getattr(settings, 'FACTURE_CODE_FORMAT', 'F%(year)s%(ordre)04d')
return format % ctx return format % ctx
@property @property
@ -478,13 +478,13 @@ class Facture(models.Model):
def clean(self): def clean(self):
if not (self.contrat or self.client): if not (self.contrat or self.client):
raise ValidationError("La facture doit avoir un client ou un contrat") raise ValidationError('La facture doit avoir un client ou un contrat')
if self.contrat: if self.contrat:
if not self.intitule: if not self.intitule:
self.intitule = self.contrat.intitule self.intitule = self.contrat.intitule
if self.client: if self.client:
if self.client != self.contrat.client and self.contrat.client is not None: if self.client != self.contrat.client and self.contrat.client is not None:
raise ValidationError("Le client de la facture et du contrat doivent être identiques.") raise ValidationError('Le client de la facture et du contrat doivent être identiques.')
else: else:
self.client = self.contrat.client self.client = self.contrat.client
if self.contrat.periodicite and not self.annulation: if self.contrat.periodicite and not self.annulation:
@ -506,7 +506,7 @@ class Facture(models.Model):
) )
else: else:
if not self.intitule: if not self.intitule:
raise ValidationError("La facture doit avoir un intitulé") raise ValidationError('La facture doit avoir un intitulé')
if self.numero_d_echeance is not None: if self.numero_d_echeance is not None:
raise ValidationError( raise ValidationError(
'Un numéro d\'échéance ne peut être défini que pour un contrat récurrent.' 'Un numéro d\'échéance ne peut être défini que pour un contrat récurrent.'
@ -516,7 +516,7 @@ class Facture(models.Model):
for ligne in self.lignes.all(): for ligne in self.lignes.all():
ligne.clean() ligne.clean()
except ValidationError: except ValidationError:
raise ValidationError("Il y a un problème avec les lignes de cette facture") raise ValidationError('Il y a un problème avec les lignes de cette facture')
self.update_paid(save=False) self.update_paid(save=False)
self.numero_engagement = self.numero_engagement.strip() self.numero_engagement = self.numero_engagement.strip()
if ( if (
@ -544,7 +544,7 @@ class Facture(models.Model):
amount_by_vat[ligne.tva] += ligne.montant amount_by_vat[ligne.tva] += ligne.montant
vat = Decimal(0) vat = Decimal(0)
for vat_percentage, amount in amount_by_vat.items(): for vat_percentage, amount in amount_by_vat.items():
var_ratio = vat_percentage / Decimal("100") var_ratio = vat_percentage / Decimal('100')
vat += accounting_rounding(var_ratio * amount) vat += accounting_rounding(var_ratio * amount)
return vat return vat
@ -627,7 +627,7 @@ class Facture(models.Model):
facture_avoir.ordre = None facture_avoir.ordre = None
facture_avoir.annulation = self facture_avoir.annulation = self
facture_avoir.proforma = True facture_avoir.proforma = True
facture_avoir.intitule = "AVOIR POUR LA FACTURE " + self.intitule facture_avoir.intitule = 'AVOIR POUR LA FACTURE ' + self.intitule
facture_avoir.paid = False facture_avoir.paid = False
facture_avoir.creator = creator facture_avoir.creator = creator
facture_avoir.account_on_previous_period = False facture_avoir.account_on_previous_period = False
@ -679,14 +679,14 @@ class Facture(models.Model):
periode = property(periode) periode = property(periode)
class Meta: class Meta:
ordering = ("-id",) ordering = ('-id',)
unique_together = [ unique_together = [
('contrat', 'numero_d_echeance'), ('contrat', 'numero_d_echeance'),
] ]
class Ligne(models.Model): class Ligne(models.Model):
facture = models.ForeignKey(Facture, related_name="lignes", on_delete=models.CASCADE) facture = models.ForeignKey(Facture, related_name='lignes', on_delete=models.CASCADE)
intitule = models.TextField(blank=True) intitule = models.TextField(blank=True)
prix_unitaire_ht = models.DecimalField(max_digits=8, decimal_places=2, default=Decimal('0')) prix_unitaire_ht = models.DecimalField(max_digits=8, decimal_places=2, default=Decimal('0'))
quantite = models.DecimalField(max_digits=8, decimal_places=3, default=Decimal('1.0')) quantite = models.DecimalField(max_digits=8, decimal_places=3, default=Decimal('1.0'))
@ -711,9 +711,9 @@ class Ligne(models.Model):
def clean(self): def clean(self):
errors = [] errors = []
if self.taux_tva and self.taux_tva < 0: if self.taux_tva and self.taux_tva < 0:
errors.append("Le taux de tva doit être une valeur positive ou nulle.") errors.append('Le taux de tva doit être une valeur positive ou nulle.')
if self.prix_unitaire_ht < 0: if self.prix_unitaire_ht < 0:
errors.append("Le prix unitaire hors taxe doit être une valeur positive ou nulle.") errors.append('Le prix unitaire hors taxe doit être une valeur positive ou nulle.')
if self.facture.contrat and not self.facture.proforma: if self.facture.contrat and not self.facture.proforma:
facture = self.facture facture = self.facture
contrat = facture.contrat contrat = facture.contrat
@ -728,17 +728,17 @@ class Ligne(models.Model):
raise ValidationError(errors) raise ValidationError(errors)
class Meta: class Meta:
ordering = ("order",) ordering = ('order',)
def __str__(self): def __str__(self):
if self.facture.client: if self.facture.client:
return "%s pour %s %s" % ( return '%s pour %s %s' % (
self.intitule, self.intitule,
self.montant, self.montant,
self.facture.client.monnaie, self.facture.client.monnaie,
) )
else: else:
return "%s pour %s" % (self.intitule, self.montant) return '%s pour %s' % (self.intitule, self.montant)
def encaissements_avec_solde_non_affecte(): def encaissements_avec_solde_non_affecte():
@ -775,7 +775,7 @@ class Payment(models.Model):
self.montant_affecte = min(self.ligne_banque_pop.montant, self.facture.montant_ttc) self.montant_affecte = min(self.ligne_banque_pop.montant, self.facture.montant_ttc)
except (banque_models.LigneBanquePop.DoesNotExist, Facture.DoesNotExist): except (banque_models.LigneBanquePop.DoesNotExist, Facture.DoesNotExist):
pass pass
aggregate = models.Sum("montant_affecte") aggregate = models.Sum('montant_affecte')
# from the ligne de banque pov # from the ligne de banque pov
try: try:
other_payments = self.ligne_banque_pop.payments other_payments = self.ligne_banque_pop.payments
@ -784,7 +784,7 @@ class Payment(models.Model):
except banque_models.LigneBanquePop.DoesNotExist: except banque_models.LigneBanquePop.DoesNotExist:
pass pass
else: else:
deja_affecte = other_payments.aggregate(aggregate).get("montant_affecte", 0) deja_affecte = other_payments.aggregate(aggregate).get('montant_affecte', 0)
if deja_affecte + self.montant_affecte > self.ligne_banque_pop.montant: if deja_affecte + self.montant_affecte > self.ligne_banque_pop.montant:
raise ValidationError( raise ValidationError(
'Le montant affecté aux différentes factures ' 'Le montant affecté aux différentes factures '
@ -796,7 +796,7 @@ class Payment(models.Model):
except Facture.DoesNotExist: except Facture.DoesNotExist:
pass pass
else: else:
deja_affecte = other_payments.aggregate(aggregate).get("montant_affecte") or 0 deja_affecte = other_payments.aggregate(aggregate).get('montant_affecte') or 0
if ( if (
self.montant_affecte is not None self.montant_affecte is not None
and deja_affecte + self.montant_affecte > self.facture.montant_ttc and deja_affecte + self.montant_affecte > self.facture.montant_ttc

View File

@ -156,17 +156,17 @@ class StringWithHref(str):
def client_and_link(c): def client_and_link(c):
if c is not None: if c is not None:
s = str(c) s = str(c)
url = reverse("admin:eo_facture_client_change", args=(c.id,)) url = reverse('admin:eo_facture_client_change', args=(c.id,))
return StringWithHref(s, url) return StringWithHref(s, url)
else: else:
return "" return ''
def dict_of_list(): def dict_of_list():
return defaultdict(lambda: []) return defaultdict(lambda: [])
@register.inclusion_tag("eo_facture/table.html") @register.inclusion_tag('eo_facture/table.html')
@cache @cache
def income_by_clients(year=None): def income_by_clients(year=None):
if not year: if not year:
@ -218,7 +218,7 @@ def income_by_clients(year=None):
pareto[client] = running_total pareto[client] = running_total
return dict( return dict(
title="Chiffre d'affaire prévisionnel par client pour %s" % year, title="Chiffre d'affaire prévisionnel par client pour %s" % year,
name="previsional-income-by-client-%s" % year, name='previsional-income-by-client-%s' % year,
headers=[ headers=[
('client', 'Client'), ('client', 'Client'),
('income', 'Chiffre d\'affaire'), ('income', 'Chiffre d\'affaire'),
@ -291,12 +291,12 @@ def a_facturer():
def ago(date): def ago(date):
ago = timesince(date) ago = timesince(date)
# selects only the first part of the returned string # selects only the first part of the returned string
return ago.split(",")[0] return ago.split(',')[0]
@register.filter(is_safe=True) @register.filter(is_safe=True)
def amountformat(value, use_l10n=True): def amountformat(value, use_l10n=True):
return number_format(Decimal(value).quantize(Decimal("0.01")), force_grouping=True) return number_format(Decimal(value).quantize(Decimal('0.01')), force_grouping=True)
# invalidate impayees() cache when Payment set changes # invalidate impayees() cache when Payment set changes

View File

@ -22,13 +22,13 @@ from datetime import date, datetime
from django.utils.encoding import force_text from django.utils.encoding import force_text
NS = { NS = {
"fo": "urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0", 'fo': 'urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0',
"office": "urn:oasis:names:tc:opendocument:xmlns:office:1.0", 'office': 'urn:oasis:names:tc:opendocument:xmlns:office:1.0',
"style": "urn:oasis:names:tc:opendocument:xmlns:style:1.0", 'style': 'urn:oasis:names:tc:opendocument:xmlns:style:1.0',
"number": "urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0", 'number': 'urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0',
"table": "urn:oasis:names:tc:opendocument:xmlns:table:1.0", 'table': 'urn:oasis:names:tc:opendocument:xmlns:table:1.0',
"text": "urn:oasis:names:tc:opendocument:xmlns:text:1.0", 'text': 'urn:oasis:names:tc:opendocument:xmlns:text:1.0',
"xlink": "http://www.w3.org/1999/xlink", 'xlink': 'http://www.w3.org/1999/xlink',
} }
for prefix, uri in NS.items(): for prefix, uri in NS.items():
@ -36,7 +36,7 @@ for prefix, uri in NS.items():
def is_number(value): def is_number(value):
if isinstance(value, str) and (value.startswith("0") or value.startswith("+")): if isinstance(value, str) and (value.startswith('0') or value.startswith('+')):
return False return False
try: try:
float(value) float(value)
@ -55,96 +55,96 @@ class Workbook:
return sheet return sheet
def get_content_node(self): def get_content_node(self):
root = ET.Element("{%s}document-content" % NS["office"]) root = ET.Element('{%s}document-content' % NS['office'])
ET.SubElement(root, "{%s}scripts" % NS["office"]) ET.SubElement(root, '{%s}scripts' % NS['office'])
ET.SubElement(root, "{%s}font-face-decls" % NS["office"]) ET.SubElement(root, '{%s}font-face-decls' % NS['office'])
body = ET.SubElement(root, "{%s}body" % NS["office"]) body = ET.SubElement(root, '{%s}body' % NS['office'])
spreadsheet = ET.SubElement(body, "{%s}spreadsheet" % NS["office"]) spreadsheet = ET.SubElement(body, '{%s}spreadsheet' % NS['office'])
for sheet in self.sheets: for sheet in self.sheets:
spreadsheet.append(sheet.get_node()) spreadsheet.append(sheet.get_node())
return root return root
def get_styles_node(self): def get_styles_node(self):
root = ET.Element("{%s}document-styles" % NS["office"]) root = ET.Element('{%s}document-styles' % NS['office'])
ET.SubElement(root, "{%s}font-face-decls" % NS["office"]) ET.SubElement(root, '{%s}font-face-decls' % NS['office'])
automatic_styles = ET.SubElement(root, "{%s}styles" % NS["office"]) automatic_styles = ET.SubElement(root, '{%s}styles' % NS['office'])
style = ET.SubElement(automatic_styles, "{%s}style" % NS["style"]) style = ET.SubElement(automatic_styles, '{%s}style' % NS['style'])
style.attrib["{%s}name" % NS["style"]] = "Default" style.attrib['{%s}name' % NS['style']] = 'Default'
def define_number_style(): def define_number_style():
node = ET.SubElement(automatic_styles, '{%s}number-style' % NS['number']) node = ET.SubElement(automatic_styles, '{%s}number-style' % NS['number'])
node.attrib["{%s}name" % NS["style"]] = "NumberFormat" node.attrib['{%s}name' % NS['style']] = 'NumberFormat'
sub_node = ET.SubElement(node, '{%s}number' % NS['number']) sub_node = ET.SubElement(node, '{%s}number' % NS['number'])
sub_node.attrib['{%s}decimal-places' % NS['number']] = '2' sub_node.attrib['{%s}decimal-places' % NS['number']] = '2'
sub_node.attrib['{%s}min-decimal-places' % NS['number']] = '2' sub_node.attrib['{%s}min-decimal-places' % NS['number']] = '2'
sub_node.attrib['{%s}min-integer-digits' % NS['number']] = '1' sub_node.attrib['{%s}min-integer-digits' % NS['number']] = '1'
sub_node.attrib['{%s}grouping' % NS['number']] = 'true' sub_node.attrib['{%s}grouping' % NS['number']] = 'true'
style = ET.SubElement(automatic_styles, "{%s}style" % NS["style"]) style = ET.SubElement(automatic_styles, '{%s}style' % NS['style'])
style.attrib["{%s}name" % NS["style"]] = 'Number' style.attrib['{%s}name' % NS['style']] = 'Number'
style.attrib["{%s}family" % NS["style"]] = "table-cell" style.attrib['{%s}family' % NS['style']] = 'table-cell'
style.attrib["{%s}data-style-name" % NS["style"]] = "NumberFormat" style.attrib['{%s}data-style-name' % NS['style']] = 'NumberFormat'
style.attrib["{%s}parent-style" % NS["style"]] = "Default" style.attrib['{%s}parent-style' % NS['style']] = 'Default'
define_number_style() define_number_style()
def define_date_style(name, strftime_string): def define_date_style(name, strftime_string):
node = ET.SubElement(automatic_styles, "{%s}date-style" % NS["number"]) node = ET.SubElement(automatic_styles, '{%s}date-style' % NS['number'])
node.attrib["{%s}name" % NS["style"]] = name + "NumberFormat" node.attrib['{%s}name' % NS['style']] = name + 'NumberFormat'
for part in re.findall(r"%?.", strftime_string): for part in re.findall(r'%?.', strftime_string):
if part == "%Y": if part == '%Y':
ET.SubElement(node, "{%s}year" % NS["number"]).attrib["{%s}style" % NS["number"]] = "long" ET.SubElement(node, '{%s}year' % NS['number']).attrib['{%s}style' % NS['number']] = 'long'
elif part == "%m": elif part == '%m':
ET.SubElement(node, "{%s}month" % NS["number"]).attrib[ ET.SubElement(node, '{%s}month' % NS['number']).attrib[
"{%s}style" % NS["number"] '{%s}style' % NS['number']
] = "long" ] = 'long'
elif part == "%d": elif part == '%d':
ET.SubElement(node, "{%s}day" % NS["number"]).attrib["{%s}style" % NS["number"]] = "long" ET.SubElement(node, '{%s}day' % NS['number']).attrib['{%s}style' % NS['number']] = 'long'
elif part == "%H": elif part == '%H':
ET.SubElement(node, "{%s}hours" % NS["number"]).attrib[ ET.SubElement(node, '{%s}hours' % NS['number']).attrib[
"{%s}style" % NS["number"] '{%s}style' % NS['number']
] = "long" ] = 'long'
elif part == "%M": elif part == '%M':
ET.SubElement(node, "{%s}minutes" % NS["number"]).attrib[ ET.SubElement(node, '{%s}minutes' % NS['number']).attrib[
"{%s}style" % NS["number"] '{%s}style' % NS['number']
] = "long" ] = 'long'
elif part == "%S": elif part == '%S':
ET.SubElement(node, "{%s}seconds" % NS["number"]).attrib[ ET.SubElement(node, '{%s}seconds' % NS['number']).attrib[
"{%s}style" % NS["number"] '{%s}style' % NS['number']
] = "long" ] = 'long'
else: else:
ET.SubElement(node, "{%s}text" % NS["number"]).text = part ET.SubElement(node, '{%s}text' % NS['number']).text = part
style = ET.SubElement(automatic_styles, "{%s}style" % NS["style"]) style = ET.SubElement(automatic_styles, '{%s}style' % NS['style'])
style.attrib["{%s}name" % NS["style"]] = name style.attrib['{%s}name' % NS['style']] = name
style.attrib["{%s}family" % NS["style"]] = "table-cell" style.attrib['{%s}family' % NS['style']] = 'table-cell'
style.attrib["{%s}data-style-name" % NS["style"]] = name + "NumberFormat" style.attrib['{%s}data-style-name' % NS['style']] = name + 'NumberFormat'
style.attrib["{%s}parent-style" % NS["style"]] = "Default" style.attrib['{%s}parent-style' % NS['style']] = 'Default'
style = ET.SubElement(automatic_styles, "{%s}style" % NS["style"]) style = ET.SubElement(automatic_styles, '{%s}style' % NS['style'])
style.attrib["{%s}name" % NS["style"]] = name + "Column" style.attrib['{%s}name' % NS['style']] = name + 'Column'
style.attrib["{%s}family" % NS["style"]] = "table-column" style.attrib['{%s}family' % NS['style']] = 'table-column'
ET.SubElement(style, "{%s}table-column-properties" % NS["style"]).attrib[ ET.SubElement(style, '{%s}table-column-properties' % NS['style']).attrib[
"{%s}column-width" % NS["style"] '{%s}column-width' % NS['style']
] = "80mm" ] = '80mm'
define_date_style("Date", '%d/%m/%Y') define_date_style('Date', '%d/%m/%Y')
define_date_style("DateTime", '%d/%m/%Y %H:%M') define_date_style('DateTime', '%d/%m/%Y %H:%M')
return root return root
def get_styles(self): def get_styles(self):
return ET.tostring(self.get_styles_node(), "utf-8") return ET.tostring(self.get_styles_node(), 'utf-8')
def get_content(self): def get_content(self):
return ET.tostring(self.get_content_node(), "utf-8") return ET.tostring(self.get_content_node(), 'utf-8')
def save(self, output): def save(self, output):
with zipfile.ZipFile(output, "w") as z: with zipfile.ZipFile(output, 'w') as z:
z.writestr("content.xml", self.get_content()) z.writestr('content.xml', self.get_content())
z.writestr("styles.xml", self.get_styles()) z.writestr('styles.xml', self.get_styles())
z.writestr("mimetype", "application/vnd.oasis.opendocument.spreadsheet") z.writestr('mimetype', 'application/vnd.oasis.opendocument.spreadsheet')
z.writestr( z.writestr(
"META-INF/manifest.xml", 'META-INF/manifest.xml',
"""<?xml version="1.0" encoding="UTF-8"?> """<?xml version="1.0" encoding="UTF-8"?>
<manifest:manifest xmlns:manifest="urn:oasis:names:tc:opendocument:xmlns:manifest:1.0"> <manifest:manifest xmlns:manifest="urn:oasis:names:tc:opendocument:xmlns:manifest:1.0">
<manifest:file-entry manifest:full-path="/" manifest:media-type="application/vnd.oasis.opendocument.spreadsheet"/> <manifest:file-entry manifest:full-path="/" manifest:media-type="application/vnd.oasis.opendocument.spreadsheet"/>
@ -168,15 +168,15 @@ class WorkSheet:
self.cells[row][column] = WorkCell(self, value, **kwargs) self.cells[row][column] = WorkCell(self, value, **kwargs)
def get_node(self): def get_node(self):
root = ET.Element("{%s}table" % NS["table"]) root = ET.Element('{%s}table' % NS['table'])
root.attrib["{%s}name" % NS["table"]] = self.name root.attrib['{%s}name' % NS['table']] = self.name
ET.SubElement(root, "{%s}table-column" % NS["table"]) ET.SubElement(root, '{%s}table-column' % NS['table'])
for i in range(0, max(self.cells.keys()) + 1): for i in range(0, max(self.cells.keys()) + 1):
row = ET.SubElement(root, "{%s}table-row" % NS["table"]) row = ET.SubElement(root, '{%s}table-row' % NS['table'])
for j in range(0, max(self.cells.get(i).keys()) + 1): for j in range(0, max(self.cells.get(i).keys()) + 1):
cell = self.cells.get(i, {}).get(j, None) cell = self.cells.get(i, {}).get(j, None)
if not cell: if not cell:
ET.SubElement(row, "{%s}table-cell" % NS["table"]) ET.SubElement(row, '{%s}table-cell' % NS['table'])
else: else:
row.append(cell.get_node()) row.append(cell.get_node())
return root return root
@ -185,39 +185,39 @@ class WorkSheet:
class WorkCell: class WorkCell:
def __init__(self, worksheet, value): def __init__(self, worksheet, value):
if value is None: if value is None:
value = "" value = ''
self.worksheet = worksheet self.worksheet = worksheet
self.native_value = value self.native_value = value
value = force_text(value) value = force_text(value)
for i in range(0x20): # remove control characters for i in range(0x20): # remove control characters
char = chr(i) char = chr(i)
if char in ("\t", "\r", "\n"): if char in ('\t', '\r', '\n'):
# only allow tab, carriage return and line feed. # only allow tab, carriage return and line feed.
continue continue
value = value.replace(char, "") value = value.replace(char, '')
# fffe and ffff are also invalid characters # fffe and ffff are also invalid characters
value = value.replace("\ufffe", "").replace("\uffff", "") value = value.replace('\ufffe', '').replace('\uffff', '')
self.value = value self.value = value
def get_node(self): def get_node(self):
root = ET.Element("{%s}table-cell" % NS["table"]) root = ET.Element('{%s}table-cell' % NS['table'])
p = ET.SubElement(root, "{%s}p" % NS["text"]) p = ET.SubElement(root, '{%s}p' % NS['text'])
p.text = self.value p.text = self.value
value = self.native_value value = self.native_value
if is_number(value): if is_number(value):
root.attrib["{%s}value-type" % NS["office"]] = "float" root.attrib['{%s}value-type' % NS['office']] = 'float'
root.attrib["{%s}value" % NS["office"]] = self.value root.attrib['{%s}value' % NS['office']] = self.value
root.attrib["{%s}style-name" % NS["table"]] = "Number" root.attrib['{%s}style-name' % NS['table']] = 'Number'
elif isinstance(value, datetime): elif isinstance(value, datetime):
root.attrib["{%s}style-name" % NS["table"]] = "DateTime" root.attrib['{%s}style-name' % NS['table']] = 'DateTime'
root.attrib["{%s}value-type" % NS["office"]] = "date" root.attrib['{%s}value-type' % NS['office']] = 'date'
root.attrib["{%s}date-value" % NS["office"]] = value.strftime("%Y-%m-%dT%H:%M:%S") root.attrib['{%s}date-value' % NS['office']] = value.strftime('%Y-%m-%dT%H:%M:%S')
p.text = value.strftime("%d/%m/%Y %H:%M:%S") p.text = value.strftime('%d/%m/%Y %H:%M:%S')
elif isinstance(value, date): elif isinstance(value, date):
root.attrib["{%s}style-name" % NS["table"]] = "Date" root.attrib['{%s}style-name' % NS['table']] = 'Date'
root.attrib["{%s}value-type" % NS["office"]] = "date" root.attrib['{%s}value-type' % NS['office']] = 'date'
root.attrib["{%s}date-value" % NS["office"]] = value.strftime("%Y-%m-%d") root.attrib['{%s}date-value' % NS['office']] = value.strftime('%Y-%m-%d')
p.text = value.strftime("%d/%m/%Y") p.text = value.strftime('%d/%m/%Y')
else: else:
root.attrib["{%s}value-type" % NS["office"]] = "string" root.attrib['{%s}value-type' % NS['office']] = 'string'
return root return root

View File

@ -86,11 +86,11 @@ LOGGING = {
# timezone as the operating system. # timezone as the operating system.
# If running in a Windows environment this must be set to the same as your # If running in a Windows environment this must be set to the same as your
# system time zone. # system time zone.
TIME_ZONE = "Europe/Paris" TIME_ZONE = 'Europe/Paris'
# Language code for this installation. All choices can be found here: # Language code for this installation. All choices can be found here:
# http://www.i18nguy.com/unicode/language-identifiers.html # http://www.i18nguy.com/unicode/language-identifiers.html
LANGUAGE_CODE = "fr-fr" LANGUAGE_CODE = 'fr-fr'
SITE_ID = 1 SITE_ID = 1
@ -110,28 +110,28 @@ LOCALE_PATHS = (os.path.join(BASE_DIR, 'eo_gestion', 'locale'),)
# URL that handles the media served from MEDIA_ROOT. Make sure to use a # URL that handles the media served from MEDIA_ROOT. Make sure to use a
# trailing slash if there is a path component (optional in other cases). # trailing slash if there is a path component (optional in other cases).
# Examples: "http://media.lawrence.com", "http://example.com/media/" # Examples: "http://media.lawrence.com", "http://example.com/media/"
MEDIA_URL = "/media/" MEDIA_URL = '/media/'
# URL prefix for static files. # URL prefix for static files.
# Example: "http://media.lawrence.com/static/" # Example: "http://media.lawrence.com/static/"
STATIC_URL = "/static/" STATIC_URL = '/static/'
STATICFILES_FINDERS = list(global_settings.STATICFILES_FINDERS) + ['gadjo.finders.XStaticFinder'] STATICFILES_FINDERS = list(global_settings.STATICFILES_FINDERS) + ['gadjo.finders.XStaticFinder']
# Make this unique, and don't share it with anybody. # Make this unique, and don't share it with anybody.
SECRET_KEY = "5w+ifr2ho!#x06q7dshr08wd#gt0wwp@wvbvw33kmtb+x$(9ts" SECRET_KEY = '5w+ifr2ho!#x06q7dshr08wd#gt0wwp@wvbvw33kmtb+x$(9ts'
MIDDLEWARE = ( MIDDLEWARE = (
"django.middleware.common.CommonMiddleware", 'django.middleware.common.CommonMiddleware',
"django.middleware.http.ConditionalGetMiddleware", 'django.middleware.http.ConditionalGetMiddleware',
"django.contrib.sessions.middleware.SessionMiddleware", 'django.contrib.sessions.middleware.SessionMiddleware',
"django.middleware.csrf.CsrfViewMiddleware", 'django.middleware.csrf.CsrfViewMiddleware',
"django.middleware.locale.LocaleMiddleware", 'django.middleware.locale.LocaleMiddleware',
"django.contrib.auth.middleware.AuthenticationMiddleware", 'django.contrib.auth.middleware.AuthenticationMiddleware',
"django.contrib.messages.middleware.MessageMiddleware", 'django.contrib.messages.middleware.MessageMiddleware',
) )
ROOT_URLCONF = "eo_gestion.urls" ROOT_URLCONF = 'eo_gestion.urls'
# Templates # Templates
TEMPLATES = [ TEMPLATES = [
@ -157,17 +157,17 @@ TEMPLATES = [
] ]
INSTALLED_APPS = ( INSTALLED_APPS = (
"django.contrib.auth", 'django.contrib.auth',
"django.contrib.contenttypes", 'django.contrib.contenttypes',
"django.contrib.sessions", 'django.contrib.sessions',
"django.contrib.sites", 'django.contrib.sites',
"django.contrib.messages", 'django.contrib.messages',
"django.contrib.staticfiles", 'django.contrib.staticfiles',
"django.contrib.humanize", 'django.contrib.humanize',
# Uncomment the next line to enable the admin: # Uncomment the next line to enable the admin:
"django.contrib.admin", 'django.contrib.admin',
"eo_gestion.eo_facture", 'eo_gestion.eo_facture',
"eo_gestion.eo_banque", 'eo_gestion.eo_banque',
'eo_gestion.eo_conges', 'eo_gestion.eo_conges',
'eo_gestion.chorus', 'eo_gestion.chorus',
'taggit', 'taggit',
@ -180,7 +180,7 @@ AUTHENTICATION_BACKENDS = ('django.contrib.auth.backends.ModelBackend',)
DEFAULT_AUTO_FIELD = 'django.db.models.AutoField' DEFAULT_AUTO_FIELD = 'django.db.models.AutoField'
LOGIN_REDIRECT_URL = "/" LOGIN_REDIRECT_URL = '/'
# timeout used in python-requests call, in seconds # timeout used in python-requests call, in seconds
# timeout just after web server, which is usually 30s, # timeout just after web server, which is usually 30s,
@ -207,7 +207,7 @@ WORKERS_CALENDAR_CONFIG = {
], ],
} }
local_settings_file = os.environ.get("BARBACOMPTA_SETTINGS_FILE", "local_settings.py") local_settings_file = os.environ.get('BARBACOMPTA_SETTINGS_FILE', 'local_settings.py')
if os.path.exists(local_settings_file): if os.path.exists(local_settings_file):
with open(local_settings_file) as fd: with open(local_settings_file) as fd:
exec(fd.read()) exec(fd.read())

View File

@ -162,7 +162,7 @@ class SortableAdminMixin(SortableAdminBase):
first_order_direction, first_order_field_index = self._get_first_ordering(request) first_order_direction, first_order_field_index = self._get_first_ordering(request)
if first_order_field_index == self.default_order_field_index: if first_order_field_index == self.default_order_field_index:
self.enable_sorting = True self.enable_sorting = True
self.order_by = f"{first_order_direction}{self.default_order_field}" self.order_by = f'{first_order_direction}{self.default_order_field}'
else: else:
self.enable_sorting = False self.enable_sorting = False
return super().get_changelist(request, **kwargs) return super().get_changelist(request, **kwargs)
@ -178,9 +178,9 @@ class SortableAdminMixin(SortableAdminBase):
first_order_direction = self.default_order_direction first_order_direction = self.default_order_direction
else: else:
first_order_field_index = None first_order_field_index = None
first_order_direction = "" first_order_direction = ''
for p in order_var.split("."): for p in order_var.split('.'):
none, prefix, index = p.rpartition("-") none, prefix, index = p.rpartition('-')
try: try:
index = int(index) index = int(index)
except ValueError: except ValueError:
@ -316,8 +316,8 @@ class SortableAdminMixin(SortableAdminBase):
# noinspection PyProtectedMember # noinspection PyProtectedMember
raise model.MultipleObjectsReturned( raise model.MultipleObjectsReturned(
"Detected non-unique values in field '{rank_field}' used for sorting this model.\n" "Detected non-unique values in field '{rank_field}' used for sorting this model.\n"
"Consider to run \n python manage.py reorder {model._meta.label}\n" 'Consider to run \n python manage.py reorder {model._meta.label}\n'
"to adjust this inconsistency." 'to adjust this inconsistency.'
) )
move_qs = model.objects.filter(**move_filter).order_by(order_by) move_qs = model.objects.filter(**move_filter).order_by(order_by)
@ -398,7 +398,7 @@ class SortableAdminMixin(SortableAdminBase):
queryset_size = queryset.count() queryset_size = queryset.count()
page_size = page.end_index() - page.start_index() + 1 page_size = page.end_index() - page.start_index() + 1
if queryset_size > page_size: if queryset_size > page_size:
msg = _(f"The target page size is {page_size}. It is too small for {queryset_size} items.") msg = _(f'The target page size is {page_size}. It is too small for {queryset_size} items.')
self.message_user(request, msg, level=messages.ERROR) self.message_user(request, msg, level=messages.ERROR)
return return

View File

@ -2,8 +2,8 @@
import os import os
import sys import sys
if __name__ == "__main__": if __name__ == '__main__':
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "eo_gestion.settings") os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'eo_gestion.settings')
from django.core.management import execute_from_command_line from django.core.management import execute_from_command_line

View File

@ -29,11 +29,11 @@ def test_references(app):
if i % 3 == 1: if i % 3 == 1:
contract.tags.add(gi) contract.tags.add(gi)
response = app.get("/api/references/") response = app.get('/api/references/')
assert any(row['tags'] for row in response.json['data']) assert any(row['tags'] for row in response.json['data'])
response_gru = app.get("/api/references/", params={'tags': 'GRU'}) response_gru = app.get('/api/references/', params={'tags': 'GRU'})
assert len(response_gru.json['data']) == 12 assert len(response_gru.json['data']) == 12
response_gi = app.get("/api/references/", params={'tags': 'Gestion d\'identité'}) response_gi = app.get('/api/references/', params={'tags': 'Gestion d\'identité'})
assert len(response_gi.json['data']) == 18 assert len(response_gi.json['data']) == 18
response_gru_and_gi = app.get("/api/references/", params={'tags': ['GRU', 'Gestion d\'identité']}) response_gru_and_gi = app.get('/api/references/', params={'tags': ['GRU', 'Gestion d\'identité']})
assert len(response_gru_and_gi.json['data']) == 30 assert len(response_gru_and_gi.json['data']) == 30

View File

@ -19,27 +19,27 @@ import pytest
@pytest.fixture @pytest.fixture
def homepage(app): def homepage(app):
response = app.get("/").follow() response = app.get('/').follow()
response.form.set("username", "admin") response.form.set('username', 'admin')
response.form.set("password", "admin") response.form.set('password', 'admin')
return response.form.submit().follow() return response.form.submit().follow()
def test_misc(homepage): def test_misc(homepage):
clients = homepage.click("Clients") clients = homepage.click('Clients')
str(clients) str(clients)
ajouter_un_client = homepage.click("Ajouter un client") ajouter_un_client = homepage.click('Ajouter un client')
str(ajouter_un_client) str(ajouter_un_client)
contrats = homepage.click("Contrats") contrats = homepage.click('Contrats')
contrat_2c210afd24c11596eeaf94bfb = contrats.click('2c210afd24c11596eeaf94bfb', href='change') contrat_2c210afd24c11596eeaf94bfb = contrats.click('2c210afd24c11596eeaf94bfb', href='change')
str(contrat_2c210afd24c11596eeaf94bfb) str(contrat_2c210afd24c11596eeaf94bfb)
ajouter_un_contrat = homepage.click("Ajouter un contrat") ajouter_un_contrat = homepage.click('Ajouter un contrat')
str(ajouter_un_contrat) str(ajouter_un_contrat)
compte_en_banque = homepage.click("Compte en banque") compte_en_banque = homepage.click('Compte en banque')
str(compte_en_banque) str(compte_en_banque)
factures = homepage.click("Factures") factures = homepage.click('Factures')
factures_00137 = factures.click('F20190137') factures_00137 = factures.click('F20190137')
str(factures_00137) str(factures_00137)
rapid = factures.click('Rapid') rapid = factures.click('Rapid')
@ -47,10 +47,10 @@ def test_misc(homepage):
def test_ods_export(app): def test_ods_export(app):
response = app.get("/eo_facture/facture/").follow() response = app.get('/eo_facture/facture/').follow()
response.form.set("username", "admin") response.form.set('username', 'admin')
response.form.set("password", "admin") response.form.set('password', 'admin')
factures = response.form.submit().follow() factures = response.form.submit().follow()
@ -59,9 +59,9 @@ def test_ods_export(app):
def test_odt_export(app): def test_odt_export(app):
response = app.get("/eo_facture/contrat/").follow() response = app.get('/eo_facture/contrat/').follow()
response.form.set("username", "admin") response.form.set('username', 'admin')
response.form.set("password", "admin") response.form.set('password', 'admin')
contracts = response.form.submit().follow() contracts = response.form.submit().follow()
form = contracts.forms['changelist-form'] form = contracts.forms['changelist-form']
@ -74,9 +74,9 @@ def test_odt_export(app):
def test_zip_export(app): def test_zip_export(app):
response = app.get("/eo_facture/facture/").follow() response = app.get('/eo_facture/facture/').follow()
response.form.set("username", "admin") response.form.set('username', 'admin')
response.form.set("password", "admin") response.form.set('password', 'admin')
factures = response.form.submit().follow() factures = response.form.submit().follow()
form = factures.forms['changelist-form'] form = factures.forms['changelist-form']

View File

@ -21,30 +21,30 @@ from django.core.management import call_command
User = get_user_model() User = get_user_model()
DATA = ["tests/fixture.json"] DATA = ['tests/fixture.json']
@pytest.fixture(scope="session") @pytest.fixture(scope='session')
def django_db_setup(django_db_setup, django_db_blocker, django_db_keepdb, django_db_createdb): def django_db_setup(django_db_setup, django_db_blocker, django_db_keepdb, django_db_createdb):
with django_db_blocker.unblock(): with django_db_blocker.unblock():
if not django_db_keepdb or django_db_createdb or User.objects.count() == 0: if not django_db_keepdb or django_db_createdb or User.objects.count() == 0:
for data in DATA: for data in DATA:
call_command("loaddata", data) call_command('loaddata', data)
admin, _ = User.objects.update_or_create( admin, _ = User.objects.update_or_create(
username="admin", username='admin',
defaults=dict(email="admin@example.com", is_superuser=True, is_staff=True), defaults=dict(email='admin@example.com', is_superuser=True, is_staff=True),
) )
admin.set_password("admin") admin.set_password('admin')
admin.save() admin.save()
yield yield
@pytest.fixture @pytest.fixture
def app(db, freezer): def app(db, freezer):
freezer.move_to("2019-01-01") freezer.move_to('2019-01-01')
wtm = django_webtest.WebTestMixin() wtm = django_webtest.WebTestMixin()
wtm._patch_settings() wtm._patch_settings()
try: try:
return django_webtest.DjangoTestApp(extra_environ={"HTTP_HOST": "localhost"}) return django_webtest.DjangoTestApp(extra_environ={'HTTP_HOST': 'localhost'})
finally: finally:
wtm._unpatch_settings() wtm._unpatch_settings()

View File

@ -16,7 +16,7 @@
import os import os
ALLOWED_HOSTS = ["localhost"] ALLOWED_HOSTS = ['localhost']
DATABASES = { DATABASES = {
'default': { 'default': {

View File

@ -44,8 +44,8 @@ class TestLoggedIn:
response.form['periodicite'] = 'annuelle' response.form['periodicite'] = 'annuelle'
response.form['periodicite_debut'] = '2018-12-01' response.form['periodicite_debut'] = '2018-12-01'
response = response.form.submit('_continue').follow() response = response.form.submit('_continue').follow()
response.forms["contrat_form"]['periodicite_debut'] = '2018-12-02' response.forms['contrat_form']['periodicite_debut'] = '2018-12-02'
response = response.forms["contrat_form"].submit('_continue').follow() response = response.forms['contrat_form'].submit('_continue').follow()
# Créer la facture de première échéance # Créer la facture de première échéance
response = app.get('/') response = app.get('/')

View File

@ -135,7 +135,7 @@ def test_conges_no_match_error(mocked_get_events, app, settings):
login(app) login(app)
resp = app.get('/conges/2023/01/') resp = app.get('/conges/2023/01/')
assert escape("Aucun·e coopérateurice trouvé·e pour les évènements : xxx, yyy") in resp.text assert escape('Aucun·e coopérateurice trouvé·e pour les évènements : xxx, yyy') in resp.text
assert 'le 06/01' in resp.text assert 'le 06/01' in resp.text

View File

@ -42,13 +42,13 @@ def test_limitations(db):
def test_facture_avoir(app): def test_facture_avoir(app):
response = app.get("/eo_facture/facture/").follow() response = app.get('/eo_facture/facture/').follow()
response.form.set("username", "admin") response.form.set('username', 'admin')
response.form.set("password", "admin") response.form.set('password', 'admin')
homepage = response.form.submit().follow() homepage = response.form.submit().follow()
factures_page = homepage.click("Factures") factures_page = homepage.click('Factures')
facture_0137_page = factures_page.click('F20190137') facture_0137_page = factures_page.click('F20190137')
assert 'F20190137' in facture_0137_page.html.find('div', {'class': 'breadcrumbs'}).text assert 'F20190137' in facture_0137_page.html.find('div', {'class': 'breadcrumbs'}).text
@ -57,7 +57,7 @@ def test_facture_avoir(app):
facture = [x for x in Facture.objects.all() if x.code() == 'F20190137'][0] facture = [x for x in Facture.objects.all() if x.code() == 'F20190137'][0]
assert facture.factures_avoir.count() == 0 assert facture.factures_avoir.count() == 0
assert 'Annuler' in [li.a.text for li in facture_0137_page.html.find_all('li')] assert 'Annuler' in [li.a.text for li in facture_0137_page.html.find_all('li')]
facture_avoir_page = facture_0137_page.click("Annuler") facture_avoir_page = facture_0137_page.click('Annuler')
facture_avoir_page = facture_avoir_page.follow() facture_avoir_page = facture_avoir_page.follow()
assert facture.factures_avoir.count() == 1 assert facture.factures_avoir.count() == 1
facture_avoir = facture.factures_avoir.first() facture_avoir = facture.factures_avoir.first()
@ -96,7 +96,7 @@ def test_facture_avoir(app):
assert facture_avoir.code() == 'F20190237' assert facture_avoir.code() == 'F20190237'
assert 'Imprimer' in [li.a.text for li in facture_avoir_page.html.find_all('li')] assert 'Imprimer' in [li.a.text for li in facture_avoir_page.html.find_all('li')]
factur_x_page = app.get("/eo_facture/facture/%s/view_pdf/F20190137.pdf?facturx" % facture_avoir.id) factur_x_page = app.get('/eo_facture/facture/%s/view_pdf/F20190137.pdf?facturx' % facture_avoir.id)
factur_x_pdf = factur_x_page.body factur_x_pdf = factur_x_page.body
_, factur_x_xml = facturx.get_facturx_xml_from_pdf(io.BytesIO(factur_x_pdf)) _, factur_x_xml = facturx.get_facturx_xml_from_pdf(io.BytesIO(factur_x_pdf))
root = ET.fromstring(factur_x_xml) root = ET.fromstring(factur_x_xml)
@ -174,32 +174,32 @@ def test_facture_filename(app, settings):
settings.FACTURE_DIR = tmpdir settings.FACTURE_DIR = tmpdir
facture = [x for x in Facture.objects.all() if x.code() == 'F20190137'][0] facture = [x for x in Facture.objects.all() if x.code() == 'F20190137'][0]
response = app.get("/eo_facture/facture/").follow() response = app.get('/eo_facture/facture/').follow()
response.form.set("username", "admin") response.form.set('username', 'admin')
response.form.set("password", "admin") response.form.set('password', 'admin')
homepage = response.form.submit().follow() homepage = response.form.submit().follow()
# facture F20190137 # facture F20190137
factures_page = homepage.click("Factures") factures_page = homepage.click('Factures')
facture_0137_page = factures_page.click(facture.code()) facture_0137_page = factures_page.click(facture.code())
assert ( assert (
'/view_pdf/F20190137.pdf' '/view_pdf/F20190137.pdf'
in [li.a['href'] for li in facture_0137_page.html.find_all('li') if li.text == 'Imprimer'][0] in [li.a['href'] for li in facture_0137_page.html.find_all('li') if li.text == 'Imprimer'][0]
) )
app.get("/eo_facture/facture/%s/view_pdf/" % facture.id) app.get('/eo_facture/facture/%s/view_pdf/' % facture.id)
assert os.path.exists(os.path.join(tmpdir, "F20190137-c3f42bb0d75d379.pdf")) assert os.path.exists(os.path.join(tmpdir, 'F20190137-c3f42bb0d75d379.pdf'))
# facture avoir proforma # facture avoir proforma
facture_avoir_page = facture_0137_page.click("Annuler") facture_avoir_page = facture_0137_page.click('Annuler')
facture_avoir_page = facture_avoir_page.follow() facture_avoir_page = facture_avoir_page.follow()
facture_avoir = facture.factures_avoir.first() facture_avoir = facture.factures_avoir.first()
assert ( assert (
'/view_pdf/Devis n°%s du 2019-10-09-AVOIR.pdf' % facture_avoir.id '/view_pdf/Devis n°%s du 2019-10-09-AVOIR.pdf' % facture_avoir.id
in [li.a['href'] for li in facture_avoir_page.html.find_all('li') if li.text == 'Imprimer'][0] in [li.a['href'] for li in facture_avoir_page.html.find_all('li') if li.text == 'Imprimer'][0]
) )
app.get("/eo_facture/facture/%s/view_pdf/" % facture_avoir.id) app.get('/eo_facture/facture/%s/view_pdf/' % facture_avoir.id)
assert os.path.exists( assert os.path.exists(
os.path.join(tmpdir, "Devis n°%s du 2019-10-09-c3f42bb0d75d379-AVOIR.pdf" % facture_avoir.id) os.path.join(tmpdir, 'Devis n°%s du 2019-10-09-c3f42bb0d75d379-AVOIR.pdf' % facture_avoir.id)
) )
# facture avoir # facture avoir
@ -211,8 +211,8 @@ def test_facture_filename(app, settings):
'/view_pdf/F20190237-AVOIR.pdf' '/view_pdf/F20190237-AVOIR.pdf'
in [li.a['href'] for li in facture_avoir_page.html.find_all('li') if li.text == 'Imprimer'][0] in [li.a['href'] for li in facture_avoir_page.html.find_all('li') if li.text == 'Imprimer'][0]
) )
app.get("/eo_facture/facture/%s/view_pdf/" % facture_avoir.id) app.get('/eo_facture/facture/%s/view_pdf/' % facture_avoir.id)
assert os.path.exists(os.path.join(tmpdir, "F20190237-c3f42bb0d75d379-AVOIR.pdf")) assert os.path.exists(os.path.join(tmpdir, 'F20190237-c3f42bb0d75d379-AVOIR.pdf'))
def test_facture_pdf(app): def test_facture_pdf(app):