misc: add and apply pre-commit hooks (#58764)

This commit is contained in:
Benjamin Dauvergne 2021-11-19 13:55:48 +01:00
parent 63aafcdb1f
commit b4475d710f
47 changed files with 350 additions and 283 deletions

18
.pre-commit-config.yaml Normal file
View File

@ -0,0 +1,18 @@
# See https://pre-commit.com for more information
# See https://pre-commit.com/hooks.html for more hooks
repos:
- repo: https://github.com/psf/black
rev: 20.8b1
hooks:
- id: black
args: ['--target-version', 'py37', '--skip-string-normalization', '--line-length', '110']
- repo: https://github.com/PyCQA/isort
rev: 5.7.0
hooks:
- id: isort
args: ['--profile', 'black', '--line-length', '110']
- repo: https://github.com/asottile/pyupgrade
rev: v2.20.0
hooks:
- id: pyupgrade
args: ['--keep-percent-format', '--py37-plus']

View File

@ -15,9 +15,9 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import csv
from io import BytesIO
import os
import zipfile
from io import BytesIO
from django.core.exceptions import PermissionDenied
from django.http import HttpResponse
@ -65,7 +65,7 @@ def export_references_as_fodt(modeladmin, request, queryset):
output_filename = 'references.fodt'
mimetype = 'application/vnd.oasis.opendocument.text'
t = Template(open(template, 'r').read())
t = Template(open(template).read())
export = t.render(Context(context)).encode('utf-8')
response = HttpResponse(content=export, content_type=mimetype)

View File

@ -14,17 +14,16 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import taggit.admin
from django.conf import settings
from django.contrib import admin
from django.contrib.auth import REDIRECT_FIELD_NAME
from django.contrib.auth.models import User, Group
from django.contrib.auth.admin import UserAdmin, GroupAdmin
from django.contrib.auth.admin import GroupAdmin, UserAdmin
from django.contrib.auth.models import Group, User
from django.http import HttpResponseRedirect
from django.utils.http import urlencode
from django.views.decorators.cache import never_cache
import taggit.admin
class EOGestionAdminSite(admin.AdminSite):
@never_cache
@ -32,18 +31,18 @@ class EOGestionAdminSite(admin.AdminSite):
if "mellon" in settings.INSTALLED_APPS:
next_url = request.GET.get(REDIRECT_FIELD_NAME, settings.LOGIN_REDIRECT_URL or "/")
query = urlencode({REDIRECT_FIELD_NAME: next_url})
url = "/accounts/mellon/login/?{0}".format(query)
url = f"/accounts/mellon/login/?{query}"
return HttpResponseRedirect(url)
return super(EOGestionAdminSite, self).login(request, extra_context=extra_context)
return super().login(request, extra_context=extra_context)
@never_cache
def logout(self, request, extra_context=None):
if "mellon" in settings.INSTALLED_APPS:
next_url = request.GET.get(REDIRECT_FIELD_NAME, settings.LOGIN_REDIRECT_URL or "/")
query = urlencode({REDIRECT_FIELD_NAME: next_url})
url = "/accounts/mellon/logout/?{0}".format(query)
url = f"/accounts/mellon/logout/?{query}"
return HttpResponseRedirect(url)
return super(EOGestionAdminSite, self).logout(request, extra_context=extra_context)
return super().logout(request, extra_context=extra_context)
site = EOGestionAdminSite()

View File

@ -15,6 +15,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from django.contrib import admin
import eo_gestion.admin
from . import models

View File

@ -15,14 +15,12 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import itertools
import zipfile
from xml.dom import pulldom
import xml.etree.ElementTree as ET
from django.core.files.storage import default_storage
import zipfile
from xml.dom import pulldom
import requests
from django.core.files.storage import default_storage
from . import chorus
@ -34,7 +32,7 @@ def grouper(iterable, n, fillvalue=None):
return itertools.zip_longest(*args, fillvalue=fillvalue)
class AnnuaireManager(object):
class AnnuaireManager:
STRUCTURE_UNITAIRE_TAG_NAME = 'CPPStructurePartenaireUnitaire'
def _update_annuaire(self):
@ -48,7 +46,7 @@ class AnnuaireManager(object):
structures = [struct for struct in structures if struct] # ignore None
inserts = []
updates = []
identifiers = set(structure.full_identifier for structure in structures)
identifiers = {structure.full_identifier for structure in structures}
known.update(identifiers)
known_structures = {
struct.full_identifier: struct

View File

@ -17,11 +17,10 @@
import base64
import logging
import requests
from django.conf import settings
from django.utils.encoding import force_text
import requests
from eo_gestion.decorators import cache
logger = logging.getLogger(__name__)

View File

@ -1,6 +1,7 @@
# Generated by Django 2.2.9 on 2020-02-04 09:08
from django.db import migrations, models
import eo_gestion.chorus.validators
@ -31,7 +32,9 @@ class Migration(migrations.Migration):
('email', models.EmailField(max_length=254, null=True)),
('engagement_obligatoire', models.BooleanField()),
],
options={'ordering': ('name', 'service_name'),},
options={
'ordering': ('name', 'service_name'),
},
),
migrations.CreateModel(
name='Update',
@ -46,6 +49,7 @@ class Migration(migrations.Migration):
],
),
migrations.AddIndex(
model_name='structure', index=models.Index(fields=['name', 'service_name'], name='name_idx'),
model_name='structure',
index=models.Index(fields=['name', 'service_name'], name='name_idx'),
),
]

View File

@ -20,22 +20,30 @@ from .validators import validate_siret
class Update(models.Model):
timestamp = models.DateTimeField(auto_now_add=True,)
success = models.BooleanField(default=False,)
timestamp = models.DateTimeField(
auto_now_add=True,
)
success = models.BooleanField(
default=False,
)
message = models.TextField()
class Structure(models.Model):
name = models.CharField(max_length=80)
full_identifier = models.CharField(max_length=len('29202001300010') + 64, unique=True)
siret = models.CharField(max_length=len('29202001300010'), validators=[validate_siret], db_index=True,)
siret = models.CharField(
max_length=len('29202001300010'),
validators=[validate_siret],
db_index=True,
)
service_code = models.CharField(max_length=64, default='', blank=True)
service_name = models.CharField(max_length=80, default='', blank=True)
email = models.EmailField(null=True)
engagement_obligatoire = models.BooleanField()
def __init__(self, *args, **kwargs):
super(Structure, self).__init__(*args, **kwargs)
super().__init__(*args, **kwargs)
if not self.full_identifier:
self.full_identifier = (self.siret + self.service_code)[: len('29202001300010') + 64]

View File

