mirror of
				https://gitlab.crans.org/bde/nk20
				synced 2025-10-31 07:49:57 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			299 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			299 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| # Copyright (C) 2018-2025 by BDE ENS Paris-Saclay
 | |
| # SPDX-License-Identifier: GPL-3.0-or-later
 | |
| 
 | |
| import html
 | |
| 
 | |
| import django_tables2 as tables
 | |
| from django.utils.html import format_html, mark_safe
 | |
| from django_tables2.utils import A
 | |
| from django.utils.translation import gettext_lazy as _
 | |
| from note_kfet.middlewares import get_current_request
 | |
| from permission.backends import PermissionBackend
 | |
| 
 | |
| from .models.notes import Alias, Trust
 | |
| from .models.transactions import Transaction, TransactionTemplate
 | |
| from .templatetags.pretty_money import pretty_money
 | |
| 
 | |
| 
 | |
| class HistoryTable(tables.Table):
 | |
|     class Meta:
 | |
|         attrs = {
 | |
|             'class': 'table table-condensed table-striped'
 | |
|         }
 | |
|         model = Transaction
 | |
|         exclude = ("id", "polymorphic_ctype", "invalidity_reason", "source_alias", "destination_alias",)
 | |
|         template_name = 'django_tables2/bootstrap4.html'
 | |
|         sequence = ('...', 'type', 'total', 'valid',)
 | |
|         orderable = False
 | |
| 
 | |
|     source = tables.Column(
 | |
|         attrs={
 | |
|             "td": {
 | |
|                 "class": "text-nowrap",
 | |
|                 "data-toggle": "tooltip",
 | |
|                 "title": lambda record: _("used alias").capitalize() + " : " + record.source_alias,
 | |
|             }
 | |
|         }
 | |
|     )
 | |
| 
 | |
|     destination = tables.Column(
 | |
|         attrs={
 | |
|             "td": {
 | |
|                 "class": "text-nowrap",
 | |
|                 "data-toggle": "tooltip",
 | |
|                 "title": lambda record: _("used alias").capitalize() + " : " + record.destination_alias,
 | |
|             }
 | |
|         }
 | |
|     )
 | |
| 
 | |
|     created_at = tables.DateTimeColumn(
 | |
|         format='Y-m-d H:i:s',
 | |
|         attrs={
 | |
|             "td": {
 | |
|                 "class": "text-nowrap",
 | |
|             },
 | |
|         }
 | |
|     )
 | |
| 
 | |
|     amount = tables.Column(
 | |
|         attrs={
 | |
|             "td": {
 | |
|                 "class": "text-nowrap",
 | |
|             },
 | |
|         }
 | |
|     )
 | |
| 
 | |
|     reason = tables.Column(
 | |
|         attrs={
 | |
|             "td": {
 | |
|                 "class": "text-break",
 | |
|             },
 | |
|         }
 | |
|     )
 | |
| 
 | |
|     type = tables.Column()
 | |
| 
 | |
|     total = tables.Column(  # will use Transaction.total() !!
 | |
|         attrs={
 | |
|             "td": {
 | |
|                 "class": "text-nowrap",
 | |
|             },
 | |
|         }
 | |
|     )
 | |
| 
 | |
|     valid = tables.Column(
 | |
|         attrs={
 | |
|             "td": {
 | |
|                 "id": lambda record: "validate_" + str(record.id),
 | |
|                 "class": lambda record:
 | |
|                 str(record.valid).lower()
 | |
|                 + (' validate' if record.source.is_active and record.destination.is_active and PermissionBackend
 | |
|                    .check_perm(get_current_request(), "note.change_transaction_invalidity_reason", record)
 | |
|                    else ''),
 | |
|                 "data-toggle": "tooltip",
 | |
|                 "title": lambda record: (_("Click to invalidate") if record.valid else _("Click to validate"))
 | |
|                 if PermissionBackend.check_perm(get_current_request(),
 | |
|                                                 "note.change_transaction_invalidity_reason", record)
 | |
|                 and record.source.is_active and record.destination.is_active else None,
 | |
|                 "onclick": lambda record: 'de_validate(' + str(record.id) + ', ' + str(record.valid).lower()
 | |
|                                           + ', "' + str(record.__class__.__name__) + '")'
 | |
|                 if PermissionBackend.check_perm(get_current_request(),
 | |
|                                                 "note.change_transaction_invalidity_reason", record)
 | |
|                 and record.source.is_active and record.destination.is_active else None,
 | |
|                 "onmouseover": lambda record: '$("#invalidity_reason_'
 | |
|                                               + str(record.id) + '").show();$("#invalidity_reason_'
 | |
|                                               + str(record.id) + '").focus();',
 | |
|                 "onmouseout": lambda record: '$("#invalidity_reason_' + str(record.id) + '").hide()',
 | |
|             }
 | |
|         }
 | |
|     )
 | |
| 
 | |
|     def render_amount(self, value):
 | |
|         return pretty_money(value)
 | |
| 
 | |
|     def render_total(self, value):
 | |
|         return pretty_money(value)
 | |
| 
 | |
|     def render_type(self, value):
 | |
|         return _(value)
 | |
| 
 | |
|     # Django-tables escape strings. That's a wrong thing.
 | |
|     def render_reason(self, value):
 | |
|         return html.unescape(value)
 | |
| 
 | |
|     def render_valid(self, value, record):
 | |
|         """
 | |
|         When the validation status is hovered, an input field is displayed to let the user specify an invalidity reason
 | |
|         """
 | |
|         has_perm = PermissionBackend \
 | |
|             .check_perm(get_current_request(), "note.change_transaction_invalidity_reason", record)
 | |
| 
 | |
|         val = "✔" if value else "✖"
 | |
| 
 | |
|         if value and not has_perm:
 | |
|             return val
 | |
| 
 | |
|         val += "<input type='text' class='form-control' id='invalidity_reason_" + str(record.id) \
 | |
|                + "' value='" + (html.escape(record.invalidity_reason)
 | |
|                                 if record.invalidity_reason else ("" if value else str(_("No reason specified")))) \
 | |
|                + "'" + ("" if value and record.source.is_active and record.destination.is_active else " disabled") \
 | |
|                + " placeholder='" + html.escape(_("invalidity reason").capitalize()) + "'" \
 | |
|                + " style='position: absolute; width: 15em; margin-left: -15.5em; margin-top: -2em; display: none;'>"
 | |
|         return format_html(val)
 | |
| 
 | |
| 
 | |
| # function delete_button(id) provided in template file
 | |
| DELETE_TEMPLATE = """
 | |
|     <button id="{{ record.pk }}" class="btn btn-danger btn-sm" onclick="delete_button(this.id)"> {{ delete_trans }}</button>
 | |
| """
 | |
| 
 | |
| 
 | |
| class TrustTable(tables.Table):
 | |
|     class Meta:
 | |
|         attrs = {
 | |
|             'class': 'table table condensed table-striped',
 | |
|             'id': "trust_table"
 | |
|         }
 | |
|         model = Trust
 | |
|         fields = ("trusted",)
 | |
|         template_name = 'django_tables2/bootstrap4.html'
 | |
| 
 | |
|     show_header = False
 | |
|     trusted = tables.Column(attrs={'td': {'class': 'text-center'}})
 | |
| 
 | |
|     delete_col = tables.TemplateColumn(
 | |
|         template_code=DELETE_TEMPLATE,
 | |
|         extra_context={"delete_trans": _('Delete')},
 | |
|         attrs={
 | |
|             'td': {
 | |
|                 'class': lambda record: 'col-sm-1'
 | |
|                 + (' d-none' if not PermissionBackend.check_perm(
 | |
|                     get_current_request(), "note.delete_trust", record)
 | |
|                    else '')}},
 | |
|         verbose_name=_("Delete"),)
 | |
| 
 | |
| 
 | |
| class TrustedTable(tables.Table):
 | |
|     class Meta:
 | |
|         attrs = {
 | |
|             'class': 'table table condensed table-striped',
 | |
|             'id': 'trusted_table'
 | |
|         }
 | |
|         Model = Trust
 | |
|         fields = ("trusting",)
 | |
|         template_name = "django_tables2/bootstrap4.html"
 | |
| 
 | |
|     show_header = False
 | |
|     trusting = tables.Column(attrs={
 | |
|         'td': {'class': 'text-center', 'width': '100%'}})
 | |
| 
 | |
|     trust_back = tables.Column(
 | |
|         verbose_name=_("Trust back"),
 | |
|         accessor="pk",
 | |
|         attrs={
 | |
|             'td': {
 | |
|                 'class': '',
 | |
|                 'id': lambda record: "trust_back_" + str(record.pk),
 | |
|             }
 | |
|         },
 | |
|     )
 | |
