misc: apply double-quote-string-fixer (#79788)
This commit is contained in:
parent
5b36982e20
commit
f1941a3ad8
|
@ -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'}]
|
||||||
|
|
|
@ -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')
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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())
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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/',
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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',)
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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())
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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']
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
|
||||||
ALLOWED_HOSTS = ["localhost"]
|
ALLOWED_HOSTS = ['localhost']
|
||||||
|
|
||||||
DATABASES = {
|
DATABASES = {
|
||||||
'default': {
|
'default': {
|
||||||
|
|
|
@ -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('/')
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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):
|
||||||
|
|
Loading…
Reference in New Issue