20 changed files with 427 additions and 7 deletions
-
4abonapp/templates/abonapp/editAbon.html
-
2devapp/locale/ru/LC_MESSAGES/django.po
-
2devapp/templates/devapp/devices.html
-
9djing/settings.py
-
3djing/urls.py
-
0docsapp/__init__.py
-
5docsapp/apps.py
-
8docsapp/forms.py
-
71docsapp/locale/ru/LC_MESSAGES/django.po
-
27docsapp/migrations/0001_initial.py
-
0docsapp/migrations/__init__.py
-
24docsapp/models.py
-
14docsapp/templates/docsapp/documenttemplatemodel_confirm_delete.html
-
39docsapp/templates/docsapp/documenttemplatemodel_form.html
-
77docsapp/templates/docsapp/documenttemplatemodel_list.html
-
26docsapp/templates/docsapp/simple_list.html
-
14docsapp/urls.py
-
101docsapp/views.py
-
2requirements.txt
-
6templates/base.html
@ -0,0 +1,5 @@ |
|||||
|
from django.apps import AppConfig |
||||
|
|
||||
|
|
||||
|
class DocsappConfig(AppConfig): |
||||
|
name = 'docsapp' |
||||
@ -0,0 +1,8 @@ |
|||||
|
from django import forms |
||||
|
from docsapp.models import DocumentTemplateModel |
||||
|
|
||||
|
|
||||
|
class DocumentTemplateModelForm(forms.ModelForm): |
||||
|
class Meta: |
||||
|
model = DocumentTemplateModel |
||||
|
fields = '__all__' |
||||
@ -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 "Документы не найдены" |
||||
@ -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',), |
||||
|
}, |
||||
|
), |
||||
|
] |
||||
@ -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',) |
||||
@ -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 %} |
||||
|
<h4>{% trans 'Are you sure you want to delete this document?' %}</h4> |
||||
|
{% endblock %} |
||||
@ -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 %} |
||||
|
|
||||
|
<form action="{{ objurl }}" method="post" enctype="multipart/form-data">{% csrf_token %} |
||||
|
<div class="modal-header primary"> |
||||
|
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button> |
||||
|
<h4 class="modal-title"> |
||||
|
<span class="glyphicon glyphicon-exclamation-sign"></span> |
||||
|
|
||||
|
{% if object %} |
||||
|
{% trans 'Change document' %} |
||||
|
{% else %} |
||||
|
{% trans 'Add document' %} |
||||
|
{% endif %} |
||||
|
|
||||
|
</h4> |
||||
|
</div> |
||||
|
<div class="modal-body"> |
||||
|
{% bootstrap_form form %} |
||||
|
|
||||
|
<div class="btn-group btn-group-sm"> |
||||
|
{% bootstrap_button _('Save') button_type="submit" button_class="btn-primary" icon="save" %} |
||||
|
|
||||
|
{% if not request.is_ajax %} |
||||
|
{% url 'docsapp:docs_list' as backurl %} |
||||
|
{% bootstrap_button _('Back') button_type="link" href=backurl icon="fast-backward" %} |
||||
|
{% endif %} |
||||
|
</div> |
||||
|
</div> |
||||
|
</form> |
||||
|
{% endblock %} |
||||
@ -0,0 +1,77 @@ |
|||||
|
{% extends request.is_ajax|yesno:'bajax.html,base.html' %} |
||||
|
{% load i18n %} |
||||
|
|
||||
|
{% block title %} |
||||
|
{% trans 'Documents' %} |
||||
|
{% endblock %} |
||||
|
|
||||
|
{% block breadcrumb %} |
||||
|
<ol class="breadcrumb"> |
||||
|
<li><span class="glyphicon glyphicon-home"></span></li> |
||||
|
<li class="active">{% trans 'Documents' %}</li> |
||||
|
</ol> |
||||
|
{% endblock %} |
||||
|
|
||||
|
{% block page-header %} |
||||
|
{% trans 'Documents' %} |
||||
|
{% endblock %} |
||||
|
|
||||
|
{% block main %} |
||||
|
<div class="table-responsive"> |
||||
|
<table class="table table-bordered"> |
||||
|
<thead> |
||||
|
<tr> |
||||
|
<th>.</th> |
||||
|
<th class="col-md-2">{% trans 'Title' %}</th> |
||||
|
<th class="col-md-4">{% trans 'File' %}</th> |
||||
|
<th class="col-md-3">{% trans 'Document type' %}</th> |
||||
|
<th>#</th> |
||||
|
</tr> |
||||
|
</thead> |
||||
|
|
||||
|
<tbody> |
||||
|
{% with can_del_doc=perms.docsapp.delete_documenttemplatemodel can_ch_doc=perms.docsapp.change_documenttemplatemodel %} |
||||
|
{% for doc in object_list %} |
||||
|
<tr> |
||||
|
<td>{{ doc.id }}</td> |
||||
|
<td>{{ doc.title }}</td> |
||||
|
<td>{{ doc.doc_template }}</td> |
||||
|
<td>DocX</td> |
||||
|
|
||||
|
<td class="btn-group btn-group-xs btn-group-justified"> |
||||
|
{% if can_del_doc %} |
||||
|
<a href="{% url 'docsapp:doc_del' doc.pk %}" class="btn btn-danger btn-modal"> |
||||
|
<span class="glyphicon glyphicon-remove"></span> |
||||
|
</a> |
||||
|
{% endif %} |
||||
|
{% if can_ch_doc %} |
||||
|
<a href="{% url 'docsapp:doc_edit' doc.id %}" class="btn btn-default btn-modal"> |
||||
|
<span class="glyphicon glyphicon-edit"></span> |
||||
|
</a> |
||||
|
{% endif %} |
||||
|
</td> |
||||
|
</tr> |
||||
|
{% empty %} |
||||
|
<tr> |
||||
|
<td colspan="6"> |
||||
|
{% trans 'Documents not found' %}. |
||||
|
<a href="{% url 'docsapp:doc_add' %}" class="btn-modal">{% trans 'Add document' %}</a> |
||||
|
</td> |
||||
|
</tr> |
||||
|
{% endfor %} |
||||
|
{% endwith %} |
||||
|
</tbody> |
||||
|
|
||||
|
<tfoot> |
||||
|
<tr> |
||||
|
<td colspan="6"> |
||||
|
<a href="{% url 'docsapp:doc_add' %}" class="btn btn-success btn-sm btn-modal"> |
||||
|
<span class="glyphicon glyphicon-plus"></span> {% trans 'Add document' %} |
||||
|
</a> |
||||
|
</td> |
||||
|
</tr> |
||||
|
</tfoot> |
||||
|
|
||||
|
</table> |
||||
|
</div> |
||||
|
{% endblock %} |
||||
@ -0,0 +1,26 @@ |
|||||
|
{% load i18n %} |
||||
|
|
||||
|
<div class="modal-header primary"> |
||||
|
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button> |
||||
|
<h4 class="modal-title">{% trans 'Documents' %}</h4> |
||||
|
</div> |
||||
|
<div class="modal-body"> |
||||
|
|
||||
|
<div class="panel panel-default"> |
||||
|
<div class="panel-body"> |
||||
|
<ul class="list-group"> |
||||
|
{% for obj in object_list %} |
||||
|
<li class="list-group-item"> |
||||
|
<b>{{ obj.title }}</b> |
||||
|
<a href="{% url 'docsapp:doc_render' obj.pk account_name %}" class="btn btn-default"> |
||||
|
<span class="glyphicon glyphicon-print"></span> {% trans 'Render document' %} |
||||
|
</a> |
||||
|
</li> |
||||
|
{% empty %} |
||||
|
{% trans 'Documents not found' %} |
||||
|
{% endfor %} |
||||
|
</ul> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
</div> |
||||
@ -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('<int:pk>/', views.DocumentUpdateView.as_view(), name='doc_edit'), |
||||
|
path('<int:pk>/del/', views.DocumentDeleteView.as_view(), name='doc_del'), |
||||
|
path('<int:pk>/<slug:uname>/render/', views.RenderDocument.as_view(), name='doc_render'), |
||||
|
path('<slug:account_name>/simple_list/', views.SimpleListView.as_view(), name='simple_list'), |
||||
|
] |
||||
@ -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 |
||||
|
}) |
||||
|
} |
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue