diff --git a/abonapp/templates/abonapp/editAbon.html b/abonapp/templates/abonapp/editAbon.html index cca8636..8795c4f 100644 --- a/abonapp/templates/abonapp/editAbon.html +++ b/abonapp/templates/abonapp/editAbon.html @@ -99,6 +99,10 @@ {% trans 'Remove subscriber' %} {% endif %} + + + {% trans 'Document list' %} + diff --git a/devapp/locale/ru/LC_MESSAGES/django.po b/devapp/locale/ru/LC_MESSAGES/django.po index 2b09633..64bd2f9 100644 --- a/devapp/locale/ru/LC_MESSAGES/django.po +++ b/devapp/locale/ru/LC_MESSAGES/django.po @@ -484,7 +484,7 @@ msgid "Are you sure you want to delete device?" msgstr "Вы уверены что хотите удалить устройство?" #: templates/devapp/devices.html:83 templates/devapp/devices_null_group.html:65 -msgid "Devices does not found" +msgid "Devices not found" msgstr "Нет созданных устройств" #: templates/devapp/devices.html:83 templates/devapp/devices.html:93 diff --git a/devapp/templates/devapp/devices.html b/devapp/templates/devapp/devices.html index e2d95a0..293b99c 100644 --- a/devapp/templates/devapp/devices.html +++ b/devapp/templates/devapp/devices.html @@ -82,7 +82,7 @@ {% empty %} - {% trans 'Devices does not found' %}. {% trans 'Create' %} + {% trans 'Devices not found' %}. {% trans 'Create' %} {% endfor %} {% endwith %} diff --git a/djing/settings.py b/djing/settings.py index 7a60f2a..a49983f 100644 --- a/djing/settings.py +++ b/djing/settings.py @@ -43,6 +43,10 @@ INSTALLED_APPS = [ 'django.contrib.staticfiles', 'encrypted_model_fields', 'django_cleanup.apps.CleanupConfig', + 'guardian', + 'pinax_theme_bootstrap', + 'bootstrapform', + 'bootstrap3', 'ip_pool', 'accounts_app', 'gw_app', @@ -59,10 +63,7 @@ INSTALLED_APPS = [ 'msg_app', 'group_app', 'new_customers', - 'guardian', - 'pinax_theme_bootstrap', - 'bootstrapform', - 'bootstrap3' + 'docsapp' ] if DEBUG: diff --git a/djing/urls.py b/djing/urls.py index a1dab3a..12fc23d 100644 --- a/djing/urls.py +++ b/djing/urls.py @@ -20,7 +20,8 @@ urlpatterns = [ path('messenger/', include('messenger.urls', namespace='messenger')), path('gw/', include('gw_app.urls', namespace='gw_app')), path('new_customers/', include('new_customers.urls', namespace='new_customers')), - path('fin/', include('finapp.urls', namespace='finapp')) + path('fin/', include('finapp.urls', namespace='finapp')), + path('docs/', include('docsapp.urls', namespace='docsapp')) # Switch language #path(r'i18n/', include('django.conf.urls.i18n')), diff --git a/docsapp/__init__.py b/docsapp/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/docsapp/apps.py b/docsapp/apps.py new file mode 100644 index 0000000..2dc9042 --- /dev/null +++ b/docsapp/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class DocsappConfig(AppConfig): + name = 'docsapp' diff --git a/docsapp/forms.py b/docsapp/forms.py new file mode 100644 index 0000000..8030259 --- /dev/null +++ b/docsapp/forms.py @@ -0,0 +1,8 @@ +from django import forms +from docsapp.models import DocumentTemplateModel + + +class DocumentTemplateModelForm(forms.ModelForm): + class Meta: + model = DocumentTemplateModel + fields = '__all__' diff --git a/docsapp/locale/ru/LC_MESSAGES/django.po b/docsapp/locale/ru/LC_MESSAGES/django.po new file mode 100644 index 0000000..eb91290 --- /dev/null +++ b/docsapp/locale/ru/LC_MESSAGES/django.po @@ -0,0 +1,71 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# Dmitry Novikov nerosketch@gmail.com, 2019. +# +#, fuzzy +msgid "" +msgstr "" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-09-12 13:57+0300\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: Dmitry Novikov nerosketch@gmail.com\n" +"Language: ru\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" +"%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n" +"%100>=11 && n%100<=14)? 2 : 3);\n" + +#: models.py:8 templates/docsapp/documenttemplatemodel_list.html:25 +msgid "Title" +msgstr "Название" + +#: models.py:10 +msgid "File docx template" +msgstr "Файл шаблон docx" + +#: templates/docsapp/documenttemplatemodel_confirm_delete.html:9 +msgid "Remove document" +msgstr "Удалить документ" + +#: templates/docsapp/documenttemplatemodel_confirm_delete.html:13 +msgid "Are you sure you want to delete this document?" +msgstr "Вы уверены что хотите удалить документ?" + +#: templates/docsapp/documenttemplatemodel_form.html:19 +msgid "Change document" +msgstr "Изменить документ" + +#: templates/docsapp/documenttemplatemodel_form.html:21 +#: templates/docsapp/documenttemplatemodel_list.html:58 +#: templates/docsapp/documenttemplatemodel_list.html:69 +msgid "Add document" +msgstr "Добавить документ" + +#: templates/docsapp/documenttemplatemodel_form.html:30 +msgid "Save" +msgstr "Сохранить" + +#: templates/docsapp/documenttemplatemodel_form.html:34 +msgid "Back" +msgstr "Назад" + +#: templates/docsapp/documenttemplatemodel_list.html:5 +#: templates/docsapp/documenttemplatemodel_list.html:11 +#: templates/docsapp/documenttemplatemodel_list.html:16 +msgid "Documents" +msgstr "Документы" + +#: templates/docsapp/documenttemplatemodel_list.html:26 +msgid "File" +msgstr "Файл" + +#: templates/docsapp/documenttemplatemodel_list.html:27 +msgid "Document type" +msgstr "Тип документа" + +#: templates/docsapp/documenttemplatemodel_list.html:57 +msgid "Documents not found" +msgstr "Документы не найдены" diff --git a/docsapp/migrations/0001_initial.py b/docsapp/migrations/0001_initial.py new file mode 100644 index 0000000..09c9ac8 --- /dev/null +++ b/docsapp/migrations/0001_initial.py @@ -0,0 +1,27 @@ +# Generated by Django 2.2.4 on 2019-09-12 12:52 + +import django.core.validators +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='DocumentTemplateModel', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(max_length=80, verbose_name='Title', unique=True)), + ('doc_template', models.FileField(upload_to='word_docs', validators=[django.core.validators.FileExtensionValidator(allowed_extensions=('docx',))], verbose_name='File docx template')), + ], + options={ + 'db_table': 'documents', + 'ordering': ('title',), + }, + ), + ] diff --git a/docsapp/migrations/__init__.py b/docsapp/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/docsapp/models.py b/docsapp/models.py new file mode 100644 index 0000000..52141ea --- /dev/null +++ b/docsapp/models.py @@ -0,0 +1,24 @@ +from django.shortcuts import resolve_url +from django.utils.translation import gettext_lazy as _ +from django.core.validators import FileExtensionValidator +from django.db import models + +from abonapp.models import Abon + + +class DocumentTemplateModel(models.Model): + title = models.CharField(_('Title'), max_length=80, unique=True) + doc_template = models.FileField( + _('File docx template'), upload_to='word_docs', + validators=[FileExtensionValidator(allowed_extensions=('docx',))] + ) + + def get_absolute_url(self): + return resolve_url('docsapp:doc_edit', pk=self.pk) + + def __str__(self): + return self.title + + class Meta: + db_table = 'documents' + ordering = ('title',) diff --git a/docsapp/templates/docsapp/documenttemplatemodel_confirm_delete.html b/docsapp/templates/docsapp/documenttemplatemodel_confirm_delete.html new file mode 100644 index 0000000..792772e --- /dev/null +++ b/docsapp/templates/docsapp/documenttemplatemodel_confirm_delete.html @@ -0,0 +1,14 @@ +{% extends 'base_delete_modal.html' %} +{% load i18n %} + +{% block modal_form_url %} + {% url 'docsapp:doc_del' object.pk %} +{% endblock %} + +{% block modal_form_title %} + {% trans 'Remove document' %} +{% endblock %} + +{% block modal_form_text %} +