@ -17,11 +17,12 @@
from django.contrib import admin
from django.contrib.contenttypes.admin import GenericStackedInline
from django.db.models import Sum, F, Q
from django.db.models import F, Q, Sum
from .. import actions
from .. import admin as eo_gestion_admin
from ..eo_facture.admin import PaymentInline
from ..eo_facture.templatetags.eo_facture import amountformat
from .. import actions, admin as eo_gestion_admin
from . import models
@ -83,7 +84,7 @@ class LigneBanquePopAdmin(admin.ModelAdmin):
return True
def get_queryset(self, request):
qs = super(LigneBanquePopAdmin, self).get_queryset(request)
qs = super().get_queryset(request)
qs = qs.prefetch_related("payments")
return qs

View File

@ -8,10 +8,10 @@ from eo_gestion.eo_banque.models import LigneBanquePop
class Command(BaseCommand):
'''
Charge un fichier CSV exporté depuis notre banque en ligne Banque
Populaire
'''
"""
Charge un fichier CSV exporté depuis notre banque en ligne Banque
Populaire
"""
can_import_django_settings = True
output_transaction = True
@ -39,7 +39,7 @@ class Command(BaseCommand):
def load_one_file(self, csv_file_path):
for delimiter in [";", "\t"]:
csv_file = open(csv_file_path, 'r', encoding='latin1')
csv_file = open(csv_file_path, encoding='latin1')
csv_lines = csv.reader(csv_file, delimiter=delimiter)
first_line = next(csv_lines)
if first_line == self.HEADER:

View File

@ -7,9 +7,9 @@ from eo_gestion.eo_banque.models import SoldeBanquePop
class Command(BaseCommand):
'''
Charge un fichier CSV exporté depuis notre banque en ligne Baneque Populaire
'''
"""
Charge un fichier CSV exporté depuis notre banque en ligne Baneque Populaire
"""
can_import_django_settings = True
output_transaction = True

View File

@ -1,4 +1,4 @@
from datetime import timedelta, date, datetime
from datetime import date, datetime, timedelta
from django.core.management.base import BaseCommand
from django.db import transaction
@ -7,9 +7,9 @@ from eo_gestion.eo_banque.models import solde
class Command(BaseCommand):
'''
Charge un fichier CSV exporté depuis notre banque en ligne Banque Populaire
'''
"""
Charge un fichier CSV exporté depuis notre banque en ligne Banque Populaire
"""
can_import_django_settings = True
output_transaction = True

View File

@ -1,4 +1,4 @@
from django.db import models, migrations
from django.db import migrations, models
class Migration(migrations.Migration):
@ -57,8 +57,8 @@ class Migration(migrations.Migration):
),
migrations.AlterUniqueTogether(
name='lignebanquepop',
unique_together=set(
[('compte', 'date_comptabilisation', 'date_operation', 'libelle', 'reference', 'montant')]
),
unique_together={
('compte', 'date_comptabilisation', 'date_operation', 'libelle', 'reference', 'montant')
},
),
]

View File

@ -16,9 +16,9 @@
from datetime import date
from django.db import models
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
from django.db import models
from django.utils import six
@ -44,7 +44,6 @@ def solde(t=None):
return m
@six.python_2_unicode_compatible
class Commentaire(models.Model):
contenu = models.TextField()
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
@ -56,11 +55,10 @@ class Commentaire(models.Model):
return "Commentaire créé le %s" % self.creation
@six.python_2_unicode_compatible
class LigneBanquePop(models.Model):
'''
Une ligne de notre relevé de compte Banque Populaire
'''
"""
Une ligne de notre relevé de compte Banque Populaire
"""
compte = models.CharField(max_length=20)
date_comptabilisation = models.DateField()
@ -79,13 +77,13 @@ class LigneBanquePop(models.Model):
return "%(date_valeur)s %(libelle)s %(montant)s" % self.__dict__
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())
class SoldeBanquePop(models.Model):
'''
Le solde à un temps T, permet de calculer notre solde à toute date
'''
"""
Le solde à un temps T, permet de calculer notre solde à toute date
"""
compte = models.CharField(max_length=20)
date = models.DateField(auto_now_add=True)

View File

@ -21,8 +21,8 @@ from django import template
from django.db.models import Sum
from django.urls import reverse
from ..models import LigneBanquePop, solde
from ...decorators import cache
from ..models import LigneBanquePop, solde
register = template.Library()
@ -84,8 +84,8 @@ def finances(month=3):
def total(context):
qs = context["cl"].get_queryset(context["request"])
ls = [ligne[0] for ligne in qs.values_list("montant")]
credit = sum([montant for montant in ls if montant > 0])
debit = 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)
total = sum(ls)
return {'credit': credit, 'debit': debit, 'total': total}

View File