| 
 | |
|     def render_trust_back(self, record):
 | |
|         user_note = record.trusted
 | |
|         trusting_note = record.trusting
 | |
|         if Trust.objects.filter(trusted=trusting_note, trusting=user_note):
 | |
|             return ""
 | |
|         val = '<button id="'
 | |
|         val += str(record.pk)
 | |
|         val += '" class="btn btn-success btn-sm text-nowrap" \
 | |
|             onclick="create_trust(' + str(record.trusted.pk) + ',' + \
 | |
|             str(record.trusting.pk) + ')">'
 | |
|         val += str(_("Add back"))
 | |
|         val += '</button>'
 | |
|         return mark_safe(val)
 | |
| 
 | |
| 
 | |
| class AliasTable(tables.Table):
 | |
|     class Meta:
 | |
|         attrs = {
 | |
|             'class': 'table table condensed table-striped',
 | |
|             'id': "alias_table"
 | |
|         }
 | |
|         model = Alias
 | |
|         fields = ('name',)
 | |
|         template_name = 'django_tables2/bootstrap4.html'
 | |
| 
 | |
|     show_header = False
 | |
|     name = tables.Column(attrs={'td': {'class': 'text-center'}})
 | |
| 
 | |
|     delete_col = tables.TemplateColumn(template_code=DELETE_TEMPLATE,
 | |
|                                        extra_context={"delete_trans": _('delete')},
 | |
|                                        attrs={'td': {'class': lambda record: 'col-sm-1' + (
 | |
|                                            ' d-none' if not PermissionBackend.check_perm(
 | |
|                                                get_current_request(), "note.delete_alias",
 | |
|                                                record) else '')}}, verbose_name=_("Delete"), )
 | |
| 
 | |
| 
 | |
| class ButtonTable(tables.Table):
 | |
|     class Meta:
 | |
|         attrs = {
 | |
|             'class': 'table table-bordered condensed'
 | |
|         }
 | |
|         row_attrs = {
 | |
|             'class': lambda record: 'table-row ' + ('table-success' if record.display else 'table-danger'),
 | |
|             'id': lambda record: "row-" + str(record.pk),
 | |
|         }
 | |
| 
 | |
|         model = TransactionTemplate
 | |
|         exclude = ('id',)
 | |
| 
 | |
|     edit = tables.LinkColumn(
 | |
|         'note:template_update',
 | |
|         args=[A('pk')],
 | |
|         attrs={
 | |
|             'td': {'class': 'col-sm-1'},
 | |
|             'a': {
 | |
|                 'class': 'btn btn-sm btn-primary',
 | |
|                 'data-turbolinks': 'false',
 | |
|             }
 | |
|         },
 | |
|         text=_('edit'),
 | |
|         accessor='pk',
 | |
|         verbose_name=_("Edit"),
 | |
|         orderable=False,
 | |
|     )
 | |
| 
 | |
|     hideshow = tables.Column(
 | |
|         verbose_name=_("Hide/Show"),
 | |
|         accessor="pk",
 | |
|         orderable=False,
 | |
|         attrs={
 | |
|             'td': {
 | |
|                 'class': 'col-sm-1',
 | |
|                 'id': lambda record: "hideshow_" + str(record.pk),
 | |
|             }
 | |
|         },
 | |
|     )
 | |
| 
 | |
|     delete_col = tables.TemplateColumn(template_code=DELETE_TEMPLATE,
 | |
|                                        extra_context={"delete_trans": _('delete')},
 | |
|                                        attrs={'td': {'class': 'col-sm-1'}},
 | |
|                                        verbose_name=_("Delete"),
 | |
|                                        orderable=False, )
 | |
| 
 | |
|     def render_amount(self, value):
 | |
|         return pretty_money(value)
 | |
| 
 | |
|     def order_category(self, queryset, is_descending):
 | |
|         return queryset.order_by(f"{'-' if is_descending else ''}category__name"), True
 | |
| 
 | |
|     def render_hideshow(self, record):
 | |
|         val = '<button id="'
 | |
|         val += str(record.pk)
 | |
|         val += '" class="btn btn-secondary btn-sm" \
 | |
|             onclick="hideshow(' + str(record.id) + ',' + \
 | |
|             str(record.display).lower() + ')">'
 | |
|         val += str(_("Hide/Show"))
 | |
|         val += '</button>'
 | |
|         return mark_safe(val)
 |