{% trans 'Are you sure you want to delete this document?' %}

+{% endblock %} diff --git a/docsapp/templates/docsapp/documenttemplatemodel_form.html b/docsapp/templates/docsapp/documenttemplatemodel_form.html new file mode 100644 index 0000000..1bb0980 --- /dev/null +++ b/docsapp/templates/docsapp/documenttemplatemodel_form.html @@ -0,0 +1,39 @@ +{% extends request.is_ajax|yesno:'bajax.html,base.html' %} +{% load i18n bootstrap3 %} + +{% block main %} + + {% if object %} + {% url 'docsapp:doc_edit' object.pk as objurl %} + {% else %} + {% url 'docsapp:doc_add' as objurl %} + {% endif %} + +
{% csrf_token %} + + +
+{% endblock %} diff --git a/docsapp/templates/docsapp/documenttemplatemodel_list.html b/docsapp/templates/docsapp/documenttemplatemodel_list.html new file mode 100644 index 0000000..fc08c2e --- /dev/null +++ b/docsapp/templates/docsapp/documenttemplatemodel_list.html @@ -0,0 +1,77 @@ +{% extends request.is_ajax|yesno:'bajax.html,base.html' %} +{% load i18n %} + +{% block title %} + {% trans 'Documents' %} +{% endblock %} + +{% block breadcrumb %} + +{% endblock %} + +{% block page-header %} + {% trans 'Documents' %} +{% endblock %} + +{% block main %} +
+ + + + + + + + + + + + + {% with can_del_doc=perms.docsapp.delete_documenttemplatemodel can_ch_doc=perms.docsapp.change_documenttemplatemodel %} + {% for doc in object_list %} + + + + + + + + + {% empty %} + + + + {% endfor %} + {% endwith %} + + + + + + + + +
.{% trans 'Title' %}{% trans 'File' %}{% trans 'Document type' %}#
{{ doc.id }}{{ doc.title }}{{ doc.doc_template }}DocX + {% if can_del_doc %} + + + + {% endif %} + {% if can_ch_doc %} + + + + {% endif %} +
+ {% trans 'Documents not found' %}. + {% trans 'Add document' %} +
+ + {% trans 'Add document' %} + +
+
+{% endblock %} diff --git a/docsapp/templates/docsapp/simple_list.html b/docsapp/templates/docsapp/simple_list.html new file mode 100644 index 0000000..175185f --- /dev/null +++ b/docsapp/templates/docsapp/simple_list.html @@ -0,0 +1,26 @@ +{% load i18n %} + + + diff --git a/docsapp/urls.py b/docsapp/urls.py new file mode 100644 index 0000000..50cb896 --- /dev/null +++ b/docsapp/urls.py @@ -0,0 +1,14 @@ +from django.urls import path +from docsapp import views + +app_name = 'docsapp' + + +urlpatterns = [ + path('', views.DocumentsListView.as_view(), name='docs_list'), + path('add/', views.DocumentCreateView.as_view(), name='doc_add'), + path('/', views.DocumentUpdateView.as_view(), name='doc_edit'), + path('/del/', views.DocumentDeleteView.as_view(), name='doc_del'), + path('//render/', views.RenderDocument.as_view(), name='doc_render'), + path('/simple_list/', views.SimpleListView.as_view(), name='simple_list'), +] diff --git a/docsapp/views.py b/docsapp/views.py new file mode 100644 index 0000000..1395993 --- /dev/null +++ b/docsapp/views.py @@ -0,0 +1,101 @@ +from io import BytesIO + +from django.contrib import messages +from django.utils.datetime_safe import datetime +from django.utils.translation import gettext_lazy as _ +from django.views.generic import UpdateView, DeleteView, CreateView, DetailView, ListView +from django.contrib.auth.mixins import PermissionRequiredMixin +from django.core.exceptions import ValidationError +from django.http import HttpResponse +from django.shortcuts import redirect, get_object_or_404 +from django.urls import reverse_lazy +from docxtpl import DocxTemplate +from docx.opc.constants import CONTENT_TYPE +from jinja2.exceptions import UndefinedError, TemplateSyntaxError + +from abonapp.models import Abon +from djing.global_base_views import OrderedFilteredList +from djing.lib.mixins import LoginAdminMixin, LoginAdminPermissionMixin +from docsapp.models import DocumentTemplateModel +from docsapp.forms import DocumentTemplateModelForm + + +class DocumentsListView(LoginAdminMixin, OrderedFilteredList): + model = DocumentTemplateModel + + +class SimpleListView(LoginAdminMixin, ListView): + template_name = 'docsapp/simple_list.html' + model = DocumentTemplateModel + + def get_context_data(self, **kwargs): + kwargs.update({ + 'account_name': self.kwargs.get('account_name') + }) + return super().get_context_data(**kwargs) + + +class DocumentUpdateView(LoginAdminPermissionMixin, UpdateView): + permission_required = 'docsapp.change_documenttemplatemodel' + model = DocumentTemplateModel + form_class = DocumentTemplateModelForm + + +class DocumentDeleteView(LoginAdminPermissionMixin, DeleteView): + permission_required = 'docsapp.delete_documenttemplatemodel' + model = DocumentTemplateModel + success_url = reverse_lazy('docsapp:docs_list') + + +class DocumentCreateView(LoginAdminMixin, PermissionRequiredMixin, CreateView): + model = DocumentTemplateModel + form_class = DocumentTemplateModelForm + permission_required = 'docsapp.add_documenttemplatemodel' + + +class RenderDocument(LoginAdminPermissionMixin, DetailView): + model = DocumentTemplateModel + permission_required = 'docsapp.view_documenttemplatemodel' + context_object_name = 'document' + extra_context = { + 'date': datetime.now() + } + + def get(self, request, *args, **kwargs): + obj = self.get_object() + self.object = obj + acc = get_object_or_404(Abon, username=self.kwargs.get('uname')) + + try: + doc = DocxTemplate( + obj.doc_template.path + ) + context = self.get_context_data(object=obj) + context.update(self.get_context_account(acc)) + doc.render(context) + + destination_document_file = BytesIO() + doc.get_docx().save(destination_document_file) + resp = HttpResponse( + destination_document_file.getvalue(), + content_type=CONTENT_TYPE.WML_DOCUMENT + ) + resp['Content-Disposition'] = 'attachment; filename="document.docx"' + return resp + except (UndefinedError, ValidationError, TemplateSyntaxError) as e: + messages.error(request, str(e)) + return redirect('docsapp:docs_list') + + def get_context_account(self, account): + if not isinstance(account, Abon): + raise ValidationError(message=_('Account required and must be Abon')) + bp = '_' * 20 + return { + 'account': account, + 'passport': getattr(account, 'passportinfo', { + 'series': bp, + 'number': bp, + 'distributor': bp, + 'date_of_acceptance': bp + }) + } diff --git a/requirements.txt b/requirements.txt index 0ca642a..297f4d7 100644 --- a/requirements.txt +++ b/requirements.txt @@ -45,3 +45,5 @@ django-encrypted-model-fields Celery redis celery[redis] + +docxtpl diff --git a/templates/base.html b/templates/base.html index 4cb2ae0..e58f7b1 100644 --- a/templates/base.html +++ b/templates/base.html @@ -129,6 +129,12 @@ + {% url 'docsapp:docs_list' as docslist %} + + + {% trans 'Documents' %} + +