@ -17,35 +17,35 @@
import datetime as dt
from django.contrib import admin
import django.http as http
from adminsortable2.admin import SortableInlineAdminMixin
from django.conf.urls import url
from django.contrib import admin
from django.contrib.admin.options import BaseModelAdmin
from django.contrib.humanize.templatetags.humanize import ordinal
from django.db.models import TextField
from django.forms import Textarea
from django.forms.models import BaseInlineFormSet
from django.shortcuts import render
from django.urls import reverse
from django.contrib.admin.options import BaseModelAdmin
from django.db.models import TextField
import django.http as http
from django.contrib.humanize.templatetags.humanize import ordinal
from django.forms.models import BaseInlineFormSet
from django.forms import Textarea
from django.utils.html import format_html
from django.utils.six import BytesIO
from adminsortable2.admin import SortableInlineAdminMixin
from .. import ods, actions
from . import forms, models, views
from .templatetags.eo_facture import amountformat
import eo_gestion.admin
from .. import actions, ods
from . import forms, models, views
from .templatetags.eo_facture import amountformat
class LookupAllowed(object):
class LookupAllowed:
def lookup_allowed(self, *args, **kwargs):
return True
class SelectRelatedMixin(object):
class SelectRelatedMixin:
def get_queryset(self, request):
qs = super(SelectRelatedMixin, self).get_queryset(request)
qs = super().get_queryset(request)
return qs.select_related()
@ -55,7 +55,7 @@ class CommonPaymentInline(BaseModelAdmin):
kwargs['queryset'] = models.Facture.objects.avec_solde()
if db_field.name == 'ligne_banque_pop' and request.path.endswith('/add/'):
kwargs['queryset'] = models.encaissements_avec_solde_non_affecte()
return super(CommonPaymentInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
return super().formfield_for_foreignkey(db_field, request, **kwargs)
class PrestationInline(SelectRelatedMixin, admin.TabularInline):
@ -70,7 +70,7 @@ class FactureInline(SelectRelatedMixin, admin.TabularInline):
class PaymentInlineFormset(BaseInlineFormSet):
def __init__(self, *args, **kwargs):
super(PaymentInlineFormset, self).__init__(*args, **kwargs)
super().__init__(*args, **kwargs)
for form in self.forms:
if form.instance.id is None:
field = form.fields["ligne_banque_pop"]
@ -135,7 +135,7 @@ class ClientAdmin(admin.ModelAdmin):
raw_id_fields = ['chorus_structure']
def get_readonly_fields(self, request, obj=None):
readonly_fields = super(ClientAdmin, self).get_readonly_fields(request, obj=obj)
readonly_fields = super().get_readonly_fields(request, obj=obj)
if obj and obj.chorus_structure:
readonly_fields += ('siret', 'service_code')
return readonly_fields
@ -186,7 +186,7 @@ class ContratAdmin(LookupAllowed, admin.ModelAdmin):
column_montant.short_description = 'Montant'
def get_queryset(self, request):
qs = super(ContratAdmin, self).get_queryset(request)
qs = super().get_queryset(request)
return qs.prefetch_related('prestations', 'factures__lignes', 'tags')
def tag_list(self, obj):
@ -223,7 +223,7 @@ class ContratAdmin(LookupAllowed, admin.ModelAdmin):
return render(request, "admin/eo_facture/contrat/duplicate.html", context=context)
def get_urls(self):
urls = super(ContratAdmin, self).get_urls()
urls = super().get_urls()
duplicate_view = self.admin_site.admin_view(self.duplicate)
my_urls = [url(r'^(.+)/duplicate/$', duplicate_view, name='eo_facture_contrat_duplicate')]
return my_urls + urls
@ -295,7 +295,7 @@ class FactureAdmin(LookupAllowed, admin.ModelAdmin):
def get_queryset(self, request):
from django.db import connection
qs = super(FactureAdmin, self).get_queryset(request)
qs = super().get_queryset(request)
qs = qs.prefetch_related("lignes__facture__client")
qs = qs.prefetch_related("payments__ligne_banque_pop")
qs = qs.prefetch_related("contrat__factures")
@ -359,7 +359,7 @@ class FactureAdmin(LookupAllowed, admin.ModelAdmin):
return response
def get_urls(self):
urls = super(FactureAdmin, self).get_urls()
urls = super().get_urls()
my_urls = [
url(r'^view/([^/]*)', views.facture),
url(
@ -378,7 +378,11 @@ class FactureAdmin(LookupAllowed, admin.ModelAdmin):
self.admin_site.admin_view(views.send_to_chorus),
name='eo_facture_facture_send_to_chorus',
),
url(r"^sheet/$", self.admin_site.admin_view(self.sheet), name="eo_facture_facture_sheet",),
url(
r"^sheet/$",
self.admin_site.admin_view(self.sheet),
name="eo_facture_facture_sheet",
),
]
return my_urls + urls

View File

@ -19,9 +19,8 @@ import os
import subprocess
import tempfile
from django.template.loader import render_to_string
import facturx
from django.template.loader import render_to_string
logger = logging.getLogger(__name__)
@ -50,9 +49,7 @@ def to_pdfa(pdf_bytes: bytes, icc_profile: str = DEFAULT_ICC_PROFILE):
input_fd.name,
]
logger.debug('converting to PDF/A, calling %s', args)
completed = subprocess.run(
args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True, check=False
)
completed = subprocess.run(args, capture_output=True, text=True, check=False)
logger.debug('ghostscript result %s', completed)
if completed.returncode != 0:
logger.error('ghostcript call failed %s', completed)

View File

@ -17,17 +17,17 @@
from decimal import Decimal, InvalidOperation
from django.core.exceptions import ValidationError
from django.core import validators
from django.db import models
from django import forms
from django.utils.translation import ugettext_lazy as _
from django.core import validators
from django.core.exceptions import ValidationError
from django.db import models
from django.utils.six import text_type
from django.utils.translation import ugettext_lazy as _
class PercentagePerYear(list):
def __init__(self, sequence):
super(PercentagePerYear, self).__init__(sequence)
super().__init__(sequence)
def __str__(self):
return ','.join(map(lambda p: ':'.join(map(str, [p[0], int(100 * p[1])])), self))
@ -37,7 +37,7 @@ class EuroField(models.DecimalField):
def __init__(self, *args, **kwargs):
kwargs["max_digits"] = 8
kwargs["decimal_places"] = 2
super(EuroField, self).__init__(*args, **kwargs)
super().__init__(*args, **kwargs)
def assertion_error_to_validation_error(fun):
@ -105,7 +105,7 @@ class PercentagePerYearField(models.Field):
def __init__(self, *args, **kwargs):
kwargs["max_length"] = 64
super(PercentagePerYearField, self).__init__(*args, **kwargs)
super().__init__(*args, **kwargs)
def from_db_value(self, value, expression, connection, context):
return self.to_python(value)
@ -143,4 +143,4 @@ class PercentagePerYearField(models.Field):
def formfield(self, **kwargs):
defaults = {"form_class": PercentagePerYearFormField}
defaults.update(kwargs)
return super(PercentagePerYearField, self).formfield(**kwargs)
return super().formfield(**kwargs)

View File

@ -19,9 +19,9 @@ import datetime
from decimal import Decimal
from django import forms
from django.db.transaction import atomic
from django.core.exceptions import ValidationError
from django.contrib.admin import widgets
from django.core.exceptions import ValidationError
from django.db.transaction import atomic
from . import models
@ -37,7 +37,7 @@ class RapidFactureForm(forms.Form):
def __init__(self, request=None, *args, **kwargs):
self.request = request
super(RapidFactureForm, self).__init__(*args, **kwargs)
super().__init__(*args, **kwargs)
@atomic
def clean(self):
@ -89,7 +89,7 @@ class DuplicateContractForm(forms.Form):
def __init__(self, request=None, *args, **kwargs):
self.request = request
super(DuplicateContractForm, self).__init__(*args, **kwargs)
super().__init__(*args, **kwargs)
@atomic
def clean(self):

View File

