You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

827 lines
34 KiB

# -*- coding: utf-8 -*-
import os
import glob
from datetime import datetime
import tempfile
from email.header import Header
from time import time
import simplejson as json
import ghostscript
from django.shortcuts import render, get_object_or_404, redirect
from django.http import HttpResponseServerError, HttpResponseBadRequest, HttpResponse
from django.utils.decorators import method_decorator
from django.views.decorators.http import require_POST
from django.views.decorators.csrf import csrf_protect
from django.db.models import Sum
from django.forms.models import inlineformset_factory, model_to_dict
from django.template.loader import render_to_string
from django.core.mail import EmailMessage
from django.utils.encoding import smart_str
from django.conf import settings
from project.commons.utils import dthandler
from project.commons.paginator import pagination, save_per_page_value
from project.commons.pdf_tools import render_pdf_to_string, pdf_to_response
from project.commons.xls import xls_to_response
from project.customer.models import get_profile, BankAccount
from project.customer.forms import ClientsListForm, ClientForm
from ..as_xls import render_xls_to_string
from ..forms import EmailForm, InvoicesListForm
from .. import filters
from project.customer.decorators import license_required
DEBUG = getattr(settings, 'DEBUG', False)
SUPPORT_EMAIL = getattr(settings, 'SUPPORT_EMAIL', '')
MEDIA_ROOT = getattr(settings, 'MEDIA_ROOT', '')
MEDIA_URL = getattr(settings, 'MEDIA_URL', '')
ORDER_VAR = 'o'
ORDER_TYPE_VAR = 'ot'
class Ordering(object):
"""Параметры сортировки. Для передачи в шаблон."""
order_var = ORDER_VAR
order_type_var = ORDER_TYPE_VAR
def __init__(self, order_field, order_type):
self.order_field = order_field
self.order_type = order_type
class BaseViews(object):
"""Базовые views для простых документов (без табличной части)."""
MODEL = None # модель документа
FORM_CLASS = None # форма документа
EMAIL_FORM_CLASS = EmailForm # форма создания и отправки email
# поля, по которым можно сортировать список документов
ORDER_FIELDS = ('doc_date', 'doc_num', 'client__name', 'doc_sum',)
# поля, по которым можно фильтровать список документов
# должны поддерживаться в docs.filters.build_filterset_class !
FILTER_FIELDS = ('client', 'doc_date',)
# префикс именованных урлов документов данного типа, для передачи в шаблон
URL_PREFIX = ''
# именованные урлы операций
URL_LIST = ''
URL_EDIT = ''
# пути к шаблонам
TEMPLATE_LIST = 'docs/_base/base_list.html'
TEMPLATE_ADD = 'docs/_base/base_add.html'
TEMPLATE_EDIT = 'docs/_base/base_edit.html'
TEMPLATE_DELETE = 'docs/_base/base_delete.html'
TEMPLATE_FORM = 'docs/_base/base_form.html'
TEMPLATE_IMG = 'docs/_base/preview.html'
TEMPLATE_FORM_JS = 'docs/stub_js.html'
TEMPLATE_CREATE_DOCS = ''
TEMPLATE_EMAIL = 'docs/email/base_email.html'
TEMPLATE_EMAIL_FORM = 'docs/email/base_email_form.html'
EMAIL_MSG_TEMPLATE = 'docs/email/email.txt' # шаблон письма
# для генерации pdf/xls
PDF_TEMPLATE = ''
XLS_TEMPLATE = ''
FILENAME = u'Документ № %s, %s' # без расширения
MAYBE_SIGNED = False
# --- константы для вывода наименований в шаблонах
PADEJI = {
'imenit': u'документ', # кто? что?
'rodit': u'документа', # кого? чего?
'dateln': u'документу', # кому? чему?
'vinit': u'документ', # кого? что?
'tvorit': u'документом', # кем? чем?
'predlojn': u'документе', # о ком? о чём?
}
PADEJI_MNOJ = {
'imenit': u'документы', # кто? что?
'rodit': u'документов', # кого? чего?
'dateln': u'документам', # кому? чему?
'vinit': u'документы', # кого? что?
'tvorit': u'документами', # кем? чем?
'predlojn': u'документах', # о ком? о чём?
}
def __init__(self, request):
self.request = request
self.user = request.user
self.profile = request.profile
self.set_redirects()
self.MODEL_NAME = self.MODEL.__name__.lower()
self.asserts()
def asserts(self):
"""Проверить объект класса на типичные ошибки."""
assert self.request is not None, (u"%s.request can't be None!" % self.__class__.__name__)
assert self.MODEL is not None, (u"%s.MODEL can't be None!" % self.__class__.__name__)
assert self.FORM_CLASS is not None, (u"%s.FORM_CLASS can't be None!" % self.__class__.__name__)
assert self.EMAIL_FORM_CLASS is not None, (u"%s.EMAIL_FORM_CLASS can't be None!" % self.__class__.__name__)
assert (isinstance(self.ORDER_FIELDS, tuple) or isinstance(self.ORDER_FIELDS, list)), (u"%s.ORDER_FIELDS should be of tuple or list type!" % self.__class__.__name__)
def set_redirects(self):
"""Куда редиректить после операции."""
self.REDIRECT_AFTER_ADD = self.URL_LIST
self.REDIRECT_AFTER_EDIT = self.URL_LIST
self.REDIRECT_AFTER_COPY = self.URL_EDIT
self.REDIRECT_AFTER_DELETE = self.URL_LIST
self.REDIRECT_AFTER_EMAIL = self.URL_LIST
def get_ordering(self):
"""Поле и порядок сортировки."""
order_field, order_type = 'doc_date', 'desc' # default
params = dict(self.request.GET.items())
if params.get(ORDER_VAR) in self.ORDER_FIELDS:
order_field = params.get(ORDER_VAR)
if params.get(ORDER_TYPE_VAR) in ('asc', 'desc'):
order_type = params.get(ORDER_TYPE_VAR)
return order_field, order_type
def get_list_qs(self):
"""QuerySet для просмотра списка документов."""
qs = self.MODEL.objects.filter(user=self.request.user)
qs = qs.select_related('client')
# задать сортировку
order_field, order_type = self.get_ordering()
if order_field:
qs = qs.order_by('%s%s' % ((order_type == 'desc' and '-' or ''), order_field,))
return qs
def get_filters_class(self):
"""Возвращает класс с набором фильтров."""
return filters.build_filterset_class(self.MODEL, self.request.user, self.FILTER_FIELDS)
def get_filters(self, qs):
"""Возвращает объект с набором фильтров."""
klass = self.get_filters_class()
return klass(self.request.user, self.request.GET, qs)
def get_obj(self, id, only_form_fields=False):
"""Объект документа или ошибка 404, если его нет в базе.
Поведение когда флаг only_form_fields=True:
если в форме редактирования документа задан атрибут Meta.fields, то запрашивает только поля,
перечисленные в нём. Иначе (как и по умолчанию) дампит вообще все поля, которые есть в модели.
"""
if only_form_fields:
try:
fields_list = self.FORM_CLASS.Meta.fields
except AttributeError:
fields_list = []
return get_object_or_404(self.MODEL.objects.values(*fields_list), pk=id, user=self.request.user)
else:
return get_object_or_404(self.MODEL, pk=id, user=self.request.user)
def get_filename(self, *args, **kwargs):
obj = self.get_obj(kwargs['id'])
client = obj.client.name.replace('\n',' ').replace('\r',' ').strip()
return self.FILENAME % (obj.doc_num, client,)
def update_list_dict(self, dictionary):
"""Здесь можно изменить словарь параметров перед передачей его в шаблон вывода списка документов."""
dictionary['clients_form'] = ClientsListForm(self.request.user)
dictionary['invoices_form'] = InvoicesListForm(self.request.user)
@method_decorator(csrf_protect)
@method_decorator(save_per_page_value)
def list(self, *args, **kwargs):
"""Список документов."""
obj_list = self.get_list_qs()
# фильтрация списка
filters = self.get_filters(obj_list)
obj_list_count_before_filtering = 0 # сколько записей было в списке до его фильтрации
if not filters.qs:
obj_list_count_before_filtering = obj_list.count()
obj_list = filters.qs
# пагинация списка
page_num = kwargs.get('page_num')
page, pagination_form = pagination(self.request, obj_list, page_num)
# параметры сортировки для отрисовки в шаблоне
# реальная сортировка QuerySet производится в методе get_list_qs
order_field, order_type = self.get_ordering()
ordering = Ordering(order_field, order_type)
email_form = self.EMAIL_FORM_CLASS()
dictionary = {
'padeji': self.PADEJI,
'padeji_mnoj': self.PADEJI_MNOJ,
'url_prefix': self.URL_PREFIX,
'model_name': self.MODEL_NAME,
'page': page,
'pagination_form': pagination_form,
'ordering': ordering,
'filters': filters,
'obj_list_count_before_filtering': obj_list_count_before_filtering,
'email_form': email_form,
'template_create_docs': self.TEMPLATE_CREATE_DOCS,
'maybe_sign': self.MAYBE_SIGNED,
}
self.update_list_dict(dictionary)
return render(self.request, self.TEMPLATE_LIST, dictionary)
def init_form(self):
"""Начальные значения полей формы документа."""
initial = {'doc_date': datetime.now(),}
# номер нового документа
doc_num = self.MODEL.objects.get_max_doc_num(self.request.user) or 0
initial['doc_num'] = doc_num + 1
return initial
@method_decorator(license_required)
@method_decorator(csrf_protect)
def add(self, *args, **kwargs):
"""Добавить документ.
Если при GET-запросе в kwargs передать initial, то создаст предзаполненный документ.
"""
if self.request.method == 'POST' and '_cancel' in self.request.POST:
return redirect(self.REDIRECT_AFTER_ADD)
if self.request.method == 'POST':
form = self.FORM_CLASS(self.request.user, data=self.request.POST)
if form.is_valid():
new_obj = form.save(commit=False)
new_obj.user = self.request.user
new_obj.company = self.request.user.profile
new_obj.save()
return redirect(self.REDIRECT_AFTER_ADD)
else:
initial = kwargs.get('initial') or self.init_form()
form = self.FORM_CLASS(self.request.user, initial=initial)
dictionary = {
'padeji': self.PADEJI,
'padeji_mnoj': self.PADEJI_MNOJ,
'url_prefix': self.URL_PREFIX,
'form_template': self.TEMPLATE_FORM,
'form_template_js': self.TEMPLATE_FORM_JS,
'form': form,
'client_form': ClientForm(),
}
return render(self.request, self.TEMPLATE_ADD, dictionary)
def copy(self, *args, **kwargs):
"""Создать полную копию документа."""
obj = self.get_obj(kwargs['id'])
kwargs['initial'] = model_to_dict(
obj,
fields=getattr(self.FORM_CLASS.Meta, 'fields', None),
exclude=getattr(self.FORM_CLASS.Meta, 'exclude', None)
)
kwargs['initial'].update(self.init_form())
# обязательно убрать ключи
kwargs['initial'].pop('pk', None)
kwargs['initial'].pop('id', None)
kwargs['initial'].pop('created_at', None)
kwargs['initial'].pop('updated_at', None)
return self.add(self.request, *args, **kwargs)
@method_decorator(csrf_protect)
def edit(self, *args, **kwargs):
"""Редактировать документ."""
if self.request.method == 'POST' and '_cancel' in self.request.POST:
return redirect(self.REDIRECT_AFTER_EDIT)
obj = self.get_obj(kwargs['id'])
if self.request.method == 'POST':
form = self.FORM_CLASS(self.request.user, data=self.request.POST, instance=obj)
if form.is_valid():
new_obj = form.save()
return redirect(self.REDIRECT_AFTER_EDIT)
else:
form = self.FORM_CLASS(self.request.user, instance=obj)
dictionary = {
'padeji': self.PADEJI,
'padeji_mnoj': self.PADEJI_MNOJ,
'url_prefix': self.URL_PREFIX,
'form_template': self.TEMPLATE_FORM,
'form_template_js': self.TEMPLATE_FORM_JS,
'obj': obj,
'form': form,
'client_form': ClientForm(),
}
return render(self.request, self.TEMPLATE_EDIT, dictionary)
@method_decorator(csrf_protect)
def delete(self, *args, **kwargs):
"""Удалить документ."""
success = False
result = None
if self.request.is_ajax():
obj = self.get_obj(kwargs['id'])
del_num = obj.doc_num
del_id = obj.id
del_date = datetime.strftime(obj.doc_date, '%d.%m.%Y')
if self.request.method == 'POST':
obj.delete()
success = True
result = {'title': u'Удаление документа',
'msg': u'Удалён документ:<br /> %s%d от %s' % (self.PADEJI['imenit'], del_num, del_date),
'success': success,
'del_id': del_id,
}
return HttpResponse(json.dumps(result), mimetype='application/json')
#dictionary = {
# 'padeji': self.PADEJI,
# 'padeji_mnoj': self.PADEJI_MNOJ,
# 'obj': obj,
#}
#return render(self.request, self.TEMPLATE_DELETE, dictionary)
def prepare(self, obj, export_to=None):
"""Изменить/подмешать дополнительные поля к документу."""
pass
def get_pdf(self, *args, **kwargs):
"""Создать документ в PDF и вернуть как строку."""
obj = self.get_obj(kwargs['id'])
profile = get_profile(obj.user)
main_account = BankAccount.objects.get_main(profile)
self.prepare(obj, export_to='pdf')
params = {
'obj': obj,
'obj_items': None,
'profile': profile,
'main_account': main_account,
'doc_sign': kwargs.get('doc_sign', True),
}
c1 = time()
pdf = render_pdf_to_string(self.request, self.PDF_TEMPLATE, params)
if DEBUG:
print '%s generation time (seconds): %s' % (self.PDF_TEMPLATE, time()-c1,)
return pdf
def get_xls(self, *args, **kwargs):
"""Создать документ в Excel и вернуть как строку."""
obj = self.get_obj(kwargs['id'])
profile = get_profile(obj.user)
main_account = BankAccount.objects.get_main(profile)
self.prepare(obj, export_to='xls')
params = {
'obj': obj,
'obj_items': None,
'profile': profile,
'main_account': main_account,
}
c1 = time()
xls = render_xls_to_string(self.request, self.XLS_TEMPLATE, params)
if DEBUG:
print '%s generation time (seconds): %s' % (self.XLS_TEMPLATE, time()-c1,)
return xls
def as_img(self, *args, **kwargs):
"""Вывести превью документа."""
try:
obj = self.get_obj(kwargs['id'])
pdf = self.get_pdf(*args, **kwargs)
_, filename = tempfile.mkstemp()
tmp_media_dir = os.path.join(MEDIA_ROOT, 'tmp')
if not os.path.exists(tmp_media_dir):
os.makedirs(tmp_media_dir)
tmp_dir = tempfile.mkdtemp(dir=tmp_media_dir)
tmp_dirname = os.path.split(tmp_dir)[1]
f = open(filename, 'w')
f.write(pdf)
f.close()
args = ["",
"-dNOPAUSE", "-dBATCH", "-dSAFER",
"-sDEVICE=png16m",
"-r150",
"-sOutputFile=%s" % os.path.join(tmp_dir, "page-%03d.png"),
"-f", filename
]
GS = ghostscript.Ghostscript(*args)
os.remove(filename)
filename = '%s.pdf' % self.get_filename(*args, **kwargs)
imgs = glob.glob(os.path.join(tmp_dir, '*.png'))
imgs = [os.path.split(i)[1] for i in imgs]
imgs = ['%stmp/%s/%s' % (MEDIA_URL, tmp_dirname, i) for i in imgs]
dictionary = {
'obj': obj,
'images': imgs,
'padeji': self.PADEJI,
'padeji_mnoj': self.PADEJI_MNOJ,
'url_prefix': self.URL_PREFIX,
'template_create_docs': self.TEMPLATE_CREATE_DOCS,
}
return render(self.request, self.TEMPLATE_IMG, dictionary)
except:
if DEBUG:
raise
else:
return HttpResponseServerError('Server error. Try later.')
def as_pdf(self, *args, **kwargs):
"""Вывести документ в формате PDF в HttpResponse."""
try:
pdf = self.get_pdf(*args, **kwargs)
filename = '%s.pdf' % self.get_filename(*args, **kwargs)
return pdf_to_response(pdf, filename)
except:
if DEBUG:
raise
else:
return HttpResponseServerError('Server error. Try later.')
def as_xls(self, *args, **kwargs):
"""Вывести документ в формате Excel в HttpResponse."""
try:
xls = self.get_xls(*args, **kwargs)
filename = '%s.xls' % self.get_filename(*args, **kwargs)
return xls_to_response(xls, filename)
except:
if DEBUG:
raise
else:
return HttpResponseServerError('Server error. Try later.')
def send_email(self, subject, to, body, files, sign_doc):
"""Отправка письма."""
dict_context = {'body': body, 'support_email': SUPPORT_EMAIL,}
email_body = render_to_string(self.EMAIL_MSG_TEMPLATE, dict_context)
attachments = []
for f in files:
attachments.append((smart_str(Header(f['filename'], 'cp1251')), f['content'], f['mimetype']))
email = EmailMessage(subject=subject, to=(to,), body=email_body, attachments=attachments)
return email.send()
def _process_email_form_and_send(self, form, *args, **kwargs):
"""Обработка формы отправки документа и отправка email-а."""
if form.cleaned_data['save_client_email']:
client = getattr(self.get_obj(kwargs['id']), 'client', None)
if client:
client.contact_email = form.cleaned_data['to'] # сохранить email клиента
client.save()
doc_format = form.cleaned_data['doc_format']
kwargs['doc_sign'] = form.cleaned_data.get('insert_sign', False)
if doc_format in ('pdf', 'xls',):
files = []
filename = self.get_filename(*args, **kwargs)
if doc_format == 'pdf':
files = [{
'filename': '%s.%s' % (filename, doc_format,),
'content': self.get_pdf(*args, **kwargs),
'mimetype': 'application/pdf',
},]
elif doc_format == 'xls':
files = [{
'filename': '%s.%s' % (filename, doc_format,),
'content': self.get_xls(*args, **kwargs),
'mimetype': 'application/ms-excel',
},]
return self.send_email(
subject = u'%s' % filename, # тема письма = имя файла без расширения
to = form.cleaned_data['to'],
body = form.cleaned_data['body'],
files = files,
sign_doc = form.cleaned_data.get('save_client_email', None)
)
return False # что-то пошло не так
@method_decorator(csrf_protect)
def email(self, *args, **kwargs):
"""Отправить документ на email аттачем в заданном формате."""
if self.request.method == 'POST' and '_cancel' in self.request.POST:
return redirect(self.REDIRECT_AFTER_EMAIL)
obj = self.get_obj(kwargs['id'])
if self.request.method == 'POST':
form = self.EMAIL_FORM_CLASS(data=self.request.POST)
if form.is_valid():
self._process_email_form_and_send(form, *args, **kwargs)
return redirect(self.REDIRECT_AFTER_EMAIL)
else:
initial = {}
client = getattr(self.get_obj(kwargs['id']), 'client', None)
if client:
initial['to'] = client.contact_email # подставить в форму email клиента
form = self.EMAIL_FORM_CLASS(initial=initial)
dictionary = {
'padeji': self.PADEJI,
'padeji_mnoj': self.PADEJI_MNOJ,
'url_prefix': self.URL_PREFIX,
'form_template': self.TEMPLATE_EMAIL_FORM,
'obj': obj,
'form': form,
}
return render(self.request, self.TEMPLATE_EMAIL, dictionary)
@method_decorator(require_POST)
@method_decorator(csrf_protect)
def email_ajax(self, *args, **kwargs):
"""Отправить документ на email аттачем в заданном формате - AJAX."""
if not self.request.is_ajax():
return HttpResponseBadRequest()
result = False
form = self.EMAIL_FORM_CLASS(data=self.request.POST)
if form.is_valid():
result = self._process_email_form_and_send(form, *args, **kwargs)
non_field_errors = form.non_field_errors()
if not form.is_valid():
non_field_errors.append(u'Заполните/исправьте выделенные поля.')
data = {
'success': form.is_valid(),
'field_errors': form.errors, # ошибки полей
'form_errors': non_field_errors, # ошибки формы
}
if form.is_valid() and result:
data['message'] = {'title': 'Инфо', 'msg': 'Письмо отправлено.',}
return HttpResponse(json.dumps(data), mimetype='application/json')
def get_ajax(self, *args, **kwargs):
"""Получить документ - AJAX."""
if not self.request.is_ajax():
return HttpResponseBadRequest()
obj = self.get_obj(kwargs['id'], only_form_fields=True)
data = json.dumps(obj, default=dthandler)
return HttpResponse(data, mimetype='application/json')
# -----------------------------------------------------------------------------
class BaseItemsViews(BaseViews):
"""Базовые views для документов с табличной частью."""
ITEM_MODEL = None # модель табличной части документа
ITEM_FORM_CLASS = None # форма табличной части документа
ITEM_FORM_PREFIX = None # префикс формы табличной части
# по какому полю суммировать табличную часть при показе списка документов
LIST_SUM_FIELD = None # None или строка
def __init__(self, request):
super(BaseItemsViews, self).__init__(request)
self.set_item_formset_class()
def asserts(self):
"""Проверить объект класса на типичные ошибки."""
super(BaseItemsViews, self).asserts()
assert self.ITEM_MODEL is not None, (u"%s.ITEM_MODEL can't be None!" % self.__class__.__name__)
assert self.ITEM_FORM_CLASS is not None, (u"%s.ITEM_FORM_CLASS can't be None!" % self.__class__.__name__)
def set_item_formset_class(self, extra_count=2):
"""Класс FormSet-а для табличной части документа."""
self.ITEM_FORMSET_CLASS = inlineformset_factory(
parent_model = self.MODEL,
model = self.ITEM_MODEL,
form = self.ITEM_FORM_CLASS,
extra=extra_count,
can_delete=True,
)
def get_list_qs(self):
"""QuerySet для просмотра списка документов.
Плюс суммирование табличной части по заданному полю.
"""
queryset = super(BaseItemsViews, self).get_list_qs()
if self.LIST_SUM_FIELD:
queryset = queryset.annotate(doc_sum = Sum(self.LIST_SUM_FIELD))
return queryset
def get_obj_items_qs(self, obj):
"""QuerySet табличной части документа."""
return self.ITEM_MODEL.objects.filter(parent=obj).select_related()
def update_parent_on_items_save(self, obj, obj_items):
"""Обновить родительскую модель."""
pass
@method_decorator(license_required)
@method_decorator(csrf_protect)
def add(self, *args, **kwargs):
"""Добавить документ.
Если при GET-запросе в kwargs передать initial и/или initial_items, то создаст предзаполненный документ.
"""
if self.request.method == 'POST' and '_cancel' in self.request.POST:
return redirect(self.REDIRECT_AFTER_ADD)
receiver_choice = None
sender_choice = None
if self.request.method == 'POST':
form = self.FORM_CLASS(self.request.user, data=self.request.POST)
formset = self.ITEM_FORMSET_CLASS(self.request.POST or None, prefix=self.ITEM_FORM_PREFIX)
receiver_choice = form.data.get('receiver_group', None)
sender_choice = form.data.get('sender_group', None)
if form.is_valid() and formset.is_valid():
new_obj = form.save(commit=False)
new_obj.user = self.request.user
new_obj.company = self.request.user.profile
new_obj.save()
# сохранить табличную часть
if formset.is_valid():
new_items = formset.save(commit=False)
for item in new_items:
item.parent = new_obj
item.save()
return redirect(self.REDIRECT_AFTER_ADD)
else:
initial = kwargs.get('initial') or self.init_form()
initial_items = kwargs.get('initial_items')
form = self.FORM_CLASS(self.request.user, initial=initial)
formset = self.ITEM_FORMSET_CLASS(prefix=self.ITEM_FORM_PREFIX, initial=initial_items)
dictionary = {
'padeji': self.PADEJI,
'padeji_mnoj': self.PADEJI_MNOJ,
'url_prefix': self.URL_PREFIX,
'form_template': self.TEMPLATE_FORM,
'form_template_js': self.TEMPLATE_FORM_JS,
'form': form,
'formset': formset,
'client_form': ClientForm(),
'receiver_choice': receiver_choice,
'sender_choice': sender_choice,
}
return render(self.request, self.TEMPLATE_ADD, dictionary)
def copy(self, *args, **kwargs):
"""Создать полную копию документа."""
source_id = kwargs['id']
obj = self.get_obj(source_id)
obj_items = self.get_obj_items_qs(obj)
kwargs['initial'] = model_to_dict(
obj,
fields=getattr(self.FORM_CLASS.Meta, 'fields', None),
exclude=getattr(self.FORM_CLASS.Meta, 'exclude', None)
)
kwargs['initial'].update(self.init_form())
# обязательно убрать ключи
kwargs['initial'].pop('pk', None)
kwargs['initial'].pop('id', None)
kwargs['initial'].pop('created_at', None)
kwargs['initial'].pop('updated_at', None)
if obj_items:
kwargs['initial_items'] = []
for item in obj_items:
d = model_to_dict(
item,
fields=getattr(self.ITEM_FORM_CLASS.Meta, 'fields', None),
exclude=getattr(self.ITEM_FORM_CLASS.Meta, 'exclude', None)
)
# обязательно убрать ключи
d.pop('pk', None)
d.pop('id', None)
d.pop('created_at', None)
d.pop('updated_at', None)
kwargs['initial_items'].append(d)
self.set_item_formset_class(extra_count=len(kwargs['initial_items']))
return self.add(self.request, *args, **kwargs)
@method_decorator(csrf_protect)
def edit(self, *args, **kwargs):
"""Редактировать документ."""
if self.request.method == 'POST' and '_cancel' in self.request.POST:
return redirect(self.REDIRECT_AFTER_EDIT)
obj = self.get_obj(kwargs['id'])
receiver_choice = 1
sender_choice = 1
if self.request.method == 'GET':
if hasattr(obj, 'user_is_sender'):
if obj.user_is_sender:
sender_choice = 'he_is'
elif obj.sender:
sender_choice = 'another'
else:
sender_choice = 'nobody'
if hasattr(obj, 'receiver') and hasattr(obj, 'sender'):
if obj.receiver == obj.client:
receiver_choice = 'he_is'
elif obj.receiver:
receiver_choice = 'another'
else:
receiver_choice = 'nobody'
formset = self.ITEM_FORMSET_CLASS(self.request.POST or None, prefix=self.ITEM_FORM_PREFIX, instance=obj)
form = self.FORM_CLASS(self.request.user, data=self.request.POST or None, instance=obj)
if form.is_valid() and formset.is_valid():
new_obj = form.save()
# По сути - для фактуры.
receiver = form.data.get('receiver_group', None)
if receiver == 'he_is':
new_obj.receiver = new_obj.client
elif receiver == 'nobody':
new_obj.receiver = None
sender = form.data.get('sender_group', None)
if sender == 'he_is':
new_obj.user_is_sender = True
new_obj.sender = None
elif sender == 'nobody':
new_obj.user_is_sender = False
new_obj.sender = None
elif sender == 'another':
new_obj.user_is_sender = False
new_obj.save()
# сохранить табличную часть
if formset.is_valid():
items = formset.save(commit=False)
for item in items:
item.parent = new_obj
item.save()
self.update_parent_on_items_save(new_obj, items)
return redirect(self.REDIRECT_AFTER_EDIT)
dictionary = {
'padeji': self.PADEJI,
'padeji_mnoj': self.PADEJI_MNOJ,
'url_prefix': self.URL_PREFIX,
'form_template': self.TEMPLATE_FORM,
'form_template_js': self.TEMPLATE_FORM_JS,
'obj': obj,
'form': form,
'formset': formset,
'client_form': ClientForm(),
'receiver_choice': receiver_choice,
'sender_choice': sender_choice,
}
return render(self.request, self.TEMPLATE_EDIT, dictionary)
def prepare(self, obj, obj_items, export_to=None):
"""Подмешать дополнительные поля к документу."""
pass
def get_pdf(self, *args, **kwargs):
"""Создать документ в PDF и вернуть как строку."""
obj = self.get_obj(kwargs['id'])
obj_items = self.get_obj_items_qs(obj)
profile = get_profile(obj.user)
main_account = BankAccount.objects.get_main(profile)
self.prepare(obj, obj_items, export_to='pdf')
params = {
'obj': obj,
'obj_items': obj_items,
'profile': profile,
'main_account': main_account,
'doc_sign': kwargs.get('doc_sign', True),
}
c1 = time()
pdf = render_pdf_to_string(self.request, self.PDF_TEMPLATE, params)
if DEBUG:
print '%s generation time (seconds): %s' % (self.PDF_TEMPLATE, time()-c1,)
return pdf
def get_xls(self, *args, **kwargs):
"""Создать документ в Excel и вернуть как строку."""
obj = self.get_obj(kwargs['id'])
obj_items = self.get_obj_items_qs(obj)
profile = get_profile(obj.user)
main_account = BankAccount.objects.get_main(profile)
self.prepare(obj, obj_items, export_to='xls')
params = {
'obj': obj,
'obj_items': obj_items,
'profile': profile,
'main_account': main_account,
}
c1 = time()
xls = render_xls_to_string(self.request, self.XLS_TEMPLATE, params)
if DEBUG:
print '%s generation time (seconds): %s' % (self.XLS_TEMPLATE, time()-c1,)
return xls