@ -1,10 +1,12 @@
from django.db import models, migrations
import datetime
import eo_gestion.eo_facture.models
import eo_gestion.eo_facture.fields
from decimal import Decimal
from django.conf import settings
import django.core.validators
from django.conf import settings
from django.db import migrations, models
import eo_gestion.eo_facture.fields
import eo_gestion.eo_facture.models
class Migration(migrations.Migration):
@ -46,11 +48,16 @@ class Migration(migrations.Migration):
(
'tva',
models.DecimalField(
default=Decimal('20'), verbose_name='TVA par défaut', max_digits=8, decimal_places=2,
default=Decimal('20'),
verbose_name='TVA par défaut',
max_digits=8,
decimal_places=2,
),
),
],
options={'ordering': ('nom',),},
options={
'ordering': ('nom',),
},
bases=(models.Model,),
),
migrations.CreateModel(
@ -87,7 +94,9 @@ class Migration(migrations.Migration):
),
),
],
options={'ordering': ('-id',),},
options={
'ordering': ('-id',),
},
bases=(models.Model,),
),
migrations.CreateModel(
@ -168,7 +177,9 @@ class Migration(migrations.Migration):
),
),
],
options={'ordering': ('-id',),},
options={
'ordering': ('-id',),
},
bases=(models.Model,),
),
migrations.CreateModel(
@ -193,7 +204,9 @@ class Migration(migrations.Migration):
),
),
],
options={'ordering': ('order',),},
options={
'ordering': ('order',),
},
bases=(models.Model,),
),
migrations.CreateModel(
@ -230,7 +243,10 @@ class Migration(migrations.Migration):
),
),
],
options={'ordering': ('-ligne_banque_pop__date_valeur',), 'verbose_name': 'Paiement',},
options={
'ordering': ('-ligne_banque_pop__date_valeur',),
'verbose_name': 'Paiement',
},
bases=(models.Model,),
),
migrations.CreateModel(
@ -251,7 +267,9 @@ class Migration(migrations.Migration):
),
),
],
options={'ordering': ('contrat', 'id'),},
options={
'ordering': ('contrat', 'id'),
},
bases=(models.Model,),
),
]

View File

@ -1,4 +1,4 @@
from django.db import models, migrations
from django.db import migrations, models
class Migration(migrations.Migration):

View File

@ -11,6 +11,8 @@ class Migration(migrations.Migration):
operations = [
migrations.AddField(
model_name='client', name='contacts', field=models.TextField(blank=True, verbose_name='Contacts'),
model_name='client',
name='contacts',
field=models.TextField(blank=True, verbose_name='Contacts'),
),
]

View File

@ -16,6 +16,7 @@
from decimal import Decimal
from django.db import migrations, models

View File

@ -1,6 +1,7 @@
# Generated by Django 2.2.9 on 2020-02-01 14:59
from django.db import migrations, models
import eo_gestion.eo_facture.validators

View File

@ -1,7 +1,7 @@
# Generated by Django 2.2.9 on 2020-02-01 15:00
from django.db import migrations, models
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):

View File

@ -11,6 +11,8 @@ class Migration(migrations.Migration):
operations = [
migrations.AddField(
model_name='contrat', name='url', field=models.URLField(blank=True, verbose_name='URL'),
model_name='contrat',
name='url',
field=models.URLField(blank=True, verbose_name='URL'),
),
]

View File

@ -11,6 +11,8 @@ class Migration(migrations.Migration):
operations = [
migrations.AddField(
model_name='client', name='active', field=models.BooleanField(default=True, verbose_name='Actif'),
model_name='client',
name='active',
field=models.BooleanField(default=True, verbose_name='Actif'),
),
]

View File

@ -1,6 +1,7 @@
# Generated by Django 2.2.9 on 2020-07-04 14:55
from django.db import migrations
import eo_gestion.eo_facture.taggit

View File

@ -15,29 +15,28 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from decimal import Decimal, ROUND_HALF_UP
import datetime
from collections import defaultdict
from decimal import ROUND_HALF_UP, Decimal
from django.conf import settings
from django.contrib.auth.models import User
from django.core.exceptions import ValidationError
from django.core.validators import RegexValidator, validate_email
from django.db import models
from django.db.models import Sum, Q, F
from django.core.validators import validate_email, RegexValidator
from django.conf import settings
from django.db.models.signals import post_save, post_delete
from django.db.models import F, Q, Sum
from django.db.models.query import QuerySet
from django.db.models.signals import post_delete, post_save
from django.template.loader import get_template
from django.utils.translation import ugettext_lazy as _
from django.utils import six
from django.utils.timezone import now
from django.utils.translation import ugettext_lazy as _
from weasyprint import HTML
from eo_gestion.utils import percentage_str
from ..eo_banque import models as banque_models
from . import fields, validators, facturx, taggit
from eo_gestion.utils import percentage_str
from . import facturx, fields, taggit, validators
validate_telephone = RegexValidator(r"[. 0-9]*")
@ -52,7 +51,6 @@ def today():
return now().date()
@six.python_2_unicode_compatible
class Client(models.Model):
nom = models.CharField(max_length=255, unique=True)
adresse = models.TextField()
@ -73,7 +71,12 @@ class Client(models.Model):
blank=True,
default='',
)
service_code = models.CharField(max_length=128, db_index=True, blank=True, default='',)
service_code = models.CharField(
max_length=128,
db_index=True,
blank=True,
default='',
)
active = models.BooleanField(verbose_name='Actif', default=True)
chorus_structure = models.ForeignKey(
@ -101,7 +104,6 @@ def one_hundred_percent_this_year():
return "%s:100" % now().date().year
@six.python_2_unicode_compatible
class Contrat(models.Model):
client = models.ForeignKey(Client, related_name="contrats", on_delete=models.CASCADE)
intitule = models.CharField(max_length=150)
@ -123,7 +125,7 @@ class Contrat(models.Model):
tags = taggit.TaggableManager(blank=True)
def montant_facture(self):
return sum([facture.montant for facture in self.factures.non_proforma()])
return sum(facture.montant for facture in self.factures.non_proforma())
def pourcentage_facture(self):
return percentage_str(self.montant_facture(), self.montant())
@ -132,11 +134,11 @@ class Contrat(models.Model):
def montant(self):
"""
Montant total d'un contrat, y compris les prestations
optionnelles. Si pas de prestation définie, montant des factures émises.
Montant total d'un contrat, y compris les prestations
optionnelles. Si pas de prestation définie, montant des factures émises.
"""
if self.prestations.exists():
return Decimal(sum([p.montant() for p in self.prestations.all()]))
return Decimal(sum(p.montant() for p in self.prestations.all()))
else:
return self.montant_facture()
@ -145,7 +147,7 @@ class Contrat(models.Model):
def nom_client(self):
"""
Le nom du client qui a signé ce contrat avec EO.
Le nom du client qui a signé ce contrat avec EO.
"""
return self.client.nom
@ -159,7 +161,6 @@ class Contrat(models.Model):
ordering = ('-id',)
@six.python_2_unicode_compatible
class Prestation(models.Model):
contrat = models.ForeignKey(Contrat, related_name='prestations', on_delete=models.CASCADE)
intitule = models.CharField(max_length=255, verbose_name='Intitulé')
@ -213,7 +214,6 @@ class FactureQuerySet(QuerySet):
)
@six.python_2_unicode_compatible
class Facture(models.Model):
proforma = models.BooleanField(default=True, verbose_name='Facture proforma', db_index=True)
ordre = models.IntegerField(
@ -274,7 +274,7 @@ class Facture(models.Model):
def save(self):
if self.ordre is None and not self.proforma:
self.ordre = self.last_ordre_plus_one()
super(Facture, self).save()
super().save()
def clean(self):
if not (self.contrat or self.client):
@ -333,7 +333,7 @@ class Facture(models.Model):
@property
def montant(self):
return sum([ligne.montant for ligne in self.lignes.all()])
return sum(ligne.montant for ligne in self.lignes.all())
def solde(self):
payments = self.payments.all()
@ -395,7 +395,6 @@ class Facture(models.Model):
ordering = ("-id",)
@six.python_2_unicode_compatible
class Ligne(models.Model):
facture = models.ForeignKey(Facture, related_name="lignes", on_delete=models.CASCADE)
intitule = models.TextField(blank=True)
@ -428,8 +427,8 @@ class Ligne(models.Model):
if self.facture.contrat and not self.facture.proforma:
facture = self.facture
contrat = facture.contrat
deja_facture = sum([f.montant for f in contrat.factures.exclude(id=facture.id)])
deja_facture += sum([l.montant for l in facture.lignes.all() if l != self])
deja_facture = sum(f.montant for f in contrat.factures.exclude(id=facture.id))
deja_facture += sum(l.montant for l in facture.lignes.all() if l != self)
if deja_facture + self.montant > contrat.montant():
errors.append(
'Cette ligne fait dépasser le montant initial du contrat de %.2f %s'
@ -442,7 +441,11 @@ class Ligne(models.Model):
ordering = ("order",)
def __str__(self):
return "%s pour %s %s" % (self.intitule, self.montant, self.facture.client.monnaie,)
return "%s pour %s %s" % (
self.intitule,
self.montant,
self.facture.client.monnaie,
)
def encaissements_avec_solde_non_affecte():
@ -455,7 +458,6 @@ def encaissements_avec_solde_non_affecte():
)
@six.python_2_unicode_compatible
class Payment(models.Model):
facture = models.ForeignKey(Facture, related_name='payments', on_delete=models.CASCADE)
ligne_banque_pop = models.ForeignKey(
@ -502,7 +504,10 @@ class Payment(models.Model):
pass
else:
deja_affecte = other_payments.aggregate(aggregate).get("montant_affecte") or 0
if self.montant_affecte is not None and deja_affecte + self.montant_affecte > self.facture.montant_ttc:
if (
self.montant_affecte is not None
and deja_affecte + self.montant_affecte > self.facture.montant_ttc
):
raise ValidationError(
'Le montant affecté aux différentes factures '
'est supérieur au montant de l\'encaissement.'

View File

@ -15,7 +15,6 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from django.forms import ModelMultipleChoiceField, SelectMultiple
from taggit.managers import TaggableManager
from taggit.models import Tag
@ -25,6 +24,7 @@ class TagWidget(SelectMultiple):
class Media:
'''Enable use of Select2 in template'''
js = [
'xstatic/jquery.js',
'xstatic/jquery-ui.js',
@ -42,9 +42,7 @@ class TagField(ModelMultipleChoiceField):
if value is None:
return value
# TagManager returns Trough model and not the target model, we must adapt
if (hasattr(value, '__iter__')
and not isinstance(value, str)
and not hasattr(value, '_meta')):
if hasattr(value, '__iter__') and not isinstance(value, str) and not hasattr(value, '_meta'):
if value and hasattr(value[0], '_meta'):
value = list(value.select_related('tag').values_list('tag__name', flat=True))
return value

View File

@ -15,21 +15,21 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from datetime import date, timedelta, datetime
from decimal import Decimal, InvalidOperation
from collections import defaultdict
from datetime import date, datetime, timedelta
from decimal import Decimal, InvalidOperation
from django import template
from django.dispatch import receiver
from django.db import transaction
from django.db.models.signals import post_save, post_delete
from django.utils.formats import number_format
from django.utils.timesince import timesince
from django.utils.six import text_type
from django.db.models.signals import post_delete, post_save
from django.dispatch import receiver
from django.urls import reverse
from django.utils.formats import number_format
from django.utils.six import text_type
from django.utils.timesince import timesince
from eo_gestion.eo_facture.models import Contrat, Facture, DELAI_PAIEMENT, Payment
from eo_gestion.eo_banque.models import LigneBanquePop
from eo_gestion.eo_facture.models import DELAI_PAIEMENT, Contrat, Facture, Payment
from eo_gestion.utils import percentage
from ...decorators import cache
@ -107,7 +107,8 @@ def income():
invoiced_clients_by_year = {}
for year in invoiced_by_year_and_client:
clients = sorted(
invoiced_by_year_and_client[year].keys(), key=lambda x: -invoiced_by_year_and_client[year][x],
invoiced_by_year_and_client[year].keys(),
key=lambda x: -invoiced_by_year_and_client[year][x],
)
invoiced_clients_by_year[year] = clients
contracted_by_year = defaultdict(lambda: Decimal(0))
@ -153,7 +154,7 @@ class StringWithHref(text_type):
return text_type.__new__(cls, v)
def __init__(self, v, href=None):
super(StringWithHref, self).__init__()
super().__init__()
self.href = href
@ -213,7 +214,7 @@ def income_by_clients(year=None):
total = sum(total_by_clients.values())
total_invoiced = sum(invoiced_by_clients.values())
total_contracted = sum(contracted_by_clients.values())
percent_by_clients = dict([(i, Decimal(100) * v / total) for i, v in total_by_clients.items()])
percent_by_clients = {i: Decimal(100) * v / total for i, v in total_by_clients.items()}
clients = sorted(total_by_clients.keys(), key=lambda x: -total_by_clients[x])
running_total = Decimal(0)
# compute pareto index
@ -290,7 +291,7 @@ def a_facturer():
}
)
contrats_a_facturer.sort(key=lambda x: -x['depuis'])
montant = sum([x['montant'] for x in contrats_a_facturer])
montant = sum(x['montant'] for x in contrats_a_facturer)
return {'a_facturer': contrats_a_facturer, 'montant': montant}

View File

@ -20,13 +20,14 @@ import logging
import os.path
from django import http
from django.shortcuts import get_object_or_404, redirect
from django.conf import settings
from django.contrib import messages
from django.contrib.admin.models import LogEntry
from django.contrib.contenttypes.models import ContentType
from django.shortcuts import get_object_or_404, redirect
from eo_gestion.chorus.chorus import push_to_chorus
from .models import Contrat, Facture
logger = logging.getLogger(__name__)
@ -46,7 +47,8 @@ def facture_pdf(request, facture_id):
pdf = facture.pdf(base_uri=request.build_absolute_uri('/'))
if hasattr(settings, 'FACTURE_DIR'):
filename = os.path.join(
settings.FACTURE_DIR, '%s-%s.pdf' % (facture.code(), facture.contrat.client.nom.encode('utf8')),
settings.FACTURE_DIR,
'%s-%s.pdf' % (facture.code(), facture.contrat.client.nom.encode('utf8')),
)
with open(filename, 'wb') as fd:
fd.write(pdf)
@ -90,11 +92,11 @@ def send_to_chorus(request, facture_id):
description = ''
if 'numeroFluxDepot' in response:
msg = 'Facture {facture} envoyée à ChorusPro'.format(facture=facture.code())
msg = f'Facture {facture.code()} envoyée à ChorusPro'
for key, value in response.items():
description += ' | {key} - {value}'.format(key=key, value=value)
description += f' | {key} - {value}'
else:
msg = 'Échec d\'envoi de la facture {facture} à ChorusPro'.format(facture=facture.code())
msg = f'Échec d\'envoi de la facture {facture.code()} à ChorusPro'
msg += description
LogEntry.objects.create(

View File

@ -18,10 +18,8 @@
import logging
import django
from uwsgidecorators import timer
django.setup()
logger = logging.getLogger('django.server')

View File

@ -14,14 +14,13 @@
# You should have received a copy of the GNU General Public License
# along with this program; if not, see <http://www.gnu.org/licenses/>.
from datetime import date, datetime
import re
import xml.etree.ElementTree as ET
import zipfile
from datetime import date, datetime
from django.utils.encoding import force_text
NS = {
"fo": "urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0",
"office": "urn:oasis:names:tc:opendocument:xmlns:office:1.0",
@ -46,7 +45,7 @@ def is_number(value):
return True
class Workbook(object):
class Workbook:
def __init__(self):
self.sheets = []
@ -158,7 +157,7 @@ class Workbook(object):
z.close()
class WorkSheet(object):
class WorkSheet:
def __init__(self, workbook, name):
self.cells = {}
self.name = name
@ -184,7 +183,7 @@ class WorkSheet(object):
return root
class WorkCell(object):
class WorkCell:
def __init__(self, worksheet, value):
if value is None:
value = ""

View File

@ -16,9 +16,8 @@
import os.path
from django.conf import global_settings
import facturx
from django.conf import global_settings
# Django settings for facturation project.
@ -40,7 +39,12 @@ DATABASES = {
}
}
CACHES = {'default': {'BACKEND': 'django.core.cache.backends.db.DatabaseCache', 'LOCATION': 'cache',}}
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.db.DatabaseCache',
'LOCATION': 'cache',
}
}
LOGGING = {
'version': 1,
@ -51,10 +55,23 @@ LOGGING = {
'datefmt': '%Y-%m-%d %a %H:%M:%S',
},
},
'handlers': {'console': {'level': 'DEBUG', 'class': 'logging.StreamHandler', 'formatter': 'verbose',}},
'handlers': {
'console': {
'level': 'DEBUG',
'class': 'logging.StreamHandler',
'formatter': 'verbose',
}
},
'loggers': {
'': {'handlers': ['console'], 'level': 'INFO',},
'factur-x': {'level': 'WARNING', 'handlers': [], 'propagate': False,},
'': {
'handlers': ['console'],
'level': 'INFO',
},
'factur-x': {
'level': 'WARNING',
'handlers': [],
'propagate': False,
},
},
}
@ -116,7 +133,9 @@ ROOT_URLCONF = "eo_gestion.urls"
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(os.path.dirname(__file__), 'templates'),],
'DIRS': [
os.path.join(os.path.dirname(__file__), 'templates'),
],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [

View File

@ -15,14 +15,12 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from django.conf.urls import include, url
from django.conf import settings
import django.contrib.admin
from django.conf import settings
from django.conf.urls import include, url
from django.views.generic.base import RedirectView
from . import admin
from .eo_facture.views import api_references
django.contrib.admin.autodiscover()

View File

@ -1,5 +1,5 @@
import os
import json
import os
from itertools import chain
from types import MethodType
@ -19,13 +19,10 @@ from django.db.models.functions import Coalesce
from django.db.models.signals import post_save, pre_save
from django.forms import widgets
from django.forms.models import BaseInlineFormSet
from django.http import (
HttpResponse, HttpResponseBadRequest,
HttpResponseNotAllowed, HttpResponseForbidden
)
from django.http import HttpResponse, HttpResponseBadRequest, HttpResponseForbidden, HttpResponseNotAllowed
from django.urls import path, reverse
from django.utils.safestring import mark_safe
from django.utils.translation import gettext_lazy as _
from django.urls import path, reverse
__all__ = ['SortableAdminMixin', 'SortableInlineAdminMixin']
@ -55,12 +52,10 @@ class MovePageActionForm(admin.helpers.ActionForm):
required=False,
initial=1,
widget=widgets.NumberInput(attrs={'id': 'changelist-form-step'}),
label=False
label=False,
)
page = forms.IntegerField(
required=False,
widget=widgets.NumberInput(attrs={'id': 'changelist-form-page'}),
label=False
required=False, widget=widgets.NumberInput(attrs={'id': 'changelist-form-page'}), label=False
)
@ -91,7 +86,7 @@ class SortableAdminMixin(SortableAdminBase):
return [
os.path.join('adminsortable2', app_label, opts.model_name, 'change_list.html'),
os.path.join('adminsortable2', app_label, 'change_list.html'),
'adminsortable2/change_list.html'
'adminsortable2/change_list.html',
]
def __init__(self, model, admin_site):
@ -111,31 +106,22 @@ class SortableAdminMixin(SortableAdminBase):
# Insert the magic field into the same position as the first occurrence
# of the default_order_field, or, if not present, at the start
try:
self.default_order_field_index = self.list_display.index(
self.default_order_field
)
self.default_order_field_index = self.list_display.index(self.default_order_field)
except ValueError:
self.default_order_field_index = 0
self.list_display.insert(self.default_order_field_index, '_reorder')
# Remove *all* occurrences of the field from `list_display`
if self.list_display and self.default_order_field in self.list_display:
self.list_display = [
f for f in self.list_display if f != self.default_order_field
]
self.list_display = [f for f in self.list_display if f != self.default_order_field]
# Remove *all* occurrences of the field from `list_display_links`
if self.list_display_links and self.default_order_field in self.list_display_links:
self.list_display_links = [
f for f in self.list_display_links if
f != self.default_order_field
]
self.list_display_links = [f for f in self.list_display_links if f != self.default_order_field]
# Remove *all* occurrences of the field from `ordering`
if self.ordering and self.default_order_field in self.ordering:
self.ordering = [
f for f in self.ordering if f != self.default_order_field
]
self.ordering = [f for f in self.ordering if f != self.default_order_field]
rev_field = '-' + self.default_order_field
if self.ordering and rev_field in self.ordering:
self.ordering = [f for f in self.ordering if f != rev_field]
@ -148,7 +134,7 @@ class SortableAdminMixin(SortableAdminBase):
path(
'adminsortable2_update/',
self.admin_site.admin_view(self.update_order),
name=self._get_update_url_name()
name=self._get_update_url_name(),
),
]
return my_urls + super().get_urls()
@ -209,10 +195,12 @@ class SortableAdminMixin(SortableAdminBase):
def media(self):
m = super().media
if self.enable_sorting:
m = m + widgets.Media(js=[
'adminsortable2/js/libs/jquery.ui.sortable-1.11.4.js',
'adminsortable2/js/list-sortable.js',
])
m = m + widgets.Media(
js=[
'adminsortable2/js/libs/jquery.ui.sortable-1.11.4.js',
'adminsortable2/js/list-sortable.js',
]
)
return m
def _add_reorder_method(self):
@ -223,11 +211,13 @@ class SortableAdminMixin(SortableAdminBase):
This can only be done using a function, since it is not possible
to add dynamic attributes to bound methods.
"""
def func(this, item):
html = ''
if this.enable_sorting:
html = '<div class="drag js-reorder-{1}" order="{0}">' \
'&nbsp;</div>'.format(getattr(item, this.default_order_field), item.pk)
html = '<div class="drag js-reorder-{1}" order="{0}">' '&nbsp;</div>'.format(
getattr(item, this.default_order_field), item.pk
)
return mark_safe(html)
setattr(func, 'allow_tags', True)
@ -255,36 +245,37 @@ class SortableAdminMixin(SortableAdminBase):
endorder = int(request.POST.get('endorder', 0))
moved_items = list(self._move_item(request, startorder, endorder))
return HttpResponse(
json.dumps(moved_items, cls=DjangoJSONEncoder),
content_type='application/json;charset=UTF-8'
json.dumps(moved_items, cls=DjangoJSONEncoder), content_type='application/json;charset=UTF-8'
)
def save_model(self, request, obj, form, change):
if not change:
setattr(
obj, self.default_order_field,
self.get_max_order(request, obj) + 1
)
setattr(obj, self.default_order_field, self.get_max_order(request, obj) + 1)
super().save_model(request, obj, form, change)
def move_to_exact_page(self, request, queryset):
self._bulk_move(request, queryset, self.EXACT)
move_to_exact_page.short_description = _('Move selected to specific page')
def move_to_back_page(self, request, queryset):
self._bulk_move(request, queryset, self.BACK)
move_to_back_page.short_description = _('Move selected ... pages back')
def move_to_forward_page(self, request, queryset):
self._bulk_move(request, queryset, self.FORWARD)
move_to_forward_page.short_description = _('Move selected ... pages forward')
def move_to_first_page(self, request, queryset):
self._bulk_move(request, queryset, self.FIRST)
move_to_first_page.short_description = _('Move selected to first page')
def move_to_last_page(self, request, queryset):
self._bulk_move(request, queryset, self.LAST)
move_to_last_page.short_description = _('Move selected to last page')
def _move_item(self, request, startorder, endorder):
@ -332,10 +323,7 @@ class SortableAdminMixin(SortableAdminBase):
move_qs = model.objects.filter(**move_filter).order_by(order_by)
move_objs = list(move_qs)
for instance in move_objs:
setattr(
instance, rank_field,
getattr(instance, rank_field) + move_delta
)
setattr(instance, rank_field, getattr(instance, rank_field) + move_delta)
# Do not run `instance.save()`, because it will be updated
# later in bulk by `move_qs.update`.
pre_save.send(
@ -359,10 +347,10 @@ class SortableAdminMixin(SortableAdminBase):
setattr(obj, rank_field, endorder)
obj.save(update_fields=[rank_field])
return [{
'pk': instance.pk,
'order': getattr(instance, rank_field)
} for instance in chain(move_objs, [obj])]
return [
{'pk': instance.pk, 'order': getattr(instance, rank_field)}
for instance in chain(move_objs, [obj])
]
@staticmethod
def get_extra_model_filters(request):
@ -372,9 +360,7 @@ class SortableAdminMixin(SortableAdminBase):
return {}
def get_max_order(self, request, obj=None):
return self.model.objects.aggregate(
max_order=Coalesce(Max(self.default_order_field), 0)
)['max_order']
return self.model.objects.aggregate(max_order=Coalesce(Max(self.default_order_field), 0))['max_order']
def _bulk_move(self, request, queryset, method):
if not self.enable_sorting:
@ -416,15 +402,9 @@ class SortableAdminMixin(SortableAdminBase):
self.message_user(request, msg, level=messages.ERROR)
return
endorders_start = getattr(
objects[page.start_index() - 1], self.default_order_field
)
endorders_start = getattr(objects[page.start_index() - 1], self.default_order_field)
endorders_step = -1 if self.order_by.startswith('-') else 1
endorders = range(
endorders_start,
endorders_start + endorders_step * queryset_size,
endorders_step
)
endorders = range(endorders_start, endorders_start + endorders_step * queryset_size, endorders_step)
if page.number > current_page_number: # Move forward (like drag down)
queryset = queryset.reverse()
@ -455,10 +435,11 @@ class PolymorphicSortableAdminMixin(SortableAdminMixin):
rather than ``admin.ModelAdmin``, then additionally inherit from ``PolymorphicSortableAdminMixin``
rather than ``SortableAdminMixin``.
"""
def get_max_order(self, request, obj=None):
return self.base_model.objects.aggregate(
max_order=Coalesce(Max(self.default_order_field), 0)
)['max_order']
return self.base_model.objects.aggregate(max_order=Coalesce(Max(self.default_order_field), 0))[
'max_order'
]
class CustomInlineFormSetMixin:
@ -466,8 +447,9 @@ class CustomInlineFormSetMixin:
self.default_order_direction, self.default_order_field = _get_default_ordering(self.model, self)
if self.default_order_field not in self.form.base_fields:
self.form.base_fields[self.default_order_field] = \
self.model._meta.get_field(self.default_order_field).formfield()
self.form.base_fields[self.default_order_field] = self.model._meta.get_field(
self.default_order_field
).formfield()
self.form.base_fields[self.default_order_field].is_hidden = True
self.form.base_fields[self.default_order_field].required = False
@ -476,12 +458,8 @@ class CustomInlineFormSetMixin:
super().__init__(*args, **kwargs)
def get_max_order(self):
query_set = self.model.objects.filter(
**{self.fk.get_attname(): self.instance.pk}
)
return query_set.aggregate(
max_order=Coalesce(Max(self.default_order_field), 0)
)['max_order']
query_set = self.model.objects.filter(**{self.fk.get_attname(): self.instance.pk})
return query_set.aggregate(max_order=Coalesce(Max(self.default_order_field), 0))['max_order']
def save_new(self, form, commit=True):
"""
@ -548,19 +526,18 @@ class SortableInlineAdminMixin(SortableAdminBase):
@property
def media(self):
shared = (
super().media + widgets.Media(
js=('adminsortable2/js/libs/jquery.ui.sortable-1.11.4.js',
'adminsortable2/js/inline-sortable.js')))
shared = super().media + widgets.Media(
js=('adminsortable2/js/libs/jquery.ui.sortable-1.11.4.js', 'adminsortable2/js/inline-sortable.js')
)
if isinstance(self, admin.StackedInline):
return shared + widgets.Media(
js=('adminsortable2/js/inline-sortable.js',
'adminsortable2/js/inline-stacked.js'))
js=('adminsortable2/js/inline-sortable.js', 'adminsortable2/js/inline-stacked.js')
)
else:
# assume TabularInline (don't return None in any case)
return shared + widgets.Media(
js=('adminsortable2/js/inline-sortable.js',
'adminsortable2/js/inline-tabular.js'))
js=('adminsortable2/js/inline-sortable.js', 'adminsortable2/js/inline-tabular.js')
)
@property
def template(self):
@ -580,14 +557,11 @@ class CustomGenericInlineFormSet(CustomInlineFormSetMixin, BaseGenericInlineForm
**{
self.ct_fk_field.name: self.instance.pk,
self.ct_field.name: ContentType.objects.get_for_model(
self.instance,
for_concrete_model=self.for_concrete_model
)
self.instance, for_concrete_model=self.for_concrete_model
),
}
)
return query_set.aggregate(
max_order=Coalesce(Max(self.default_order_field), 0)
)['max_order']
return query_set.aggregate(max_order=Coalesce(Max(self.default_order_field), 0))['max_order']
class SortableGenericInlineAdminMixin(SortableInlineAdminMixin):

View File

@ -4,4 +4,5 @@ import sys
sys.path.append(os.path.dirname(__file__))
os.environ['DJANGO_SETTINGS_MODULE'] = 'eo_gestion.settings'
import django.core.handlers.wsgi
application = django.core.handlers.wsgi.WSGIHandler()

View File

@ -3,11 +3,11 @@
import os
import subprocess
from distutils.cmd import Command
from distutils.command.build import build as _build
from distutils.command.sdist import sdist
from setuptools import setup, find_packages
from setuptools import find_packages, setup
from setuptools.command.install_lib import install_lib as _install_lib
install_requires = [
@ -23,11 +23,11 @@ install_requires = [
def get_version():
'''Use the VERSION, if absent generates a version with git describe, if not
tag exists, take 0.0- and add the length of the commit log.
'''
"""Use the VERSION, if absent generates a version with git describe, if not
tag exists, take 0.0- and add the length of the commit log.
"""
if os.path.exists('VERSION'):
with open('VERSION', 'r') as v:
with open('VERSION') as v:
return v.read()
if os.path.exists('.git'):
p = subprocess.Popen(

View File

@ -15,11 +15,8 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import pytest
from django.core.management import call_command
from django.contrib.auth.models import User
from django.core.management import call_command
DATA = ["tests/fixture.json"]

View File

@ -14,9 +14,10 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from eo_gestion.eo_facture.models import Contrat
from taggit.models import Tag
from eo_gestion.eo_facture.models import Contrat
def test_references(app):
gru = Tag.objects.create(name='GRU')

View File

@ -14,9 +14,8 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import pytest
import django_webtest
import pytest
@pytest.fixture

View File

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

View File

@ -14,9 +14,8 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import pytest
import httmock
import pytest
import requests
from eo_gestion.chorus import chorus
@ -47,7 +46,12 @@ def chorus_connection_error(url, request):
@httmock.urlmatch()
def chorus_error_500(url, request):
return httmock.response(
500, b'Pas content \xe9', headers={'Header Pourri': 'Héhé'.encode('latin1'), 'Header-Ok': 'ok',}
500,
b'Pas content \xe9',
headers={
'Header Pourri': 'Héhé'.encode('latin1'),
'Header-Ok': 'ok',
},
)
@ -69,5 +73,8 @@ def test_push_to_chorus_error_500():
assert result == {
'http.response.status-code': 500,
'http.response.body': "b'Pas content \\xe9'",
'http.response.headers': {'Header Pourri': 'H<EFBFBD>h<EFBFBD>', 'Header-Ok': 'ok',},
'http.response.headers': {
'Header Pourri': 'H<EFBFBD>h<EFBFBD>',
'Header-Ok': 'ok',
},
}

View File

@ -16,12 +16,12 @@
import datetime
import io
import pytest
import xml.etree.ElementTree as ET
import facturx
import pytest
from eo_gestion.eo_facture.facturx import to_pdfa, add_facturx_from_bytes
from eo_gestion.eo_facture.facturx import add_facturx_from_bytes, to_pdfa
@pytest.fixture
@ -88,7 +88,14 @@ def test_add_facturx_from_bytes(fake_invoice_bytes):
['PostalTradeAddress', ['CountryID', 'FR']],
['SpecifiedTaxRegistration', ['ID', 'FR09491081899']],
],
['BuyerTradeParty', ['Name', 'RGFIPD'], ['SpecifiedLegalOrganization', ['ID', '1234'],],],
[
'BuyerTradeParty',
['Name', 'RGFIPD'],
[
'SpecifiedLegalOrganization',
['ID', '1234'],
],
],
['BuyerOrderReferencedDocument', ['IssuerAssignedID', '5678']],
['ContractReferencedDocument', ['IssuerAssignedID', 'ABCD']],
],

View File

@ -17,6 +17,7 @@
from datetime import date, timedelta
from django.contrib.auth.models import User
from eo_gestion.eo_facture.forms import FactureForm
from eo_gestion.eo_facture.models import Client, Contrat
@ -86,4 +87,4 @@ def test_facture_form(db, freezer):
data['initial-emission'] = '2019-01-03'
form = FactureForm(data=dict(data, proforma='true', emission='2019-01-01'), instance=facture)
assert not form.is_valid(), form.errors
assert set(form.errors) == set(['proforma', 'emission'])
assert set(form.errors) == {'proforma', 'emission'}

View File

@ -1,6 +1,6 @@
[tox]
toxworkdir = {env:TMPDIR:/tmp}/tox-{env:USER}/barbacompta/{env:BRANCH_NAME:}
envlist = py3-pylint
envlist = py3-pylint,code-style
[testenv]
setenv =
@ -29,6 +29,13 @@ setenv =
commands =
./manage.py {posargs:--help}
[testenv:code-style]
skip_install = true
deps =
pre-commit
commands =
pre-commit run --all-files --show-diff-on-failure
[pytest]
filterwarnings=
ignore:Using or importing the ABCs from