Compare commits

..

4 Commits

Author SHA1 Message Date
Air51 21bdc8fcff Merged Air51/dokumentor.ru into master 9 years ago
Andrey 02d89b863e changed company info on bill.xls 9 years ago
Air51 063c6f8a61 Merged Air51/dokumentor.ru into master 9 years ago
Andrey 354eb753d8 Revert "Merge branch 'mitri4' into test" 9 years ago
  1. 3
      .bowerrc
  2. 15
      .dockerignore
  3. 58
      .editorconfig
  4. 28
      .eslintrc.js
  5. 17
      .gitattributes
  6. 213
      .gitignore
  7. 106
      Dockerfile
  8. 106
      Dockerfile-prod
  9. 134
      Makefile
  10. 8
      README
  11. 61
      README.md
  12. 33
      bin/loaddata.sh
  13. 25
      bin/uploaddata.sh
  14. 30
      bower.json
  15. 9
      conf/compose/Dockerfile
  16. 6
      conf/deploy/common.py
  17. 4
      conf/deploy/develop.py
  18. 5
      conf/deploy/prod.py
  19. 5
      conf/deploy/stage.py.example
  20. 25
      conf/docker/entrypoint_prod.sh
  21. 29
      conf/docker/entrypoint_stage.sh
  22. 24
      conf/env.local
  23. 27
      conf/env.stage
  24. 24
      conf/env.template
  25. 13
      conf/flower_conf.py
  26. 44
      conf/gunicorn_logging.ini
  27. 67
      conf/gunicorn_prod.py
  28. 66
      conf/nginx.conf
  29. 29
      conf/nginx_vds_develop.conf
  30. 41
      conf/nginx_vds_prod_ssl.conf
  31. 29
      conf/nginx_vds_stage.conf
  32. 95
      conf/ssl-keys/dokumentor.ru.crt
  33. 51
      conf/ssl-keys/dokumentor.ru.key
  34. 56
      conf/supervisor.conf
  35. 19
      diff_static.sh
  36. 66
      docker-compose.develop.yml
  37. 56
      docker-compose.local.yml
  38. 66
      docker-compose.stage.yml
  39. 68
      docker-compose.yml
  40. BIN
      extra/boss_sign.png
  41. BIN
      extra/gb_sign.png
  42. BIN
      extra/stamp.png
  43. 83
      fabfile.py
  44. 1
      log/.gitignore
  45. 10
      manage.py
  46. 4
      project/__init__.py
  47. 0
      project/callback/__init__.py
  48. 5
      project/callback/admin.py
  49. 0
      project/callback/context_processors.py
  50. 19
      project/callback/forms.py
  51. 44
      project/callback/models.py
  52. 9
      project/callback/urls.py
  53. 21
      project/callback/views.py
  54. 24
      project/celery.py
  55. 0
      project/commons/__init__.py
  56. 27
      project/commons/forms.py
  57. 3
      project/commons/models.py
  58. 17
      project/commons/paginator.py
  59. 30
      project/commons/pdf_tools.py
  60. 0
      project/commons/templatetags/__init__.py
  61. 52
      project/commons/templatetags/my_tags.py
  62. 0
      project/commons/tests.py
  63. 19
      project/commons/utils.py
  64. 1
      project/commons/views.py
  65. 3
      project/commons/xls/__init__.py
  66. 38
      project/commons/xls/get_xlwt_style_list.py
  67. 90
      project/commons/xls/useful_tools.py
  68. 12
      project/commons/xls/xls_to_response.py
  69. 2
      project/customer/__init__.py
  70. 67
      project/customer/admin.py
  71. 32
      project/customer/consts.py
  72. 41
      project/customer/context_processors.py
  73. 2
      project/customer/decorators.py
  74. 281
      project/customer/forms.py
  75. 21
      project/customer/managers.py
  76. 13
      project/customer/middleware.py
  77. 289
      project/customer/migrations/0001_initial.py
  78. 147
      project/customer/migrations/0002_auto__add_field_userprofilefilters_show_logo.py
  79. 148
      project/customer/migrations/0003_auto__add_field_userprofilefilters_show_address.py
  80. 156
      project/customer/migrations/0004_auto__del_field_client_contact_icq__add_field_client_ogrn.py
  81. 149
      project/customer/migrations/0005_auto__add_field_bankaccount_short_name.py
  82. 0
      project/customer/migrations/__init__.py
  83. 516
      project/customer/models.py
  84. 33
      project/customer/tasks.py
  85. 16
      project/customer/tests.py
  86. 63
      project/customer/urls.py
  87. 86
      project/customer/utils.py
  88. 0
      project/customer/views/__init__.py
  89. 9
      project/customer/views/bank_accounts.py
  90. 91
      project/customer/views/bank_accounts_ajax.py
  91. 26
      project/customer/views/clients.py
  92. 79
      project/customer/views/clients_ajax.py
  93. 105
      project/customer/views/docs.py
  94. 161
      project/customer/views/license.py
  95. 280
      project/customer/views/profile.py
  96. 37
      project/customer/views/profile_ajax.py
  97. 0
      project/docs/__init__.py
  98. 30
      project/docs/admin.py
  99. 1
      project/docs/as_xls/__init__.py
  100. 206
      project/docs/as_xls/render_to_xls.py
  101. Some files were not shown because too many files have changed in this diff Show More

@ -1,3 +0,0 @@
{
"directory": "static/vendor"
}

@ -1,15 +0,0 @@
/env
/compose
/public
/node_modules
/bower_components
/tmp_emails
/tmp
/var
/db
/docker-compose.yml
/Dockerfile
/README.md
/db.sqlite3
/.vscode/
/upload/

@ -1,58 +0,0 @@
# EditorConfig is awesome: http://EditorConfig.org
# Howto with your editor: http://editorconfig.org/#download
# Sublime: https://github.com/sindresorhus/editorconfig-sublime
# top-most EditorConfig file
root = true
# Unix-style newlines with a newline ending every file
[**]
end_of_line = lf
insert_final_newline = true
# Standard at: https://github.com/felixge/node-style-guide
[**.{js,json}]
trim_trailing_whitespace = true
indent_style = space
indent_size = 2
quote_type = single
curly_bracket_next_line = false
spaces_around_operators = true
space_after_control_statements = true
space_after_anonymous_functions = true
spaces_in_brackets = false
# No Standard. Please document a standard if different from .js
[**.{yml,css}]
trim_trailing_whitespace = true
indent_style = tab
[**.html]
trim_trailing_whitespace = true
indent_style = space
indent_size = 2
# No standard. Please document a standard if different from .js
[**.md]
indent_style = tab
[**.py]
indent_style = space
indent_size = 4
trim_trailing_whitespace = true
# Standard at:
[Makefile]
indent_style = tab
[**.yml]
trim_trailing_whitespace = true
indent_style = space
indent_size = 4
# The indentation in package.json will always need to be 2 spaces
# https://github.com/npm/npm/issues/4718
[{package, bower}.json]
indent_style = space
indent_size = 2

@ -1,28 +0,0 @@
var OFF = 0, WARN = 1, ERROR = 2;
module.exports = exports = {
"root": true,
env: {
'es6': true, // We are writing ES6 code
'browser': true, // for the browser
'commonjs': true // and use require() for stylesheets
},
"parserOptions": {
"ecmaFeatures": {
"modules": true
},
"sourceType": "module"
},
"rules":{
"no-console": OFF,
"jsx-quotes": WARN,
"no-unused-vars": WARN,
"no-undef": WARN
},
"globals": {
"$": true
},
"extends": ["eslint:recommended"]
};

17
.gitattributes vendored

@ -1,17 +0,0 @@
# Auto detect text files and perform LF normalization
* text=auto
# Custom for Visual Studio
*.cs diff=csharp
# Standard to msysgit
*.doc diff=astextplain
*.DOC diff=astextplain
*.docx diff=astextplain
*.DOCX diff=astextplain
*.dot diff=astextplain
*.DOT diff=astextplain
*.pdf diff=astextplain
*.PDF diff=astextplain
*.rtf diff=astextplain
*.RTF diff=astextplain

213
.gitignore vendored

@ -1,204 +1,15 @@
celerybeat-schedule
/tmp_emails/
/tmp/
/db/
/public/
conf/env
!local.py
!parts
!.gitkeep
!form.html
/static/vendor
/data/
/.vscode/
######################All system################################################
# Compiled source #
###################
*.com
*.class
*.dll
*.exe
*.o
*.so
# Packages #
############
# it's better to unpack these files and commit the raw source
# git has its own built in compression methods
*.7z
*.dmg
*.gz
*.iso
*.jar
*.rar
*.tar
*.zip
# Logs and databases #
######################
*.log
*.sql
*.sqlite
*.sqlite3
*.fdb
*.gdb
# OS generated files #
######################
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
ehthumbs.db
Thumbs.db
################################################################################
####################JetBrains###################################################
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio
*.iml
## Directory-based project format:
.idea/
# if you remove the above rule, at least ignore the following:
# User-specific stuff:
# .idea/workspace.xml
# .idea/tasks.xml
# .idea/dictionaries
# Sensitive or high-churn files:
# .idea/dataSources.ids
# .idea/dataSources.xml
# .idea/sqlDataSources.xml
# .idea/dynamic.xml
# .idea/uiDesigner.xml
# Gradle:
# .idea/gradle.xml
# .idea/libraries
# Mongo Explorer plugin:
# .idea/mongoSettings.xml
## File-based project format:
*.ipr
*.iws
## Plugin-specific files:
# IntelliJ
/out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
################################################################################
################## OSX.gitignore ###############################################
*.*~
*.pyc
.DS_Store
.AppleDouble
.LSOverride
# Icon must end with two \r
Icon
# Thumbnails
._*
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
################################################################################
#######################Python.gitignore#########################################
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
env/
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
#lib
lib64/
parts/
sdist/
*.egg-info/
.installed.cfg
*.egg
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*,cover
# Translations
*.mo
*.pot
# Sphinx documentation
docs/_build/
# PyBuilder
target/
################################################################################
##############################Sass.gitignore####################################
.sass-cache/
*.css.map
################################################################################
/upload/
/conf/deploy/develop.py
/conf/deploy/prod.py
!/templates/docs/parts/
ENV/
.idea/
local_settings.py
Thumbs.db
distribute-*.tar.gz
*.bak
*.swp
_public_html/
celerybeat-schedule
yandex_money.log

@ -1,106 +0,0 @@
FROM alpine:latest
ENV NGINX_VERSION="1.13.2" \
NGINX_OPTS="--with-http_ssl_module \
--with-http_gzip_static_module \
--prefix=/usr/share/nginx \
--sbin-path=/usr/sbin/nginx \
--conf-path=/etc/nginx/nginx.conf \
--pid-path=/var/run/nginx.pid \
--http-log-path=/var/log/nginx/access.log \
--error-log-path=/var/log/nginx/error.log \
--user=nginx \
--group=nginx \
--add-module=/tmp/modules/nginx_requestid-master"
COPY requirements /opt/app/requirements
RUN apk update && apk add --update --no-cache --progress \
make \
pcre \
zlib \
libpq \
openssl \
ca-certificates \
python3 \
libxml2-dev \
py-libxml2 \
libxml2-utils \
libxslt-dev \
libmagic \
ghostscript \
supervisor \
cyrus-sasl-dev \
jpeg libpng freetype \
bash bash-completion \
gettext-dev \
libtool \
mailcap \
libgomp \
jpeg-dev \
expat \
tcl-dev \
tk-dev \
&& apk add --no-cache --virtual=.build-dependencies \
musl-dev \
curl \
build-base \
postgresql-dev \
ncurses-dev \
pcre-dev \
zlib-dev \
libpng-dev \
tiff-dev \
lcms2-dev \
libwebp-dev \
libffi-dev \
ttf-freefont \
python3-dev \
&& python3 -m ensurepip \
&& rm -r /usr/lib/python*/ensurepip \
&& pip3 install --upgrade pip setuptools \
&& pip3 install --no-cache-dir -r /opt/app/requirements/production.txt \
# Add nginx group and user
&& addgroup -S nginx \
&& adduser -S nginx -G nginx \
# Download additional nginx modules
&& mkdir -p /tmp/modules \
&& cd /tmp/modules \
&& wget -O nginx-requestid.tar.gz https://github.com/hhru/nginx_requestid/archive/master.tar.gz \
&& tar xvzf nginx-requestid.tar.gz \
# Download and compile nginx
&& cd /tmp \
&& curl -fSL http://nginx.org/download/nginx-${NGINX_VERSION}.tar.gz -o nginx-${NGINX_VERSION}.tar.gz \
&& tar xzvf nginx-${NGINX_VERSION}.tar.gz \
&& cd nginx-${NGINX_VERSION} \
&& ./configure ${NGINX_OPTS} \
&& make \
&& make install \
# Delete build dependencies after use
&& cd /tmp \
&& apk del .build-dependencies \
&& rm -rf \
modules \
nginx-${NGINX_VERSION} \
nginx-${NGINX_VERSION}.tar.gz \
/var/cache/apk/* \
/root/.cache \
# Security precautions
&& openssl rand -base64 32 > /opt/app/secret_key.txt \
&& echo "root:$(openssl passwd -apr1 uztOsdwP)" >> /etc/nginx/.htpasswd
#COPY . /opt/app
#VOLUME ["/opt/app/public/media"]
WORKDIR /opt/app
RUN rm -v /etc/nginx/nginx.conf
ADD /conf/nginx.conf /etc/nginx/
EXPOSE 80
CMD ["conf/docker/entrypoint_stage.sh"]

@ -1,106 +0,0 @@
FROM alpine:latest
ENV NGINX_VERSION="1.13.2" \
NGINX_OPTS="--with-http_ssl_module \
--with-http_gzip_static_module \
--prefix=/usr/share/nginx \
--sbin-path=/usr/sbin/nginx \
--conf-path=/etc/nginx/nginx.conf \
--pid-path=/var/run/nginx.pid \
--http-log-path=/var/log/nginx/access.log \
--error-log-path=/var/log/nginx/error.log \
--user=nginx \
--group=nginx \
--add-module=/tmp/modules/nginx_requestid-master"
COPY requirements /opt/app/requirements
RUN apk update && apk add --update --no-cache --progress \
make \
pcre \
zlib \
libpq \
openssl \
ca-certificates \
python3 \
libxml2-dev \
py-libxml2 \
libxml2-utils \
libxslt-dev \
libmagic \
ghostscript \
supervisor \
cyrus-sasl-dev \
jpeg libpng freetype \
bash bash-completion \
gettext-dev \
libtool \
mailcap \
libgomp \
jpeg-dev \
expat \
tcl-dev \
tk-dev \
&& apk add --no-cache --virtual=.build-dependencies \
musl-dev \
curl \
build-base \
postgresql-dev \
ncurses-dev \
pcre-dev \
zlib-dev \
libpng-dev \
tiff-dev \
lcms2-dev \
libwebp-dev \
libffi-dev \
ttf-freefont \
python3-dev \
&& python3 -m ensurepip \
&& rm -r /usr/lib/python*/ensurepip \
&& pip3 install --upgrade pip setuptools \
&& pip3 install --no-cache-dir -r /opt/app/requirements/production.txt \
# Add nginx group and user
&& addgroup -S nginx \
&& adduser -S nginx -G nginx \
# Download additional nginx modules
&& mkdir -p /tmp/modules \
&& cd /tmp/modules \
&& wget -O nginx-requestid.tar.gz https://github.com/hhru/nginx_requestid/archive/master.tar.gz \
&& tar xvzf nginx-requestid.tar.gz \
# Download and compile nginx
&& cd /tmp \
&& curl -fSL http://nginx.org/download/nginx-${NGINX_VERSION}.tar.gz -o nginx-${NGINX_VERSION}.tar.gz \
&& tar xzvf nginx-${NGINX_VERSION}.tar.gz \
&& cd nginx-${NGINX_VERSION} \
&& ./configure ${NGINX_OPTS} \
&& make \
&& make install \
# Delete build dependencies after use
&& cd /tmp \
&& apk del .build-dependencies \
&& rm -rf \
modules \
nginx-${NGINX_VERSION} \
nginx-${NGINX_VERSION}.tar.gz \
/var/cache/apk/* \
/root/.cache \
# Security precautions
&& openssl rand -base64 32 > /opt/app/secret_key.txt \
&& echo "root:$(openssl passwd -apr1 uztOsdwP)" >> /etc/nginx/.htpasswd
#COPY . /opt/app
#VOLUME ["/opt/app/public/media"]
WORKDIR /opt/app
RUN rm -v /etc/nginx/nginx.conf
ADD /conf/nginx.conf /etc/nginx/
EXPOSE 80
CMD ["conf/docker/entrypoint_stage.sh"]

@ -1,134 +0,0 @@
.PHONY:
all
help
build
virtualenv
requirements-local
loaddata
run
migrate
shell
collectstatic
clean
worker
qa
flush
update-develop
upgrade-develop
update-develop
upgrade-develop
update-stage
upgrade-stage
# target: all - Default target. Does nothing.
all:
@clear
@echo "Hello $(LOGNAME), nothing to do by default"
@echo "Try 'make help'"
# target: help - Display callable targets.
help:
@clear
@egrep "^# target:" [Mm]akefile
# target: build - Build all docker containers, defined in docker-compose.yml
build:
docker-compose build
# target: virtualenv - Make virtualenv
virtualenv:
virtualenv -p python3 env --no-site-packages
# target: requirements-local - Install requirements for local develop
requirements-local:
$(PIP) install -r requirements/local.txt
# target: loaddata - Load fixtures
loaddata:
python3 manage.py loaddata src/myauth/fixtures/myauth.json
python3 manage.py loaddata src/commons/fixtures/cms.json
python3 manage.py loaddata src/commons/fixtures/djangocms_text_ckeditor.json
python3 manage.py loaddata src/commons/fixtures/sites.json
python3 manage.py loaddata src/customer/fixtures/price.json
python3 manage.py loaddata src/docs/fixtures/country.json
python3 manage.py loaddata src/docs/fixtures/currency.json
python3 manage.py loaddata src/docs/fixtures/measure.json
# target: run - Runserver
run:
python3 manage.py runserver 0.0.0.0:8000
# target: migrate - Build all docker containers, defined in docker-compose.stage.yml
migrate:
python3 manage.py migrate myauth --noinput
python3 manage.py migrate --noinput
# target: shell - Run python shell
shell:
python3 manage.py shell
# target: collectstatic - Run collectstatic
collectstatic:
python3 manage.py collectstatic --noinput
clean_temp:
find . -name '*.pyc' -delete
rm -rf .coverage dist docs/_build htmlcov MANIFEST
rm -rf media/
clean_db:
find . -name '*.sqlite3' -delete
clean_venv:
rm -rf $(VENV_DIR)
# target: clean - Clean all temp files
clean: clean_temp clean_venv clean_db
# target: worker - Run celery worker
worker:
celery -A src.dokumentor worker -l info -E -B
# target: flower - Run celery flower on port 5555
flower:
celery -A src.dokumentor flower -l info -E -B --port=5555
# target: qa - Run pytest
qa:
pytest
# target: flush - clean database
flush:
python3 manage.py flush
# target: update-develop - Run push change in repo develop branch and deploy for develop server with restart only web container
update-develop:
git push origin develop
fab develop update
# target: upgrade-develop - Run push change in repo develop branch and deploy for develop server with rebuild container
upgrade-develop:
git push origin develop
fab develop upgrade
# target: update-stage - Run push change in repo master branch and deploy for stage server with restart only web container
update-stage:
git push origin prod
fab stage update
# target: upgrade-stage - Run push change in repo master branch and deploy for stage server with rebuild container
upgrade-stage:
git push origin prod
fab stage upgrade
# target: update-prod - Run push change in repo master branch and deploy for prod server with restart only web container
update-prod:
git push origin prod
fab prod update
# target: upgrade-prod - Run push change in repo master branch and deploy for prod server with rebuild container
upgrade-prod:
git push origin prod
fab prod upgrade

@ -0,0 +1,8 @@
Документор
===
Если не работает локаль ru в autocomplete_light, то значит надо её скомпилировать:
msgfmt django.po --output-file django.mo

@ -1,61 +0,0 @@
# Dokumentor
Сервис для создания и хранения первичных бухгалтерских документов.
## Установка dev версии
Клонируем проект
```bash
git clone https://bitbucket.org/Air51/dokumentor_dev dokumentor
```
Переходим в папку проекта
```bash
cd dokumentor
```
Пеерключаемся в ветку develop
```bash
git checkout develop
```
### Установка проекта для разработки вручную
Создаем окружение и ставим зависимости
```bash
virtualenv --python=python3 env
source env/bin/activate
pip install -r requirements/local.txt
```
### Настройка окружения
Копируем файл с переменными окружения
```bash
cp conf/env.template conf/env
```
и прописывем в `conf/env` свои данные
Накатываем миграции и заполняем базу данных
```bash
make migrate
```
Загружаем фикстуры
```bash
make loaddata
```
### Установка проекта для разработки
Для разработки проект можно развернуть выполив
```bash
make run
```
###Запуск сервера Celery
```bash
make worker
```
###Запуск Тестов
```bash
make qa
```

@ -1,33 +0,0 @@
#!/usr/bin/env bash
# dictionaries
python3 manage.py loaddata src/myauth/fixtures/myauth.json
python3 manage.py loaddata src/commons/fixtures/cms.json
python3 manage.py loaddata src/commons/fixtures/djangocms_text_ckeditor.json
python3 manage.py loaddata src/commons/fixtures/sites.json
python3 manage.py loaddata src/customer/fixtures/price.json
python3 manage.py loaddata src/docs/fixtures/country.json
python3 manage.py loaddata src/docs/fixtures/currency.json
python3 manage.py loaddata src/docs/fixtures/measure.json
# customer
python3 manage.py loaddata upload/customer.userprofile.json
python3 manage.py loaddata upload/customer.bankaccount.json
python3 manage.py loaddata upload/customer.client.json
python3 manage.py loaddata upload/customer.license.json
# myauth
python3 manage.py loaddata upload/myauth.json
# docs
python3 manage.py loaddata upload/docs.invoice.json
python3 manage.py loaddata upload/docs.invoiceitem.json
python3 manage.py loaddata upload/docs.aktrabot.json
python3 manage.py loaddata upload/docs.aktrabotitem.json
python3 manage.py loaddata upload/docs.aktsverki.json
python3 manage.py loaddata upload/docs.aktsverkiitem.json
python3 manage.py loaddata upload/docs.dover.json
python3 manage.py loaddata upload/docs.doveritem.json
python3 manage.py loaddata upload/docs.nakladn.json
python3 manage.py loaddata upload/docs.nakladnitem.json
python3 manage.py loaddata upload/docs.platejka.json
python3 manage.py loaddata upload/docs.faktura.json
python3 manage.py loaddata upload/docs.fakturaitem.json
# callback
python3 manage.py loaddata upload/callback.json

@ -1,25 +0,0 @@
#!/usr/bin/env bash
mkdir upload
# myauth
python manage.py dumpdata myauth --indent=4 > upload/myauth.json
# customer
python manage.py dumpdata customer.userprofile --indent=4 > upload/customer.userprofile.json
python manage.py dumpdata customer.bankaccount --indent=4 > upload/customer.bankaccount.json
python manage.py dumpdata customer.client --indent=4 > upload/customer.client.json
python manage.py dumpdata customer.license --indent=4 > upload/customer.license.json
# docs
python manage.py dumpdata docs.aktrabot --indent=4 > upload/docs.aktrabot.json
python manage.py dumpdata docs.aktrabotitem --indent=4 > upload/docs.aktrabotitem.json
python manage.py dumpdata docs.aktsverki --indent=4 > upload/docs.aktsverki.json
python manage.py dumpdata docs.aktsverkiitem --indent=4 > upload/docs.aktsverkiitem.json
python manage.py dumpdata docs.dover --indent=4 > upload/docs.dover.json
python manage.py dumpdata docs.doveritem --indent=4 > upload/docs.doveritem.json
python manage.py dumpdata docs.nakladn --indent=4 > upload/docs.nakladn.json
python manage.py dumpdata docs.nakladnitem --indent=4 > upload/docs.nakladnitem.json
python manage.py dumpdata docs.platejka --indent=4 > upload/docs.platejka.json
python manage.py dumpdata docs.invoice --indent=4 > upload/docs.invoice.json
python manage.py dumpdata docs.invoiceitem --indent=4 > upload/docs.invoiceitem.json
python manage.py dumpdata docs.faktura --indent=4 > upload/docs.faktura.json
python manage.py dumpdata docs.fakturaitem --indent=4 > upload/docs.fakturaitem.json
# callback
python manage.py dumpdata callback --indent=4 > upload/callback.json

@ -1,30 +0,0 @@
{
"name": "dokumentor",
"authors": [
"Dmitriy Shesterkin <mitri4@bk.ru>"
],
"description": "",
"main": "",
"license": "MIT",
"homepage": "",
"ignore": [
"**/.*",
"node_modules",
"bower_components",
"static/vendor",
"test",
"tests"
],
"dependencies": {
"Croppie": "croppie#^2.4.1",
"handlebars": "^4.0.10",
"jquery.cookie": "^1.4.1",
"suggestions.jquery": "^16.10.3",
"jquery": "1.10.2",
"jquery-ui": "1.10.3",
"blockUI": "*",
"django-dynamic-formset": "*",
"jquery-form": "3.37.0",
"list.js": "^1.5.0"
}
}

@ -1,9 +0,0 @@
FROM mhart/alpine-node:6.2.0
RUN apk --update add git nodejs \
&& rm -rf /var/cache/apk/* \
&& npm install -g bower && \
echo '{ "allow_root": true }' > /root/.bowerrc
WORKDIR /opt/app
CMD ["/bin/sh"]

@ -1,6 +0,0 @@
# -*- coding: utf-8 -*-
PROJECT_NAME = 'Dokumentor'
PROJECT_NAME_DEVELOP = 'dokumentor'
REPO = 'git@bitbucket.org:Air51/dokumentor_dev.git'
BRANCH_PROD = 'prod'
BRANCH_DEVELOP = 'develop'

@ -1,4 +0,0 @@
# -*- coding: utf-8 -*-
USER = 'mitri4'
PASS = '9091324913Dasha'
HOSTS = ['lets-dev.ru:10022']

@ -1,5 +0,0 @@
# -*- coding: utf-8 -*-
USER = 'root'
PASS = 'fS0h7hC87dCY'
HOSTS = ['185.204.0.134:22']

@ -1,5 +0,0 @@
# -*- coding: utf-8 -*-
USER = 'username'
PASS = 'password'
HOSTS = ['domain_name:ssh_port']

@ -1,25 +0,0 @@
#!/usr/bin/env bash
set -e
set -u
export ENV=prod
export BRANCH=prod
cd /opt/app
# Collect static files
echo "Collect static files"
python3 manage.py collectstatic --noinput
# Make database migrations
echo "Make database migrations"
python3 manage.py makemigrations
# Apply database migrations
echo "Apply database migrations"
python3 manage.py migrate myauth --noinput
python3 manage.py migrate --noinput
supervisord -c /opt/app/conf/supervisor.conf

@ -1,29 +0,0 @@
#!/usr/bin/env bash
set -e
set -u
export ENV=stage
export BRANCH=develop
cd /opt/app
# Collect static files
echo "Collect static files"
python3 manage.py collectstatic --noinput
# Make database migrations
echo "Make database migrations"
python3 manage.py makemigrations
# Apply database migrations
echo "Apply database migrations"
python3 manage.py migrate myauth --noinput
python3 manage.py migrate --noinput
#pip3 install -r requirements/test.txt
#python3 manage.py dummydata --number 100 # Load dummy data for testing interface
supervisord -c /opt/app/conf/supervisor.conf

@ -1,24 +0,0 @@
DJANGO_SETTINGS_MODULE='src.dokumentor.settings.local'
DJANGO_SECRET='CHANGE_ME_IN_PRODUCTION'
SSL=False
DJANGO_DB='postgres://dokumentor:dokumentor@localhost:5432/dokumentor'
DJANGO_TEST_DB='sqlite:////Users/mitri4/Projects/dokumentor/test_db.sqlite3'
DJANGO_FROM_EMAIL='root@localhost'
DJANGO_EMAIL_HOST=''
DJANGO_EMAIL_PORT=''
DJANGO_EMAIL_USER=''
DJANGO_EMAIL_PASSWORD=''
DJANGO_EMAIL_USE_TLS=False
DJANGO_EMAIL_USE_SSL=False
REDIS_URL='redis://localhost:6379/1'
CELERY_BROKER_URL ='amqp://guest:guest@localhost:5672//'
CELERY_RESULT_BACKEND ='amqp://guest:guest@localhost:5672//'
CALLBACK_EMAIL_SENDER='root@localhost'
CALLBACK_MANAGERS_EMAILS=('mitri4@bk.ru', 'dmitriy.shesterkin@gmail.com')

@ -1,27 +0,0 @@
DJANGO_SETTINGS_MODULE=src.dokumentor.settings.stage
SSL=False
DJANGO_SECRET='ewfrevwavrvq3tg4wvf3tvw4ug97hf3t48w7hfy3rf32'
DJANGO_DB='postgres://dokumentor:dokumentor@db:5432/dokumentor'
# Flower settings
FLOWER_PORT=5555
FLOWER_BASIC_AUTH=dokumentor:dokumentor
DJANGO_FROM_EMAIL='Открытые технологии <no-reply@o-tech.io>'
DJANGO_EMAIL_HOST='smtp.yandex.ru'
DJANGO_EMAIL_PORT=465
DJANGO_EMAIL_USER='no-reply@o-tech.io'
DJANGO_EMAIL_PASSWORD='BA4BnshqVz3Hae'
DJANGO_EMAIL_USE_TLS=False
DJANGO_EMAIL_USE_SSL=True
REDIS_URL='redis://redis:6379/1'
CELERY_BROKER_URL = 'amqp://dokumentor:dokumentor@rabbitmq:5672//'
CELERY_RESULT_BACKEND = 'amqp://dokumentor:dokumentor@rabbitmq:5672//'
CALLBACK_EMAIL_SENDER='Документор <no-reply@o-tech.io>'
CALLBACK_MANAGERS_EMAILS=('mitri4@bk.ru', 'alexander.time@gmail.com','dmitriy.shesterkin@gmail.com')

@ -1,24 +0,0 @@
DJANGO_SETTINGS_MODULE=src.dokumentor.settings.{{env}}
DJANGO_SECRET='CHANGE_ME_IN_PRODUCTION'
# Flower settings
FLOWER_PORT=5555
FLOWER_BASIC_AUTH=user:password
DJANGO_DB='postgres://USER:PASSWORD@HOST:PORT/NAME'
DJANGO_FROM_EMAIL='root@localhost'
DJANGO_EMAIL_HOST=''
DJANGO_EMAIL_PORT=''
DJANGO_EMAIL_USER=''
DJANGO_EMAIL_PASSWORD=''
DJANGO_EMAIL_USE_TLS=False
DJANGO_EMAIL_USE_SSL=False
CELERY_BROKER_URL='amqp://guest:guest@localhost:5672//'
CELERY_RESULT_BACKEND='amqp://guest:guest@localhost:5672//'
CALLBACK_EMAIL_SENDER=''
CALLBACK_MANAGERS_EMAILS=()

@ -1,13 +0,0 @@
# -*- coding: utf-8 -*-
import os
import envvars as e
conf_dir = os.path.normpath(os.path.join(os.path.dirname(__file__), '..'))
e.load(os.path.join(conf_dir, 'conf/env'))
port = e.get('FLOWER_PORT')
broker = e.get('CELERY_BROKER_URL')
basic_auth = [e.get('FLOWER_BASIC_AUTH')]

@ -1,44 +0,0 @@
[loggers]
keys=root,gunicorn.access,gunicorn.error
[logger_root]
level=INFO
handlers=root
[logger_gunicorn.access]
level=INFO
handlers=gunicorn.access
qualname=gunicorn.access
propagate=0
[logger_gunicorn.error]
level=INFO
handlers=gunicorn.error
qualname=gunicorn.error
propagate=0
[handlers]
keys=root,gunicorn.access,gunicorn.error
[handler_root]
class=logging.StreamHandler
formatter=default
args=(sys.stdout,)
[handler_gunicorn.access]
class=logging.StreamHandler
formatter=default
args=(sys.stdout,)
[handler_gunicorn.error]
class=logging.StreamHandler
formatter=default
args=(sys.stdout,)
[formatters]
keys=default
[formatter_default]
class=logging.Formatter
format=* %(asctime)s [%(levelname)s] {%(filename)s} - %(message)s
datefmt=%x %X

@ -1,67 +0,0 @@
# Settings http://docs.gunicorn.org/en/stable/settings.html
import os
bind = 'unix:/tmp/gunicorn.sock'
backlog = 2048 # The number of pending connections
preload = True # Load application code before the worker processes are forked
workers = 2
worker_class = 'sync'
worker_connections = 1000
threads = 1
timeout = 120
keepalive = 2
reload = False
spew = False
check_config = False
daemon = False
pidfile = None
umask = 0
user = None
group = None
tmp_upload_dir = None
proc_name = None
# Logging
# -------
logconfig = '/opt/app/conf/gunicorn_logging.ini'
def post_fork(server, worker):
server.log.info("Worker spawned (pid: %s)", worker.pid)
def pre_fork(server, worker):
pass
def pre_exec(server):
server.log.info("Forked child, re-executing.")
def when_ready(server):
server.log.info("Server is ready. Spawning workers")
def worker_int(worker):
worker.log.info("Worker received INT or QUIT signal")
## get traceback info
import threading, sys, traceback
id2name = dict([(th.ident, th.name) for th in threading.enumerate()])
code = []
for threadId, stack in sys._current_frames().items():
code.append("\n# Thread: %s(%d)" % (id2name.get(threadId,""),
threadId))
for filename, lineno, name, line in traceback.extract_stack(stack):
code.append('File: "%s", line %d, in %s' % (filename,
lineno, name))
if line:
code.append(" %s" % (line.strip()))
worker.log.debug("\n".join(code))
def worker_abort(worker):
worker.log.info("Worker received SIGABRT signal")

@ -1,66 +0,0 @@
worker_processes 1;
user nginx;
pid /var/run/nginx.pid;
error_log /var/log/nginx/error.log;
events {
worker_connections 1024;
accept_mutex off;
use epoll;
}
http {
sendfile on;
include /etc/nginx/mime.types;
default_type application/octet-stream;
keepalive_timeout 65;
upstream django {
server unix:/tmp/gunicorn.sock fail_timeout=0;
}
server {
listen 80;
server_name _;
charset utf-8;
keepalive_timeout 5;
client_max_body_size 64M;
access_log /var/log/nginx/access.log;
location /media {
alias /opt/app/public/media;
expires 30d;
}
location /static {
alias /opt/app/public/static;
expires 30d;
}
location /favicon.ico {
alias /opt/app/public/static/favicon.ico;
expires 30d;
}
location / {
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Scheme $scheme;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# we don't want nginx trying to do something clever with
# redirects, we set the Host: header above already.
proxy_redirect off;
proxy_connect_timeout 600;
proxy_send_timeout 600;
proxy_read_timeout 600;
send_timeout 600;
proxy_http_version 1.1;
proxy_pass http://django;
proxy_pass_header Server;
}
}
}

@ -1,29 +0,0 @@
upstream dokumentor-dev {
server localhost:32768;
}
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
server {
client_max_body_size 175M;
listen 0.0.0.0:80;
server_name www.develop.dokumentor.lets-dev.ru develop.dokumentor.lets-dev.ru;
location / {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-NginX-Proxy true;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_http_version 1.1;
proxy_pass http://dokumentor-dev/;
proxy_redirect off;
}
}

@ -1,41 +0,0 @@
upstream dokumentor-master {
server localhost:32770;
}
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
server {
server_name www.dokumentor.ru dokumentor.ru;
listen 80;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
server_name www.dokumentor.ru dokumentor.ru;
charset utf-8;
keepalive_timeout 5;
client_max_body_size 175M;
ssl_certificate /opt/app/Dokumentor/conf/ssl-keys/dokumentor.ru.crt;
ssl_certificate_key /opt/app/Dokumentor/conf/ssl-keys/dokumentor.ru.key;
add_header Strict-Transport-Security 'max-age=604800';
location / {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-NginX-Proxy true;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_http_version 1.1;
proxy_pass http://dokumentor-master/;
proxy_redirect off;
}
}

@ -1,29 +0,0 @@
upstream dokumentor-master {
server localhost:32769;
}
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
server {
client_max_body_size 175M;
listen 0.0.0.0:80;
server_name www.stage.dokumentor.mitri4.pro stage.dokumentor.mitri4.pro;
location / {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-NginX-Proxy true;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_http_version 1.1;
proxy_pass http://dokumentor-master/;
proxy_redirect off;
}
}

@ -1,95 +0,0 @@
-----BEGIN CERTIFICATE-----
MIIIdzCCB1+gAwIBAgIMAUMjCcTnjsjPnHoFMA0GCSqGSIb3DQEBCwUAMGAxCzAJ
BgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMTYwNAYDVQQDEy1H
bG9iYWxTaWduIERvbWFpbiBWYWxpZGF0aW9uIENBIC0gU0hBMjU2IC0gRzIwHhcN
MTYxMjAzMjAzNTA4WhcNMTcxMjA0MjAzNTA4WjA/MSEwHwYDVQQLExhEb21haW4g
Q29udHJvbCBWYWxpZGF0ZWQxGjAYBgNVBAMTEXd3dy5kb2t1bWVudG9yLnJ1MIIC
IjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAmUTMEhDNDQtPOqP32MpPRutH
I2EZ82nSCS24/sPfgOh7hCC348dWpzWlBGPLHuaKpnqJCc4v8MrAR8N22dXbpBhx
YZe3kV2NVCM4PRBEifWHGIqH32kT/Ie+f7qbkLR3XAujSDK6pBfe1biaYtt5NQmd
vmPe8Osk60p8Cw3ZtValR1qSwtdtQl7aJUxCK9eUI4TSKB6yLnQa7JAlvbIKJ7xv
sYvj9I9azztG80IJMxAOZ3n3aYNOGZghz836upGDXH94V6iZM/+RoywnEQg3DMMQ
hdzfINmvVMbzZweeRPkPwBmEhjWllfsJn+YbgtXsYfP5MSFRExoyAUlZjqHR7Au8
gpQP222pTvjRu0eQDAISV+8i4gNs0O+dcHoKSJhYVp36l2eoxIqYw3+QO3mOnI96
3/lfo/B4an7zWPAuHDr/J6ZzlWJhgZfB4W3rtsw7A7ftw+9eawq/E+ZKMiBNjkt0
M3iLU2pABnd+p2p7MrfIENf3r6uH1E+2bYwxYYFlC16DSaZO82jXUMDHQ0qENh/E
AEnQECzlBX/ZILsJcn8RG03yJShuDjwN45RU/VjaA1VJT2nwX+BzYqYEU5c3NoNi
/Woq3drWR109dR6A6wycASZKQo35crYekILCZzeXZDTBC8V1w1G4LKE3oc4DAO5j
XVehBkd94nrojp9ESvkCAwEAAaOCBFAwggRMMA4GA1UdDwEB/wQEAwIFoDCBlAYI
KwYBBQUHAQEEgYcwgYQwRwYIKwYBBQUHMAKGO2h0dHA6Ly9zZWN1cmUuZ2xvYmFs
c2lnbi5jb20vY2FjZXJ0L2dzZG9tYWludmFsc2hhMmcycjEuY3J0MDkGCCsGAQUF
BzABhi1odHRwOi8vb2NzcDIuZ2xvYmFsc2lnbi5jb20vZ3Nkb21haW52YWxzaGEy
ZzIwVgYDVR0gBE8wTTBBBgkrBgEEAaAyAQowNDAyBggrBgEFBQcCARYmaHR0cHM6
Ly93d3cuZ2xvYmFsc2lnbi5jb20vcmVwb3NpdG9yeS8wCAYGZ4EMAQIBMAkGA1Ud
EwQCMAAwQwYDVR0fBDwwOjA4oDagNIYyaHR0cDovL2NybC5nbG9iYWxzaWduLmNv
bS9ncy9nc2RvbWFpbnZhbHNoYTJnMi5jcmwwKwYDVR0RBCQwIoIRd3d3LmRva3Vt
ZW50b3IucnWCDWRva3VtZW50b3IucnUwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsG
AQUFBwMCMB0GA1UdDgQWBBS+30AnMCosxBj+UHlVLNY4ldsbczAfBgNVHSMEGDAW
gBTqTnzUgC3lFYGGJoyCbcCYpM+XDzCCAm0GCisGAQQB1nkCBAIEggJdBIICWQJX
AHUAVhQGmi/XwuzT9eG9RLI+x0Z2ubyZEVzA75SYVdaJ0N0AAAFYxmXMMQAABAMA
RjBEAiBTT+yOJN9obE7IZ3hfndpUNrdTNEaMaxl3Ha0pvtVABwIgAnB1uloNsPDp
EO+RaMKmoGKbFaRf1MHoq1EYhT+ei1cAdQDd6x0reg1PpiCLga2BaHB+Lo6dAdVc
iI09EcTNtuy+zAAAAVjGZc3CAAAEAwBGMEQCIGLkfFq2UZjR0F/LmLqb4gcFmzzs
KeIxhw13giZGF1ILAiAawAXRzh/E/rE68vkVBOMy+URFOfws+Oufw9BbmPOecwB3
ALvZ37wfinG1k5Qjl6qSe0c4V5UKq1LoGpCWZDaOHtGFAAABWMZlzXUAAAQDAEgw
RgIhANC0ycLeTK/XJM00So4erI219VjERMSMkeUoLijQu8vnAiEAjzPK2weX62jg
tepoU2LFGcvBt0lDm5tqu9TUn43v7xsAdgCkuQmQtBhYFIe7E6LMZ3AKPDWYBPkb
37jjd80OyA3cEAAAAVjGZczyAAAEAwBHMEUCIQDOKT2ZJhnIFRkD+rAIRVeiIXD5
yUtUA85yqXz+ubzA0gIgGUgFxZqsMTINL8qCc0oDpkYyjcIjcPuXrQl3LRFZdHMA
dgDuS723dc5guuFCaR+r4Z5mow9+X7By2IMAxHuJeqj9ywAAAVjGZc+dAAAEAwBH
MEUCIQDrmSFD+2DBhJ/9zLOC/pjSmBW7MTvxfSPI5oyu7gMVgwIgRgwREOWJDJAW
sVS+xilhj4JfBzLwWDVp1Xfz7eOIzb8wDQYJKoZIhvcNAQELBQADggEBAAmzYqXa
u1X6/oik4e/okv1yDDNRyXcNUQYOltBt7O+q+RW9H1c6EOsP0zIKrU9knDtwajMp
K5wm9+ciCCd/fubNorP2Clr3dgMcj2pzwSLPR6vRk9lOxirlEu3jj9ZFaN+Nrmf/
zMfej0KJxk+pAM8FsthMJ8AbHSSfO/kCxDBNFMlxoZz/ffheMv439Xh812KafJkw
7st4TCKJlydXFmh/rGk666hUwbC4ZsaGtThi3H4YmQNcDf36cXUErlHPny2rLAIu
C723SizzNQ5ePkT1X67ige5nsFSG0UG6+OVliVr8jvCUGGhCeIyakM2TWPPBEN8R
uL3m1LTKdX7yBy8=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIEYzCCA0ugAwIBAgILBAAAAAABRE7wPiAwDQYJKoZIhvcNAQELBQAwVzELMAkG
A1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv
b3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw0xNDAyMjAxMDAw
MDBaFw0yNDAyMjAxMDAwMDBaMGAxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9i
YWxTaWduIG52LXNhMTYwNAYDVQQDEy1HbG9iYWxTaWduIERvbWFpbiBWYWxpZGF0
aW9uIENBIC0gU0hBMjU2IC0gRzIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
AoIBAQCp3cwOs+IyOd1JIqgTaZOHiOEM7nF9vZCHll1Z8syz0lhXV/lG72wm2DZC
jn4wsy+aPlN7H262okxFHzzTFZMcie089Ffeyr3sBppqKqAZUn9R0XQ5CJ+r69eG
ExWXrjbDVGYOWvKgc4Ux47JkFGr/paKOJLu9hVIVonnu8LXuPbj0fYC82ZA1ZbgX
qa2zmJ+gfn1u+z+tfMIbWTaW2jcyS0tdNQJjjtunz2LuzC7Ujcm9PGqRcqIip3It
INH6yjfaGJjmFiRxJUvE5XuJUgkC/VkrBG7KB4HUs9ra2+PMgKhWBwZ8lgg3nds4
tmI0kWIHdAE42HIw4uuQcSZiwFfzAgMBAAGjggElMIIBITAOBgNVHQ8BAf8EBAMC
AQYwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQU6k581IAt5RWBhiaMgm3A
mKTPlw8wRwYDVR0gBEAwPjA8BgRVHSAAMDQwMgYIKwYBBQUHAgEWJmh0dHBzOi8v
d3d3Lmdsb2JhbHNpZ24uY29tL3JlcG9zaXRvcnkvMDMGA1UdHwQsMCowKKAmoCSG
Imh0dHA6Ly9jcmwuZ2xvYmFsc2lnbi5uZXQvcm9vdC5jcmwwPQYIKwYBBQUHAQEE
MTAvMC0GCCsGAQUFBzABhiFodHRwOi8vb2NzcC5nbG9iYWxzaWduLmNvbS9yb290
cjEwHwYDVR0jBBgwFoAUYHtmGkUNl8qJUC99BM00qP/8/UswDQYJKoZIhvcNAQEL
BQADggEBANdFnqDc4ONhWgt9d4QXLWVagpqNoycqhffJ7+mG/dRHzQFSlsVDvTex
4bjyqdKKEYRxkRWJ3AKdC8tsM4U0KJ4gsrGX3G0LEME8zV/qXdeYMcU0mVwAYVXE
GwJbxeOJyLS4bx448lYm6UHvPc2smU9ZSlctS32ux4j71pg79eXw6ImJuYsDy1oj
H6T9uOr7Lp2uanMJvPzVoLVEgqtEkS5QLlfBQ9iRBIvpES5ftD953x77PzAAi1Pj
tywdO02L3ORkHQRYM68bVeerDL8wBHTk8w4vMDmNSwSMHnVmZkngvkA0x1xaUZK6
EjxS1QSCVS1npd+3lXzuP8MIugS+wEY=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDdTCCAl2gAwIBAgILBAAAAAABFUtaw5QwDQYJKoZIhvcNAQEFBQAwVzELMAkG
A1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv
b3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw05ODA5MDExMjAw
MDBaFw0yODAxMjgxMjAwMDBaMFcxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9i
YWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYDVQQDExJHbG9iYWxT
aWduIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDaDuaZ
jc6j40+Kfvvxi4Mla+pIH/EqsLmVEQS98GPR4mdmzxzdzxtIK+6NiY6arymAZavp
xy0Sy6scTHAHoT0KMM0VjU/43dSMUBUc71DuxC73/OlS8pF94G3VNTCOXkNz8kHp
1Wrjsok6Vjk4bwY8iGlbKk3Fp1S4bInMm/k8yuX9ifUSPJJ4ltbcdG6TRGHRjcdG
snUOhugZitVtbNV4FpWi6cgKOOvyJBNPc1STE4U6G7weNLWLBYy5d4ux2x8gkasJ
U26Qzns3dLlwR5EiUWMWea6xrkEmCMgZK9FGqkjWZCrXgzT/LCrBbBlDSgeF59N8
9iFo7+ryUp9/k5DPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8E
BTADAQH/MB0GA1UdDgQWBBRge2YaRQ2XyolQL30EzTSo//z9SzANBgkqhkiG9w0B
AQUFAAOCAQEA1nPnfE920I2/7LqivjTFKDK1fPxsnCwrvQmeU79rXqoRSLblCKOz
yj1hTdNGCbM+w6DjY1Ub8rrvrTnhQ7k4o+YviiY776BQVvnGCv04zcQLcFGUl5gE
38NflNUVyRRBnMRddWQVDf9VMOyGj/8N7yy5Y0b2qvzfvGn9LhJIZJrglfCm7ymP
AbEVtQwdpf5pLGkkeB6zpxxxYu7KyJesF12KwvhHhm4qxFYxldBniYUr+WymXUad
DKqC5JlR3XC321Y9YeRq4VzW9v493kHMB65jUr9TU/Qr6cf9tveCX4XSQRjbgbME
HMUfpIBvFSDJ3gyICh3WZlXi/EjJKSZp4A==
-----END CERTIFICATE-----

@ -1,51 +0,0 @@
-----BEGIN RSA PRIVATE KEY-----
MIIJKAIBAAKCAgEAmUTMEhDNDQtPOqP32MpPRutHI2EZ82nSCS24/sPfgOh7hCC3
48dWpzWlBGPLHuaKpnqJCc4v8MrAR8N22dXbpBhxYZe3kV2NVCM4PRBEifWHGIqH
32kT/Ie+f7qbkLR3XAujSDK6pBfe1biaYtt5NQmdvmPe8Osk60p8Cw3ZtValR1qS
wtdtQl7aJUxCK9eUI4TSKB6yLnQa7JAlvbIKJ7xvsYvj9I9azztG80IJMxAOZ3n3
aYNOGZghz836upGDXH94V6iZM/+RoywnEQg3DMMQhdzfINmvVMbzZweeRPkPwBmE
hjWllfsJn+YbgtXsYfP5MSFRExoyAUlZjqHR7Au8gpQP222pTvjRu0eQDAISV+8i
4gNs0O+dcHoKSJhYVp36l2eoxIqYw3+QO3mOnI963/lfo/B4an7zWPAuHDr/J6Zz
lWJhgZfB4W3rtsw7A7ftw+9eawq/E+ZKMiBNjkt0M3iLU2pABnd+p2p7MrfIENf3
r6uH1E+2bYwxYYFlC16DSaZO82jXUMDHQ0qENh/EAEnQECzlBX/ZILsJcn8RG03y
JShuDjwN45RU/VjaA1VJT2nwX+BzYqYEU5c3NoNi/Woq3drWR109dR6A6wycASZK
Qo35crYekILCZzeXZDTBC8V1w1G4LKE3oc4DAO5jXVehBkd94nrojp9ESvkCAwEA
AQKCAgBfe5iWeJkF01HcemHhUMfqqpB63fdIyVfsF+vz6xrGOA8a1Tc6U+kL6kOP
ZIwaCA3AgP32edkr7WO0UXUtaCmZJREdd2MI0HtHO1nB0Oyh9a/f/g1/hPrKi9aF
xD89+u+RDD8Ar3nO2IAeLQ8hHxF3lTIMdjNha1xoqNJ2IH7MldoJhceAUqzieCz2
p3dh0L+dERXLoLPodLSBQ5v/fXcOIjqDJV8x8EzmZUtiKHWKtioDFNypgv+Yukn2
J6BXsmdfrJnQIlyvJDwap1r/JVsGPE+C1YK7+GFeM0NXoEtdNLw3NK523dz2fpoU
X0osTBEjRNatYNAdUmhsnMGKNg2MRUPuC1jdquIufCU9u70mkQf5gkHnY2URVTQ8
MR6drsHNtN+Hz45ikA/QuIv5J3jlkEZFuMbYHi5bvaU44TQhmEqeGHyQgYuXaEk/
ZRv8vs7f/dU7flnzRI+M/qAsMP4TUWmNhWyPRNLPaH8kgBqF94QblgVrF4sbXpp0
aTy8DQySHVWmwJAvQdwa9eSj8dLIbKOv8HrGZp3Jekafe8lNfXZqfY9pPqBwYa3s
Dw3C1yVotsrKA8mmYCcNMOY6C3rSA5BZ+/UPob528AV20zCqcnQTQJjZ423THI9Z
d38ph+NqQSNJqo0G807QtyPsd6oY1kXxKkDwLGCZOtqBcWqiAQKCAQEAx2DX/fdn
WZD4sSBa1B/tzrmXvWqqdLk9TDvzFVGD3H3HNqRKKk8C4jYV+ladmkVQuz9P3zIk
21WeiQRZxu8BaN4ZIqFrm6VFwnDN6jxKQ/gLi4S6eumuwDYHT8PRMd3klBeuNXE1
SPkG33D/PnMFtToMmUXf248QRFgAshFqM7xwib80rUfquy4jrdkRRPcFrYRx1DdG
5+bDUiFDjcyswdt/uvRkUnjBsqNgRpzYtn74yJnLUpxk9FF4iMIkWk6poiDYnjsy
tnHeepLnW72l4q7oFhNAMid0ws3Js72uy89QSP+gR+Qu1u3clwvSYnIKCYSkPgjA
iHh15SrIvj4D8QKCAQEAxMu07JS/ZdWdCyZER22Ybku3xzR7cW3lyeuTey0zgdhT
N80a9VRtEeK15Sel7TfooIRcKnOd5oUtP8pjUZhf/bfguRNxPlHvaapqh7O56gdv
YNJRY6r5pe/yMHa15d6RhDdVFph4dmN9ILpVCKYln5kOymNzyOkFmE/IwAibklN1
7N6+B1r0mdNun2khTWqDcT6Ms+FtY8QlSPr+i0+GXhp+4cIX7b/VNskOhWmT/MU7
+4vFT2kvWufRor4KTuk310lVDNnze5XKIh5CQ248ZBkNzD7EyJKSXSQlJReGSlfH
C3Ky2+fmFy7pZY/I7USqWFMgbofHdLoYqvyF+TofiQKCAQASEJJBbpMsqFgAJIBW
S/ZODr7S6ME9BjOpnpztx479AWB1cPlPUA1g1am0Hvj0q2cfMCNag76XKPDOVViV
D76+87CvVJTda2IgtfvSR1grRGQ3ZfVHgbjVrY+ICKQBsqstR9/+85zyWa3nvOND
k0RnITVXBd+8kv/UUWzutWk7+szvjiWFtchs4UdWTx6jcwZOrHtr0P5ZsKzt2hm2
1V6pyeuO0unsFwjl2Ku2eiugjWyZMGYdvBHhmSgkrAKDNDFmgTdfTtnPcg3IaVsR
EuPe4B4jMedVJAIOTHsEECGwUb5iftbU7LXZv3+HKAkN5mtGZYBEJqLbOp7j/rZl
llThAoIBAAqmp80iI9XEIPLIiGuBP/3ANVsfuQqjkwtBp8LD6IS/i4l6nVnnzttn
9MZJRg63tuxxHXfiuAoQ7SkFsChrYc6elBgQOFHSJ24FnixoCsjizCFmYKdPSqGz
gd0jn6aoTCHmyMEnUWW+7rfFvfJw45pms0k65rpZ+kaLEudpz9m0kK5YQ9qsDo8A
xXz1OKsy95g6dW0K51u98vKnOv2ZOUGAl5tqYNcqScTkTtfjpfHJTvzHTM+Gdtyp
rUvskyxALRiGzIaciTQIIF+GWbnCjpwwSvHh986POEK4QRZn1onRhGFXPNzwCSD6
FgG8LXjB/aI4JYo16RKlO86NSkb54VkCggEBAJr5g5lpBbcXVQjPjcTgN8WvHl5O
iuOKbAItYDYwtMYfeSU8ycEEqgsvg8yO3pG9WfHCNQOB1qjybTLeezHMec/hMlOQ
HIWfi1qk3YKBAa+keetddssC/ibHHHIr/z2ivhoFQeDilBttPCSOyldHTKZgI7a1
H1M8bubiriZy5sZ5FcD9mYXsjz/vHDp36b9SSbVUwiBwOqTKw4TClGkvGAwwehbY
1Q7A9MPycKcCsAqeWeXe+SecFBSEMh6YARR55eoBiTZdRMosjPW3z5lU/rQUyThM
uShrmZKfgC4hSp18DQFS+2OBqLEJXQh0JB9HkIajpqC5ZFyOIaImzTUzOmI=
-----END RSA PRIVATE KEY-----

@ -1,56 +0,0 @@
[supervisord]
logfile=/var/log/supervisord.log
loglevel=debug
directory=/opt/app
pidfile=/tmp/supervisord.pid
nodaemon=true
minfds=65535
minprocs=200
environment=PATH="/opt/app"
user=root
[program:nginx]
command=/usr/sbin/nginx "-g" "daemon off;"
priority=1
autorestart=true
stdout_events_enabled=true
stderr_events_enabled=true
[program:gunicorn]
command=gunicorn src.dokumentor.wsgi:application -c /opt/app/conf/gunicorn_prod.py
directory=/opt/app
priority=2
stdout_logfile=/var/log/gunicorn.log
redirect_stderr=true
stdout_events_enabled=true
stderr_events_enabled=true
autorestart=true
[program:celeryd]
command=celery -A src.dokumentor worker -l info -E -B
directory=/opt/app
stdout_logfile=/var/log/celery-worker.log
stderr_logfile=/var/log/celery-worker-error.log
autostart=true
autorestart=true
startsecs=10
stopwaitsecs=600
[program:flower]
command=celery -A src.dokumentor flower -l info -E -B --conf=/opt/app/conf/flower_conf.py
directory=/opt/app
stdout_logfile=/var/log/celery-flower.log
stderr_logfile=/var/log/celery-flower-error.log
autostart=true
autorestart=true
startsecs=10
stopwaitsecs=600
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
redirect_stderr=true
; RQ requires the TERM signal to perform a warm shutdown. If RQ does not die
; within 10 seconds, supervisor will forcefully kill it
stopsignal=TERM

@ -0,0 +1,19 @@
#!/bin/bash
# diff production and development static folders
ALL=$1
PROD_STATIC="_public_html/static"
DEV_STATIC="project/static"
if [ "$ALL" = "--all" ]; then
EXCLUDE=""
else
EXCLUDE="-x cms -x admin -x admin_tools -x debug_toolbar -x tiny_mce -x filer -x autocomplete_light -x ckeditor
-x ckeditor_plugins -x cmsplugin_filer_folder -x djangocms_admin_style -x djangocms_text_ckeditor
-x cms.ckeditor.css -x cms.ckeditor.js"
echo "# exclude =" ${EXCLUDE}
fi
diff -q -r ${EXCLUDE} ${PROD_STATIC} ${DEV_STATIC}

@ -1,66 +0,0 @@
version: '2'
services:
web:
restart: always
container_name: dokumentor-web-develop
build: .
volumes:
- /etc/localtime:/etc/localtime:ro
- /etc/timezone:/etc/timezone:ro
- ./:/opt/app
ports:
- "32768:80"
- "33768:5555"
env_file: conf/env
depends_on:
- db
- redis
- rabbitmq
db:
restart: always
container_name: dokumentor-db-develop
image: kiasaki/alpine-postgres:9.5
expose:
- "5432"
volumes:
- /etc/localtime:/etc/localtime:ro
- /etc/timezone:/etc/timezone:ro
- ./data/postgresql:/var/lib/postgresql/data
environment:
- POSTGRES_USER=dokumentor
- POSTGRES_PASSWORD=dokumentor
redis:
restart: always
container_name: dokumentor-redis-develop
image: redis:alpine
volumes:
- /etc/localtime:/etc/localtime:ro
- /etc/timezone:/etc/timezone:ro
expose:
- "6379"
rabbitmq:
restart: always
container_name: dokumentor-rabbitmq-develop
image: rabbitmq:alpine
environment:
- RABBITMQ_DEFAULT_USER=dokumentor
- RABBITMQ_DEFAULT_PASS=dokumentor
volumes:
- /etc/localtime:/etc/localtime:ro
- /etc/timezone:/etc/timezone:ro
expose:
- "5672"
- "5671"
bower:
container_name: dokumentor-bower-develop
build:
context: .
dockerfile: ./conf/compose/Dockerfile
command: /bin/sh -c "cd /opt/app && bower i"
volumes:
- ./:/opt/app

@ -1,56 +0,0 @@
version: '2'
services:
web:
restart: always
container_name: dokumentor-web
build: .
volumes:
- ./:/opt/app
ports:
- "32768:80"
- "33768:5555"
env_file: conf/env
depends_on:
- db
- redis
- rabbitmq
db:
restart: always
container_name: dokumentor-db
image: kiasaki/alpine-postgres:9.5
expose:
- "5432"
volumes:
- ./data/postgresql:/var/lib/postgresql/data
environment:
- POSTGRES_USER=dokumentor
- POSTGRES_PASSWORD=dokumentor
redis:
restart: always
container_name: dokumentor-redis
image: redis:alpine
expose:
- "6379"
rabbitmq:
restart: always
container_name: dokumentor-rabbitmq
image: rabbitmq:alpine
environment:
- RABBITMQ_DEFAULT_USER=dokumentor
- RABBITMQ_DEFAULT_PASS=dokumentor
expose:
- "5672"
- "5671"
bower:
container_name: dokumentor-bower
build:
context: .
dockerfile: ./conf/compose/Dockerfile
command: /bin/sh -c "cd /opt/app && bower i"
volumes:
- ./:/opt/app

@ -1,66 +0,0 @@
version: '2'
services:
web:
restart: always
container_name: dokumentor-web-stage
build: .
volumes:
- /etc/localtime:/etc/localtime:ro
- /etc/timezone:/etc/timezone:ro
- ./:/opt/app
ports:
- "32769:80"
- "33769:5555"
env_file: conf/env
depends_on:
- db
- redis
- rabbitmq
db:
restart: always
container_name: dokumentor-db-stage
image: kiasaki/alpine-postgres:9.5
expose:
- "5432"
volumes:
- /etc/localtime:/etc/localtime:ro
- /etc/timezone:/etc/timezone:ro
- ./data/postgresql:/var/lib/postgresql/data
environment:
- POSTGRES_USER=dokumentor
- POSTGRES_PASSWORD=dokumentor
redis:
restart: always
container_name: dokumentor-redis-stage
image: redis:alpine
volumes:
- /etc/localtime:/etc/localtime:ro
- /etc/timezone:/etc/timezone:ro
expose:
- "6379"
rabbitmq:
restart: always
container_name: dokumentor-rabbitmq-stage
image: rabbitmq:alpine
environment:
- RABBITMQ_DEFAULT_USER=dokumentor
- RABBITMQ_DEFAULT_PASS=dokumentor
volumes:
- /etc/localtime:/etc/localtime:ro
- /etc/timezone:/etc/timezone:ro
expose:
- "5672"
- "5671"
bower:
container_name: dokumentor-bower-stage
build:
context: .
dockerfile: ./conf/compose/Dockerfile
command: /bin/sh -c "cd /opt/app && bower i"
volumes:
- ./:/opt/app

@ -1,68 +0,0 @@
version: '2'
services:
web:
restart: always
container_name: dokumentor-web-prod
build:
context: .
dockerfile: Dockerfile-prod
volumes:
- /etc/localtime:/etc/localtime:ro
- /etc/timezone:/etc/timezone:ro
- ./:/opt/app
ports:
- "32770:80"
- "33770:5555"
env_file: conf/env
depends_on:
- db
- redis
- rabbitmq
db:
restart: always
container_name: dokumentor-db-prod
image: kiasaki/alpine-postgres:9.5
expose:
- "5432"
volumes:
- /etc/localtime:/etc/localtime:ro
- /etc/timezone:/etc/timezone:ro
- ./data/postgresql:/var/lib/postgresql/data
environment:
- POSTGRES_USER=dokumentor
- POSTGRES_PASSWORD=dokumentor
redis:
restart: always
container_name: dokumentor-redis-prod
image: redis:alpine
volumes:
- /etc/localtime:/etc/localtime:ro
- /etc/timezone:/etc/timezone:ro
expose:
- "6379"
rabbitmq:
restart: always
container_name: dokumentor-rabbitmq-prod
image: rabbitmq:alpine
environment:
- RABBITMQ_DEFAULT_USER=dokumentor
- RABBITMQ_DEFAULT_PASS=dokumentor
volumes:
- /etc/localtime:/etc/localtime:ro
- /etc/timezone:/etc/timezone:ro
expose:
- "5672"
- "5671"
bower:
container_name: dokumentor-bower-prod
build:
context: .
dockerfile: ./conf/compose/Dockerfile
command: /bin/sh -c "cd /opt/app && bower i"
volumes:
- ./:/opt/app

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

83
fabfile.py vendored

@ -1,83 +0,0 @@
# -*- coding: utf-8 -*-
# flake8: noqa
from fabric.api import *
from conf.deploy.common import BRANCH_DEVELOP, BRANCH_PROD, PROJECT_NAME, PROJECT_NAME_DEVELOP
from conf.deploy.prod import HOSTS, USER, PASS
from conf.deploy.develop import HOSTS as HOSTS_DEVELOP, USER as USER_DEVELOP, PASS as PASS_DEVELOP
@task
def develop():
env.status = 'develop'
env.user = USER_DEVELOP
env.password = PASS_DEVELOP
env.hosts = HOSTS_DEVELOP
env.PROJECT_DIR = f'projects/{PROJECT_NAME_DEVELOP}'
env.BRANCH = BRANCH_DEVELOP
@task
def stage():
env.status = 'stage'
env.user = USER_DEVELOP
env.password = PASS_DEVELOP
env.hosts = HOSTS_DEVELOP
env.PROJECT_DIR = f'projects/{PROJECT_NAME_DEVELOP}-stage'
env.BRANCH = BRANCH_PROD
@task
def prod():
env.status = 'prod'
env.user = USER
env.password = PASS
env.hosts = HOSTS
env.PROJECT_DIR = f'/opt/app/{PROJECT_NAME}'
env.BRANCH = BRANCH_PROD
@task
def pull():
with cd(env.PROJECT_DIR):
run(f'git pull origin {env.BRANCH}')
@task
def docker_rebuild():
with cd(env.PROJECT_DIR):
if env.status == 'prod':
run('docker-compose build')
run('docker-compose down')
run('docker-compose up -d')
elif env.status == 'stage':
run('docker-compose -f docker-compose.stage.yml build')
run('docker-compose -f docker-compose.stage.yml down')
run('docker-compose -f docker-compose.stage.yml up -d')
else:
run('docker-compose -f docker-compose.develop.yml build')
run('docker-compose -f docker-compose.develop.yml down')
run('docker-compose -f docker-compose.develop.yml up -d')
@task
def docker_restart():
with cd(env.PROJECT_DIR):
if env.status == 'prod':
run('docker-compose restart web')
elif env.status == 'stage':
run('docker-compose -f docker-compose.stage.yml restart web')
else:
run('docker-compose -f docker-compose.develop.yml restart web')
@task
def upgrade():
pull()
docker_rebuild()
@task
def update():
pull()
docker_restart()

1
log/.gitignore vendored

@ -0,0 +1 @@
*

@ -1,13 +1,9 @@
#!/usr/bin/env python -W ignore
from os import path as p
#!/usr/bin/env python
import os
import sys
import envvars as env
if __name__ == "__main__":
env_file = p.normpath(p.join(p.abspath(p.dirname(__file__)), "./conf/env"))
env.load(env_file)
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "project.settings")
from django.core.management import execute_from_command_line

@ -0,0 +1,4 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import
from .celery import app as celery_app

@ -9,9 +9,7 @@ def user_name(obj):
else:
return obj.user.email
else:
return obj.phone
return None
user_name.short_description = 'email'
@ -22,3 +20,4 @@ class ReqAvailAdmin(admin.ModelAdmin):
admin.site.register(ReqAvail, ReqAvailAdmin)

@ -3,7 +3,7 @@ from django import forms
from captcha.fields import CaptchaField
from callback import models
from . import models
class ReqAvailForm(forms.ModelForm):
@ -16,19 +16,28 @@ class ReqAvailForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(ReqAvailForm, self).__init__(*args, **kwargs)
self.fields['name'].label = u'Ваше имя'
self.fields['name'].widget.attrs['placeholder'] = u'Ваше имя, отчество'
self.fields['phone'].widget.attrs['placeholder'] = u'Ваш номер телефона или e-mail'
self.fields['name'].widget.attrs['width'] = u'Ваше имя, отчество'
def clean(self):
cleaned_data = super(ReqAvailForm, self).clean()
if (cleaned_data.get('name') and cleaned_data.get('message') and
cleaned_data.get('phone')) and not(cleaned_data.get('captcha')):
if (cleaned_data.get('name') and cleaned_data.get('message') and cleaned_data.get('phone')) and not(cleaned_data.get('captcha')):
raise forms.ValidationError(u"Проверьте правильность кода проверки")
elif not (cleaned_data.get('name') and cleaned_data.get('message') and
cleaned_data.get('phone') and cleaned_data.get('captcha')):
elif not (cleaned_data.get('name') and cleaned_data.get('message') and cleaned_data.get('phone') and cleaned_data.get('captcha')):
if self.errors.get('phone') and self.data.get('phone'):
raise forms.ValidationError(u"Проверьте правильность ввода контактов")
else:
raise forms.ValidationError(u"Заполните все поля")
return cleaned_data
#name = self.cleaned_data.get('name', '').strip()
#phone = self.cleaned_data.get('phone', '').strip()
#if not name:
# raise forms.ValidationError(u'Укажите Ваше имя.')
#if not phone:
# raise forms.ValidationError(u'Укажите Ваш телефон.')
return self.cleaned_data

@ -0,0 +1,44 @@
# -*- coding: utf-8 -*-
import os.path
from django.conf import settings
from django.db import models
from django.db.models import Q
class ReqAvail(models.Model):
"""Заказ товара."""
NEW_STATUS = 0
DONE_STATUS = 1
STATUS_CHOICES = (
(NEW_STATUS, u'Новый'),
(DONE_STATUS, u'Обработан'),
)
name = models.CharField(u'Имя клиента', max_length=100)
phone = models.CharField(u'Телефон или e-mail', max_length=50)
message = models.TextField(u'Текст сообщения')
user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='messages', null=True, blank=True)
# email = models.EmailField(u'E-mail', default='', blank=True)
# region = models.ForeignKey(Region, verbose_name=u"Регион", blank=False, null=False, default='')
#good = models.ForeignKey(CatalogItem, verbose_name=u'Товар', null=True, blank=True)
status = models.PositiveSmallIntegerField(u'Статус', choices=STATUS_CHOICES, default=NEW_STATUS)
status_changed = models.DateTimeField(u'Статус изменен', null=True, blank=True, editable=False)
created_at = models.DateTimeField(u'Дата заказа', auto_now_add=True)
updated_at = models.DateTimeField(u'Изменен', auto_now=True)
class Meta:
verbose_name = u'Обратная связь'
verbose_name_plural = u'Обратная связь'
ordering = ['-created_at',]
def __unicode__(self):
return u'Сообщение %s от %s' % (
#self.good if self.good_id else u'(Ничего)',
self.name[:20],
self.created_at.strftime("%d.%m.%Y %H:%M"),
)

@ -1,9 +1,8 @@
# -*- coding: utf-8 -*-
from django.conf.urls import url
from django.conf.urls.defaults import patterns, url
from .views import req_avail
from callback.views import req_avail
urlpatterns = [
urlpatterns = patterns('',
url(r'^send/$', req_avail, name='callback-send-message'),
url(r'^send/(?P<id>\d+)/$', req_avail, name='callback-request-item'),
]
)

@ -2,26 +2,30 @@
import json
from django.http import HttpResponseBadRequest, HttpResponse
from django.views.decorators.http import require_POST
from django.views.decorators.http import require_POST, require_GET
from django.views.decorators.csrf import csrf_protect
from django.template.loader import render_to_string
from django.core.mail import send_mail
from django.conf import settings
from callback.models import ReqAvail
from callback.forms import ReqAvailForm
from .models import ReqAvail
from .forms import ReqAvailForm
CALLBACK_SETTINGS = getattr(settings, 'CALLBACK_SETTINGS')
@require_POST
@csrf_protect
def req_avail(request):
"""Отправить сообщение AJAX."""
def req_avail(request, id=None):
"""Отправить сообщение или обработать запрос наличия товара на складе - AJAX."""
if not request.is_ajax():
return HttpResponseBadRequest()
if id:
good = get_object_or_404(CatalogItem, pk=id)
else:
good = None
form = ReqAvailForm(data=request.POST)
if form.is_valid():
@ -32,8 +36,8 @@ def req_avail(request):
req = ReqAvail.objects.create(
name=form.cleaned_data['name'].strip(),
phone=form.cleaned_data['phone'].strip(),
message=form.cleaned_data['message'],
user=user,
#good=good,
)
# отправить письма менеджерам
@ -45,12 +49,13 @@ def req_avail(request):
data = {
'success': form.is_valid(),
# 'field_errors': form.errors, # ошибки полей
'form_errors': form.non_field_errors(), # ошибки формы
}
# TODO: fix - not emerge
if form.is_valid():
data['message'] = {
'title': u'Сообщение',
'msg': u'Ваша Сообщение отправлено!<br />Скоро с Вами свяжется наш менеджер.',
}
return HttpResponse(json.dumps(data), content_type='application/json')
return HttpResponse(json.dumps(data), mimetype='application/json')

@ -0,0 +1,24 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import
import os
from celery import Celery
from django.conf import settings
# set the default Django settings module for the 'celery' program.
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'project.settings')
app = Celery('proj')
# Using a string here means the worker will not have to
# pickle the object when using Windows.
app.config_from_object('django.conf:settings')
app.autodiscover_tasks(lambda: settings.INSTALLED_APPS)
#@app.task(bind=True)
#def debug_task(self):
# print('Request: {0!r}'.format(self.request))

@ -2,14 +2,10 @@
from django import forms
import autocomplete_light
def set_field_error(form, field, msg=u'Обязательное поле.'):
"""
Добавить сообщение об ошибке поля и убрать это поле из списка
успешно прошедших валидацию.
"""Добавить сообщение об ошибке поля и убрать это поле из списка успешно прошедших валидацию.
Полезно, если нужно инвалидировать поле из метода clean() и добавить ему ошибку.
В этом случае исключение forms.ValidationError() не подходит,
т.к. оно добавит сообщение об ошибке в ошибки формы.
В этом случае исключение forms.ValidationError() не подходит, т.к. оно добавит сообщение об ошибке в ошибки формы.
"""
form._errors[field] = form.error_class([msg])
if field in form.cleaned_data:
@ -19,22 +15,17 @@ def set_field_error(form, field, msg=u'Обязательное поле.'):
class _MySuperForm(object):
"""Базовая форма. Добавляет всякого полезного функционала к форме."""
# Список условно-обязательных полей, у которых нужно установить
# атрибут required=False.
# Полезно, когда какие-то поля становятся обязательны к заполнению
# в зависимости от значения других полей.
# TODO мигрировать на unset_required
conditional_fields = []
# Список условно-обязательных полей, у которых нужно установить атрибут required=False.
# Полезно, когда какие-то поля становятся обязательны к заполнению в зависимости от значения других полей.
conditional_fields = [] #TODO мигрировать на unset_required
# Список полей, у которых нужно сбросить признак обязательности: required=False.
# Полезно, когда в базовой форме определяются какие-то поля, которые в одних
# унаследованных формах обязательны,
# Полезно, когда в базовой форме определяются какие-то поля, которые в одних унаследованных формах обязательны,
# а в других нет.
unset_required = []
# Словарь полей, у которых нужно заменить атрибут label.
# Полезно, когда нужно дать разные метки полям в админке и в форме,
# с которой работает пользователь.
# Полезно, когда нужно дать разные метки полям в админке и в форме, с которой работает пользователь.
change_labels = {}
def __init__(self, *args, **kwargs):
@ -55,7 +46,7 @@ class _MySuperForm(object):
fields[key].required = False
# заменить label
for key, label in self.change_labels.items():
for key, label in self.change_labels.iteritems():
fields[key].label = label
@ -74,5 +65,3 @@ class MyBaseModelForm(forms.ModelForm, _MySuperForm):
f = self.fields
if 'units' in f:
f['units'].widget = autocomplete_light.TextWidget('ACMeasure')
if 'total_price' in f:
f['total_price'].widget = forms.TextInput(attrs={'readonly': 'readonly'})

@ -0,0 +1,3 @@
from django.db import models
# Create your models here.

@ -5,19 +5,18 @@ from django.conf import settings
from django.core.paginator import Paginator, InvalidPage, EmptyPage
from django import forms
from commons.utils import safe_int
from utils import safe_int
# допустимые значения `per_page`
# по умолчанию
_ALLOW_PER_PAGE = (10, 20, 50, 75, 100,)
_ALLOW_PER_PAGE = (10,20,50,75,100,) # по умолчанию
ALLOW_PER_PAGE = getattr(settings, 'ALLOW_PER_PAGE', _ALLOW_PER_PAGE)
class PaginationForm(forms.Form):
"""Форма для пагинатора со списком допустимых значений `per_page`."""
per_page = forms.ChoiceField(label=u'записей на странице',
choices=zip(ALLOW_PER_PAGE, ALLOW_PER_PAGE), required=False)
per_page = forms.ChoiceField(label=u'записей на странице', choices=zip(ALLOW_PER_PAGE, ALLOW_PER_PAGE),
required=False)
def save_per_page_value(func):
@ -33,8 +32,7 @@ def save_per_page_value(func):
per_page = safe_int(request.POST[key])
if per_page in ALLOW_PER_PAGE:
if hasattr(output, 'set_cookie'):
# год
max_age = 365 * 24 * 60 * 60
max_age = 365*24*60*60 # год
output.set_cookie(key, per_page, max_age)
return output
return wrapper
@ -62,8 +60,7 @@ def pagination(request, object_list, page_num=None, form_class=PaginationForm):
"""Создает и возвращает объект django.core.paginator.Paginator и,
если form_class!=None, форму со списком допустимых значений `per_page`.
"""
# кол-во записей на странице
per_page = get_per_page_value(request)
per_page = get_per_page_value(request) # кол-во записей на странице
# пагинатор
paginator = Paginator(object_list, per_page)
page_num = max(1, safe_int(page_num, 1))
@ -74,5 +71,5 @@ def pagination(request, object_list, page_num=None, form_class=PaginationForm):
# форма
form = None
if form_class:
form = PaginationForm(initial={'per_page': per_page})
form = PaginationForm(initial={'per_page': per_page,})
return objects, form

@ -0,0 +1,30 @@
# -*- coding: utf-8 -*-
import cStringIO as StringIO
import ho.pisa as pisa
from django.template.loader import render_to_string
from django.template import RequestContext
from django.http import HttpResponse
def pdf_to_response(content, filename=None, filename_encode='windows-1251'):
"""Выводит content в django.http.HttpResponse, который и возвращает."""
response = HttpResponse(content, mimetype='application/pdf')
if filename:
if filename_encode:
filename = filename.encode(filename_encode)
response['Content-Disposition'] = ('attachment; filename="%s"' % filename.replace('"', "''"))
return response
def render_pdf_to_string(request, template_name, dictionary=None):
"""Рендерит html шаблон в pdf. Возвращает строку, в которой содержится сгенерированный pdf."""
context_instance = RequestContext(request)
html = render_to_string(template_name, dictionary, context_instance)
#return HttpResponse(html) # для отладки
result = StringIO.StringIO()
pisa.pisaDocument(StringIO.StringIO(html.encode('utf-8')), result, encoding='utf-8')
pdf_content = result.getvalue()
result.close()
return pdf_content

@ -1,7 +1,6 @@
# -*- coding: utf-8 -*-
import os
from hashlib import md5
from django import template
from django.conf import settings
@ -33,7 +32,7 @@ def sum_by_attr(obj_list, attr_name, start=0, stop=None):
result += attr()
else:
result += attr
except Exception as error:
except Exception, error:
if DEBUG:
result = 'Tag error: %s' % error
else:
@ -58,56 +57,9 @@ def to_float(value):
"""Если возможно, приводит value к типу float."""
try:
result = float(value)
except Exception as error:
except Exception, error:
if DEBUG:
result = 'Filter error, %s | %s' % (value, error,)
else:
result = value
return result
@register.simple_tag
def file_version(path):
"""Вернуть md5-хэш от статического файла."""
full_path = os.path.join(settings.STATIC_ROOT, path)
try:
return md5(open(full_path, 'rb').read()).hexdigest()
except IOError:
return ''
@register.filter
def rub_in_parts(string, part):
"""Преобразуем строку с суммой прописью для вывода в накладную"""
s = ''
rub = string.find('руб')
if rub != -1:
result = string[:rub - 1]
else:
result = string
words = result.split(' ')
len_string = 0
for word in words:
if len_string >= 50:
break
len_string += len(word) + 1
first_part = result[:len_string]
second_part = result[len_string:]
if part == 1:
s = first_part
elif part == 2:
s = second_part
else:
s = result
return s
@register.filter
def only_kopeck(sum):
kopeck = str(sum).split('.')[-1]
if len(kopeck) < 2:
return f'{kopeck}0'
else:
return kopeck

@ -0,0 +1,19 @@
# -*- coding: utf-8 -*-
import datetime
# convert datetime to json
dthandler = lambda obj: obj.isoformat() if isinstance(obj, datetime.datetime) or isinstance(obj, datetime.date) else None
def safe_int(value, default=None):
"""Возвращает value, приведенное к типу int, или default, если привести не получается."""
try:
return int(value)
except:
return default
def only_numerics(value):
"""Убирает из переданной строки все не цифровые символы."""
return u''.join(c for c in value if c.isdigit())

@ -0,0 +1 @@
# Create your views here.

@ -0,0 +1,3 @@
from useful_tools import *
from get_xlwt_style_list import *
from xls_to_response import *

@ -1,13 +1,16 @@
import xlwt
def get_xlwt_style_list(rdbook):
wt_style_list = []
for rdxf in rdbook.xf_list:
wtxf = xlwt.Style.XFStyle()
#
# number format
#
wtxf.num_format_str = rdbook.format_map[rdxf.format_key].format_str
#
# font
#
wtf = wtxf.font
rdf = rdbook.font_list[rdxf.font_index]
wtf.height = rdf.height
@ -16,43 +19,49 @@ def get_xlwt_style_list(rdbook):
wtf.outline = rdf.outline
wtf.shadow = rdf.outline
wtf.colour_index = rdf.colour_index
# This attribute is redundant, should be driven by weight
wtf.bold = rdf.bold
# Why "private"?
wtf._weight = rdf.weight
wtf.bold = rdf.bold #### This attribute is redundant, should be driven by weight
wtf._weight = rdf.weight #### Why "private"?
wtf.escapement = rdf.escapement
wtf.underline = rdf.underline_type
wtf.underline = rdf.underline_type ####
# wtf.???? = rdf.underline #### redundant attribute, set on the fly when writing
wtf.family = rdf.family
wtf.charset = rdf.character_set
wtf.name = rdf.name
#
# protection
#
wtp = wtxf.protection
rdp = rdxf.protection
wtp.cell_locked = rdp.cell_locked
wtp.formula_hidden = rdp.formula_hidden
#
# border(s) (rename ????)
#
wtb = wtxf.borders
rdb = rdxf.border
wtb.left = rdb.left_line_style
wtb.right = rdb.right_line_style
wtb.top = rdb.top_line_style
wtb.left = rdb.left_line_style
wtb.right = rdb.right_line_style
wtb.top = rdb.top_line_style
wtb.bottom = rdb.bottom_line_style
wtb.diag = rdb.diag_line_style
wtb.left_colour = rdb.left_colour_index
wtb.right_colour = rdb.right_colour_index
wtb.top_colour = rdb.top_colour_index
wtb.diag = rdb.diag_line_style
wtb.left_colour = rdb.left_colour_index
wtb.right_colour = rdb.right_colour_index
wtb.top_colour = rdb.top_colour_index
wtb.bottom_colour = rdb.bottom_colour_index
wtb.diag_colour = rdb.diag_colour_index
wtb.diag_colour = rdb.diag_colour_index
wtb.need_diag1 = rdb.diag_down
wtb.need_diag2 = rdb.diag_up
#
# background / pattern (rename???)
#
wtpat = wtxf.pattern
rdbg = rdxf.background
wtpat.pattern = rdbg.fill_pattern
wtpat.pattern_fore_colour = rdbg.pattern_colour_index
wtpat.pattern_back_colour = rdbg.background_colour_index
#
# alignment
#
wta = wtxf.alignment
rda = rdxf.alignment
wta.horz = rda.hor_align
@ -64,5 +73,6 @@ def get_xlwt_style_list(rdbook):
wta.shri = rda.shrink_to_fit
wta.inde = rda.indent_level
# wta.merg = ????
#
wt_style_list.append(wtxf)
return wt_style_list

@ -1,18 +1,18 @@
# -*- coding: utf-8 -*-
def copy_cells(src_sheet, dst_sheet, style_list, row_from=0, row_to=None, dst_row_shift=0,
col_from=0, col_to=None, dst_col_shift=0):
def copy_cells(src_sheet, dst_sheet, style_list,
row_from=0, row_to=None, dst_row_shift=0,
col_from=0, col_to=None, dst_col_shift=0):
"""
Скопировать блок ячеек из диапазона строк [row_from, row_to] и колонок
[col_from, col_to] исходного листа в новый лист с сохранением их контента,
исходных стилей форматирования, объединения и высоты строк.
"""
row_to = row_to or src_sheet.nrows - 1
col_to = col_to or src_sheet.ncols - 1
row_to = row_to or src_sheet.nrows-1
col_to = col_to or src_sheet.ncols-1
for row in range(row_from, row_to + 1):
for col in range(col_from, col_to + 1):
for row in xrange(row_from, row_to+1):
for col in xrange(col_from, col_to+1):
cell = src_sheet.cell(row, col)
# скопировать контент и стиль ячейки
dst_sheet.write(
@ -26,23 +26,26 @@ def copy_cells(src_sheet, dst_sheet, style_list, row_from=0, row_to=None, dst_ro
height_rows(src_sheet, dst_sheet, row_from, row_to, dst_row_shift)
# объединить ячейки
merge_cells(src_sheet, dst_sheet, style_list, row_from,
row_to, dst_row_shift, col_from, col_to, dst_col_shift)
merge_cells(src_sheet, dst_sheet, style_list,
row_from, row_to, dst_row_shift,
col_from, col_to, dst_col_shift)
def height_rows(src_sheet, dst_sheet, row_from=0, row_to=None, dst_row_shift=0):
def height_rows(src_sheet, dst_sheet,
row_from=0, row_to=None, dst_row_shift=0):
"""Задать в диапазоне строк [row_from, row_to] высоту как в исходном листе.
"""
row_to = row_to or src_sheet.nrows - 1
for row in range(row_from, row_to + 1):
row_to = row_to or src_sheet.nrows-1
for row in xrange(row_from, row_to+1):
src_rowinfo = src_sheet.rowinfo_map.get(row)
if src_rowinfo:
dst_sheet.row(row + dst_row_shift).height = src_rowinfo.height
dst_sheet.row(row + dst_row_shift).height_mismatch = True
dst_sheet.row(row+dst_row_shift).height = src_rowinfo.height
dst_sheet.row(row+dst_row_shift).height_mismatch = True
def merge_cells(src_sheet, dst_sheet, style_list, row_from=0,
row_to=None, dst_row_shift=0, col_from=0, col_to=None, dst_col_shift=0):
def merge_cells(src_sheet, dst_sheet, style_list,
row_from=0, row_to=None, dst_row_shift=0,
col_from=0, col_to=None, dst_col_shift=0):
"""
Объединить ячейки в заданном блоке нового листа, ограниченном строками
[row_from, row_to] и колонками [col_from, col_to], если в исходном листе
@ -51,7 +54,7 @@ def merge_cells(src_sheet, dst_sheet, style_list, row_from=0,
row_to = row_to or src_sheet.nrows
col_to = col_to or src_sheet.ncols
for r1, r2, c1, c2 in src_sheet.merged_cells:
for r1,r2,c1,c2 in src_sheet.merged_cells:
if r1 < row_from or r1 > row_to:
continue
if c1 < col_from or c1 > col_to:
@ -60,9 +63,23 @@ def merge_cells(src_sheet, dst_sheet, style_list, row_from=0,
cell = src_sheet.cell(r1, c1)
style = style_list[cell.xf_index]
# сохранить границы "крайней" ячейки
# нафиг пока эту фичу - из-за нее повылазили какие-то границы,
# которых не было вообще
# brd_1 = style.borders
# cell_2 = src_sheet.cell(r2-1, c2-1)
# brd_2 = style_list[cell_2.xf_index].borders
# print r1,c1,r2,c2,
# print 'borders 1 (left, right, top, bottom)',
# print brd_1.left, brd_1.right, brd_1.top, brd_1.bottom,
# print 'border 2 (same)',
# print brd_2.left, brd_2.right, brd_2.top, brd_2.bottom
# brd_1.right = brd_2.right
# brd_1.right_colour = brd_2.right_colour
dst_sheet.merge(
r1 + dst_row_shift, r2 + dst_row_shift - 1,
c1 + dst_col_shift, c2 + dst_col_shift - 1,
r1+dst_row_shift, r2+dst_row_shift-1,
c1+dst_col_shift, c2+dst_col_shift-1,
style)
@ -70,15 +87,15 @@ def width_cols(src_sheet, dst_sheet, col_from=0, col_to=None, dst_col_shift=0):
"""Задать в диапазоне колонок [col_from, col_to] ширину
как в исходном листе.
"""
col_to = col_to or src_sheet.ncols - 1
for col in range(col_from, col_to + 1):
dst_sheet.col(col + dst_col_shift).width = (
col_to = col_to or src_sheet.ncols-1
for col in xrange(col_from, col_to+1):
dst_sheet.col(col+dst_col_shift).width = (
src_sheet.computed_column_width(col))
def mm_to_twips(x):
"""Перевести из миллиметров в twips."""
return int(x / 25.4 * 72 * 20)
return int(x/25.4*72*20)
def horz_page_break(dst_sheet, row):
@ -86,22 +103,25 @@ def horz_page_break(dst_sheet, row):
dst_sheet.horz_page_breaks.append((row, 0, 255))
def clone_row(src_sheet, dst_sheet, style_list, src_row, n_times=1, dst_row_shift=0):
# -------------------------------------------------------------- прочие хелперы
def clone_row(src_sheet, dst_sheet, style_list,
src_row, n_times=1, dst_row_shift=0):
"""
Размножить n_times раз строку из исходного листа, с сохранением стилей
форматирования.
"""
for offset in range(n_times + 1):
for offset in xrange(n_times+1):
copy_cells(src_sheet, dst_sheet, style_list,
row_from=src_row, row_to=src_row,
dst_row_shift=dst_row_shift + offset)
row_from=src_row, row_to=src_row,
dst_row_shift=dst_row_shift+offset)
# задать высоту строк
height_rows(src_sheet, dst_sheet, src_row, src_row, dst_row_shift)
# объединить ячейки
merge_cells(src_sheet, dst_sheet, style_list,
src_row, src_row, dst_row_shift)
src_row, src_row, dst_row_shift)
def merge_cells_in_row(src_sheet, dst_sheet, style_list, src_row, dst_row):
@ -109,20 +129,19 @@ def merge_cells_in_row(src_sheet, dst_sheet, style_list, src_row, dst_row):
Объединить ячейки в заданной строке нового листа, если в исходном листе они
были объединены, с сохранением исходных стилей форматирования.
"""
for r1, r2, c1, c2 in src_sheet.merged_cells:
for r1,r2,c1,c2 in src_sheet.merged_cells:
if r1 != src_row:
continue
cell = src_sheet.cell(r1, c1)
dst_sheet.merge(dst_row, dst_row, c1, c2 - 1, style_list[cell.xf_index])
dst_sheet.merge(dst_row, dst_row, c1, c2-1, style_list[cell.xf_index])
def sum_src_heights(src_sheet, row_from, row_to):
"""
Суммарная высота всех строк диапазона [row_from, row_to]
"""Суммарная высота всех строк диапазона [row_from, row_to]
исходного листа.
"""
result = 0
for row in range(row_from, row_to + 1):
for row in xrange(row_from, row_to+1):
src_rowinfo = src_sheet.rowinfo_map.get(row)
if src_rowinfo:
result += src_rowinfo.height
@ -130,11 +149,10 @@ def sum_src_heights(src_sheet, row_from, row_to):
def sum_dst_heights(dst_sheet, row_from, row_to):
"""
Суммарная высота всех строк диапазона [row_from, row_to]
"""Суммарная высота всех строк диапазона [row_from, row_to]
на новом листе.
"""
result = 0
for row in range(row_from, row_to + 1):
for row in xrange(row_from, row_to+1):
result += dst_sheet.row(row).height
return result

@ -0,0 +1,12 @@
# -*- coding: utf-8 -*-
from django.http import HttpResponse
def xls_to_response(content, filename=None, filename_encode='windows-1251'):
"""Выводит content в django.http.HttpResponse, который и возвращает."""
response = HttpResponse(content, mimetype='application/ms-excel')
if filename:
if filename_encode:
filename = filename.encode(filename_encode)
response['Content-Disposition'] = ('attachment; filename="%s"' % filename.replace('"', "''"))
return response

@ -0,0 +1,2 @@
# -*- coding: utf-8 -*-
from .models import get_profile

@ -0,0 +1,67 @@
# -*- coding: utf-8 -*-
from django.contrib import admin
import forms
import models
class UserProfileAdmin(admin.ModelAdmin):
list_display = ('get_email', 'profile_type', 'name', 'inn', 'active')
list_display_links = list_display
form = forms.UserProfileAdminForm
class LicenseAdmin(admin.ModelAdmin):
list_display = ('get_company', 'term', 'status', 'order_date', 'date_from', 'date_to')
list_display_links = list_display
search_fields = ('company__email', )
list_filter = ('status', 'term', 'order_date', 'date_from', 'date_to')
#TODO прописать fieldsets
# fieldsets = [
# (None, {'fields': ['user',]}),
# (None, {'fields': ['profile_type',]}),
# (None, {'fields': ['name', 'phone_code', 'phone', 'address', 'inn',]}),
# (None, {'fields': ['add_glavbuh_sign', 'glavbuh_fio',]}),
# (None, {'fields': ['v_litce', 'na_osnovanii',]}),
# (u'ИП', {'fields': ['ip_surname', 'ip_name', 'ip_midname', 'ip_kod_okpo',]}),
# (u'Организация', {'fields': ['org_boss_name', 'org_kpp',]}),
# (u'Печать и подписи', {'fields': ['boss_sign', 'glavbuh_sign', 'stamp',]}),
# ]
class BankAccountAdmin(admin.ModelAdmin):
class Media:
css = {'all': ('css/custom_admin.css',)}
list_display = ('company', 'is_main', 'name', 'account', 'created_at',)
list_display_links = list_display
form = forms.BankAccountAdminForm
class ClientAdmin(admin.ModelAdmin):
class Media:
css = {'all': ('css/custom_admin.css',)}
list_display = ('company', 'name', 'inn',)
list_display_links = list_display
form = forms.ClientAdminForm
fieldsets = [
(None, {'fields': ['company',]}),
(None, {'fields': ['name', 'inn', 'address', 'ogrn',]}),
(u'ИП', {'fields': ['okpo',]}),
(u'Организация', {'fields': ['kpp',]}),
(u'Банковские реквизиты',
{'fields': ['bank_bik', 'bank_name', 'bank_korr_account', 'bank_account',]}),
(u'Контакты',
{'fields': ['contact_name', 'contact_email', 'contact_phone', 'contact_skype',
'contact_other',]}),
]
admin.site.register(models.UserProfile, UserProfileAdmin)
admin.site.register(models.BankAccount, BankAccountAdmin)
admin.site.register(models.Client, ClientAdmin)
admin.site.register(models.License, LicenseAdmin)
admin.site.register(models.LicensePrice)

@ -0,0 +1,32 @@
# -*- coding: utf-8 -*-
IP_PROFILE = 1
ORG_PROFILE = 2
PROFILE_TYPES = (
(IP_PROFILE, u'Индивидуальный предприниматель'),
(ORG_PROFILE, u'Организация'),
)
LICENSE_STATUSES = (
(-1, u'Пробный период'),
(0, u'Не оплачен'),
(1, u'Оплачен'),
(2, u'Активирован'),
(3, u'Срок действия истёк'),
(4, u'Заморожен'),
)
PAYFORMS = (
(-1, u'Бесплатно'),
(0, u'Безналичный расчёт'),
(1, u'Банковская карта'),
# (2, u'Квитанция Сбербанка'),
)
TERMS = (
(1, u'1 месяц'),
(6, u'6 месяцев'),
(12, u'12 месяцев'),
(24, u'24 месяца'),
)

@ -0,0 +1,41 @@
# -*- coding: utf-8 -*-
from datetime import datetime, timedelta
from django.core.cache import cache
from .models import License
def license_check_soon_ends(request):
try:
license_cookie = request.COOKIES.get('close_message_license')
license_15days = cache.get('license_15_%s' % (request.user.username,), None)
days_left = cache.get('days_left_%s' % (request.user.username,), None)
cur_license = cache.get('cur_license_%s' % (request.user.username,), None)
if not days_left or not cur_license:
now = datetime.today()
cur_license = License.objects.filter(company=request.user.profile, date_from__lte=now, date_to__gte=now, status__in=[-1, 1, 2], deleted=False)
if cur_license:
cur_license = cur_license[0]
days_left = (cur_license.date_to - now.date()).days
cache.set('days_left_%s' % (request.user.username,), days_left, 3600)
cache.set('cur_license_%s' % (request.user.username,), cur_license, 3600)
if not license_cookie:
now = datetime.today()
if license_15days is None:
licenses_ends = License.objects.filter(company=request.user.profile, date_to__lte=now + timedelta(15), status__in=[-1, 1, 2], deleted=False)
next_licenses = License.objects.filter(company=request.user.profile, status=1, deleted=False)
if licenses_ends and not next_licenses:
days_to_end = licenses_ends[0].date_to
cache.set('license_15_%s' % (request.user.username,), days_to_end, 3600)
license_15days = days_to_end
else:
license_15days = ''
return {
'license_15days': license_15days,
'license_days': days_left,
'cur_license': cur_license,
}
except Exception as e:
# print e
return { }

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
from django.contrib.auth.decorators import user_passes_test
from django.core.urlresolvers import reverse
def license_required(function=None, redirect_field_name=None, login_url='/my/license/'):
"""

@ -1,18 +1,16 @@
# -*- coding: utf-8 -*-
from django import forms
from django.utils.encoding import force_text
from django.utils.encoding import force_unicode
from django.utils.safestring import mark_safe
from django.conf import settings
from commons.forms import MyBaseModelForm, set_field_error
from yandex_money.forms import PaymentForm
from project.commons.forms import MyBaseModelForm, set_field_error
from customer import consts, models
from django.forms.utils import ErrorList
from . import consts, models
from django.forms.forms import NON_FIELD_ERRORS
FILE_UPLOAD_MAX_MEMORY_SIZE = getattr(settings, 'FILE_UPLOAD_MAX_MEMORY_SIZE ',
2621440) # default 2.5Mb
FILE_UPLOAD_MAX_MEMORY_SIZE = getattr(settings, 'FILE_UPLOAD_MAX_MEMORY_SIZE ', 2621440) # default 2.5Mb
def get_profile_form_class(profile_type):
@ -33,15 +31,18 @@ def get_profile_filters_form_class(profile_type):
return None
###
def _numeric(field):
real_maxlength = field.widget.attrs['maxlength']
return {
'maxlength': int(real_maxlength) * 3,
# set fake maxlength. otherwise browser would cut pasted values too early
'maxlength': int(real_maxlength)*3, # set fake maxlength. otherwise browser would cut pasted values too early
'data-type': 'number', # used by jquery selector
'data-maxlength': real_maxlength, # save real maxlength
}
# -----------------------------------------------------------------------------
class UserProfileForm(MyBaseModelForm):
"""Общая форма редактирования профиля пользователя.
@ -58,7 +59,6 @@ class UserProfileForm(MyBaseModelForm):
class Meta:
model = models.UserProfile
fields = "__all__"
def __init__(self, *args, **kwargs):
super(UserProfileForm, self).__init__(*args, **kwargs)
@ -74,8 +74,8 @@ class UserProfileForm(MyBaseModelForm):
def _check_file_size(self, image):
"""Ограничить максимальный размер загружаемого файла."""
if image and image.size > FILE_UPLOAD_MAX_MEMORY_SIZE:
raise forms.ValidationError(u'Размер изображения превышает %i Мб' % (
FILE_UPLOAD_MAX_MEMORY_SIZE / (1024 * 1024)))
raise forms.ValidationError(
u'Размер изображения превышает %i Мб' % (FILE_UPLOAD_MAX_MEMORY_SIZE / (1024*1024)))
return image
def clean_boss_sign(self):
@ -110,8 +110,7 @@ class IpUserProfileForm(UserProfileForm):
# фио главбуха
'glavbuh_surname', 'glavbuh_name', 'glavbuh_midname',
# контактная информация - адреса, телефон, факс, почта, сайт
'address', 'jur_address', 'real_address', 'phone_code', 'phone', 'fax_code', 'fax',
'email', 'site',
'address', 'jur_address', 'real_address', 'phone_code', 'phone', 'fax_code', 'fax', 'email', 'site',
# подписи, печать и логотип
'boss_sign', 'glavbuh_sign', 'stamp', 'logo',
'tmb_logo', 'tmb_boss_sign', 'tmb_glavbuh_sign', 'tmb_stamp',
@ -134,8 +133,7 @@ class OrgUserProfileForm(UserProfileForm):
# фио главбуха
'glavbuh_surname', 'glavbuh_name', 'glavbuh_midname',
# контактная информация - адреса, телефон, факс, почта, сайт
'address', 'jur_address', 'real_address', 'phone_code', 'phone', 'fax_code', 'fax',
'email', 'site',
'address', 'jur_address', 'real_address', 'phone_code', 'phone', 'fax_code', 'fax', 'email', 'site',
# подписи, печать и логотип
'boss_sign', 'glavbuh_sign', 'stamp', 'logo',
'tmb_logo', 'tmb_boss_sign', 'tmb_glavbuh_sign', 'tmb_stamp',
@ -145,8 +143,7 @@ class OrgUserProfileForm(UserProfileForm):
class UserProfileAdminForm(UserProfileForm):
"""Форма редактирования профиля - для админки."""
# условно-обязательные поля, проверять отдельно -
# могут быть обязательны в зависимости от типа профиля
# условно-обязательные поля, проверять отдельно - могут быть обязательны в зависимости от типа профиля
unset_required = [
# для ИП
'kpp', 'name'
@ -161,122 +158,86 @@ class UserProfileAdminForm(UserProfileForm):
# тип профиля - ИП или Организация
profile_type = cleaned_data.get('profile_type')
if profile_type == consts.IP_PROFILE: # поля, обязательные для ИП
if profile_type == consts.IP_PROFILE: # поля, обязательные для ИП
pass
elif profile_type == consts.ORG_PROFILE: # поля, обязательные для Организаций
elif profile_type == consts.ORG_PROFILE: # поля, обязательные для Организаций
org_boss_name = cleaned_data.get('org_boss_name')
kpp = cleaned_data.get('kpp')
if not org_boss_name:
set_field_error(self, 'org_boss_name')
if not kpp:
set_field_error(self, 'kpp')
if not org_boss_name: set_field_error(self, 'org_boss_name')
if not kpp: set_field_error(self, 'kpp')
return cleaned_data
# -----------------------------------------------------------------------------
class BankAccountForm(forms.ModelForm):
"""Форма редактирования расчетных счетов."""
class Meta:
model = models.BankAccount
fields = ('bik', 'name', 'short_name', 'korr_account', 'account', 'is_main', 'company')
_textarea = forms.Textarea(attrs={'cols': 80, 'rows': 3})
widgets = {
'bik': forms.HiddenInput(),
'name': forms.HiddenInput(),
'short_name': forms.HiddenInput(),
'korr_account': forms.HiddenInput(),
'company': forms.HiddenInput(),
'account': forms.TextInput(attrs={'style': 'width:150px;'})
}
widgets = {'name': _textarea, 'short_name': _textarea, 'company': forms.HiddenInput()}
def __init__(self, *args, **kwargs):
super(BankAccountForm, self).__init__(*args, **kwargs)
self.fields['bik'].widget.attrs.update(_numeric(self.fields['bik']))
self.fields['korr_account'].widget.attrs.update(_numeric(self.fields['korr_account']))
self.fields['account'].widget.attrs.update(_numeric(self.fields['account']))
class BankAccountAdminForm(BankAccountForm):
"""Форма редактирования расчетных счетов - для админки."""
class Meta(BankAccountForm.Meta):
fields = '__all__'
fields = None
exclude = ('address',)
class BankAccountListForm(forms.Form):
"""Форма со списком всех расчетных счетов пользователя."""
bank_account = forms.ModelChoiceField(queryset=models.BankAccount.objects.get_all(None),
empty_label=u'все контрагенты', required=False)
empty_label=u'все контрагенты', required=False)
def __init__(self, user, *args, **kwargs):
super(BankAccountListForm, self).__init__(*args, **kwargs)
self.fields['bank_account'].queryset = models.BankAccount.objects.get_all(user.profile)
# -----------------------------------------------------------------------------
class ClientForm(forms.ModelForm):
"""Форма редактирования контрагентов."""
class Meta:
model = models.Client
fields = (
'name',
'name_short_self',
'name_short_dadata',
'inn',
'kpp',
'ogrn',
'okpo',
'address',
# банковские реквизиты
'bank_bik',
'bank_name',
'bank_short_name',
'bank_korr_account',
'bank_account',
# контакты
'contact_name',
'contact_email',
'contact_phone',
'contact_skype',
'contact_other',
fields = ('name', 'inn', 'kpp', 'ogrn', 'okpo', 'address',
# банковские реквизиты
'bank_bik', 'bank_name', 'bank_korr_account', 'bank_account',
# контакты
'contact_name', 'contact_email', 'contact_phone', 'contact_skype', 'contact_other',
)
_textarea = forms.Textarea(attrs={'cols': 80, 'rows': 3})
widgets = {
'bank_bik': forms.HiddenInput(),
'bank_name': forms.HiddenInput(),
'bank_short_name': forms.HiddenInput(),
'bank_korr_account': forms.HiddenInput(),
#'name': _textarea,
#'address': _textarea,
'bank_name': _textarea,
#'contact_other': _textarea,
}
def __init__(self, *args, **kwargs):
self.request = kwargs.pop('request', None)
super(ClientForm, self).__init__(*args, **kwargs)
self.fields['inn'].widget.attrs.update(_numeric(self.fields['inn']))
self.fields['kpp'].widget.attrs.update(_numeric(self.fields['kpp']))
self.fields['ogrn'].widget.attrs.update(_numeric(self.fields['ogrn']))
self.fields['okpo'].widget.attrs.update(_numeric(self.fields['okpo']))
def clean(self):
if self.instance.inn:
return self.cleaned_data
inn = self.cleaned_data.get('inn')
partner_exists = models.Client.objects.filter(inn=inn, company=self.request.user.profile)
if partner_exists:
if 'inn' not in self._errors:
error = 'Контрагент с ИНН {} уже существует'.format(inn)
self._errors['inn'] = ErrorList()
self._errors['inn'].append(error)
self._errors[NON_FIELD_ERRORS] = self.error_class([error])
return self.cleaned_data
self.fields['bank_bik'].widget.attrs.update(_numeric(self.fields['bank_bik']))
self.fields['bank_korr_account'].widget.attrs.update(_numeric(self.fields['bank_korr_account']))
self.fields['bank_account'].widget.attrs.update(_numeric(self.fields['bank_account']))
class ClientAdminForm(ClientForm):
"""Форма редактирования контрагентов - для админки."""
class Meta(ClientForm.Meta):
fields = None
exclude = ('bank_address',)
@ -284,18 +245,19 @@ class ClientAdminForm(ClientForm):
class ClientsListForm(forms.Form):
"""Форма со списком всех контрагентов пользователя."""
client = forms.ModelChoiceField(queryset=models.Client.objects.get_all(None),
empty_label=u'все контрагенты', required=False)
client = forms.ModelChoiceField(queryset=models.Client.objects.get_all(None), empty_label=u'все контрагенты',
required=False)
def __init__(self, user, *args, **kwargs):
super(ClientsListForm, self).__init__(*args, **kwargs)
self.fields['client'].queryset = models.Client.objects.get_all(user.profile)
# -----------------------------------------------------------------------------
class UserProfileFiltersForm(MyBaseModelForm):
"""Общая форма фильтрации реквизитов."""
# задать в наследнике!
_profile_type = None
_profile_type = None # задать в наследнике!
_is_admin = False
_user = None
@ -305,7 +267,6 @@ class UserProfileFiltersForm(MyBaseModelForm):
widgets = {
'bank_account': forms.RadioSelect(),
}
fields = "__all__"
def __init__(self, profile=None, accounts=None, *args, **kwargs):
instance = kwargs.get('instance')
@ -320,8 +281,7 @@ class UserProfileFiltersForm(MyBaseModelForm):
# TODO 1. переписать проверки в стиле new_initial['show_inn'] = bool(profile.inn)
# TODO 2. загнать условия в словарь вида {'show_inn': 'inn'}. потом в цикле обработать
if profile:
# сбросить чекбоксы, если не заполнены определенные поля
# в профиле или нет расчетных счетов
# сбросить чекбоксы, если не заполнены определенные поля в профиле или нет расчетных счетов
if instance:
new_initial['show_inn'] = bool(profile.inn)
new_initial['show_ogrn'] = bool(profile.ogrn)
@ -387,60 +347,37 @@ class UserProfileFiltersForm(MyBaseModelForm):
f = self.fields
# только расчетные счета пользователя
f_acc = f['bank_account'] # TODO вынести настройку расчетных счетов в mixin?
f_acc = f['bank_account'] # TODO вынести настройку расчетных счетов в mixin?
f_acc.queryset = accounts
f_acc.empty_label = None
# исправить метку
f_acc.label_from_instance = lambda obj: mark_safe(
force_text(f'{obj.account}<br /><span class="name">{obj.name}</span>'))
force_unicode('%s<br /><span class="name">%s</span>' % (obj.account, obj.name,))) # исправить метку
# TODO: refactor
# заблокировать чекбоксы, если: не заполнены определенные поля
# в профиле или нет расчетных счетов
# заблокировать чекбоксы, если: не заполнены определенные поля в профиле или нет расчетных счетов
if profile:
if not profile.inn:
f['show_inn'].widget.attrs['disabled'] = 'disabled'
if not profile.ogrn:
f['show_ogrn'].widget.attrs['disabled'] = 'disabled'
if not profile.okpo:
f['show_okpo'].widget.attrs['disabled'] = 'disabled'
if not profile.get_glavbuh_fio():
f['show_glavbuh'].widget.attrs['disabled'] = 'disabled'
if not profile.address:
f['show_address'].widget.attrs['disabled'] = 'disabled'
if not profile.jur_address:
f['show_jur_address'].widget.attrs['disabled'] = 'disabled'
if not profile.real_address:
f['show_real_address'].widget.attrs['disabled'] = 'disabled'
if not profile.get_full_phone():
f['show_phone'].widget.attrs['disabled'] = 'disabled'
if not profile.get_full_fax():
f['show_fax'].widget.attrs['disabled'] = 'disabled'
if not profile.email:
f['show_email'].widget.attrs['disabled'] = 'disabled'
if not profile.site:
f['show_site'].widget.attrs['disabled'] = 'disabled'
if not profile.logo:
f['show_logo'].widget.attrs['disabled'] = 'disabled'
if not accounts:
f['show_bank_account'].widget.attrs['disabled'] = 'disabled'
if not profile.inn: f['show_inn'].widget.attrs['disabled'] = 'disabled'
if not profile.ogrn: f['show_ogrn'].widget.attrs['disabled'] = 'disabled'
if not profile.okpo: f['show_okpo'].widget.attrs['disabled'] = 'disabled'
if not profile.get_glavbuh_fio(): f['show_glavbuh'].widget.attrs['disabled'] = 'disabled'
if not profile.address: f['show_address'].widget.attrs['disabled'] = 'disabled'
if not profile.jur_address: f['show_jur_address'].widget.attrs['disabled'] = 'disabled'
if not profile.real_address: f['show_real_address'].widget.attrs['disabled'] = 'disabled'
if not profile.get_full_phone(): f['show_phone'].widget.attrs['disabled'] = 'disabled'
if not profile.get_full_fax(): f['show_fax'].widget.attrs['disabled'] = 'disabled'
if not profile.email: f['show_email'].widget.attrs['disabled'] = 'disabled'
if not profile.site: f['show_site'].widget.attrs['disabled'] = 'disabled'
if not profile.logo: f['show_logo'].widget.attrs['disabled'] = 'disabled'
if not accounts: f['show_bank_account'].widget.attrs['disabled'] = 'disabled'
if self._profile_type == consts.IP_PROFILE:
if not profile.svid_gos_reg:
f['show_svid_gos_reg'].widget.attrs['disabled'] = 'disabled'
if not profile.ip_reg_date:
f['show_ip_reg_date'].widget.attrs['disabled'] = 'disabled'
if not profile.svid_gos_reg: f['show_svid_gos_reg'].widget.attrs['disabled'] = 'disabled'
if not profile.ip_reg_date: f['show_ip_reg_date'].widget.attrs['disabled'] = 'disabled'
elif self._profile_type == consts.ORG_PROFILE:
if not profile.name:
f['show_name'].widget.attrs['disabled'] = 'disabled'
if not profile.full_name:
f['show_full_name'].widget.attrs['disabled'] = 'disabled'
if not profile.kpp:
f['show_kpp'].widget.attrs['disabled'] = 'disabled'
if not profile.boss_title:
f['show_org_boss_title_and_fio'].widget.attrs['disabled'] = 'disabled'
if not profile.na_osnovanii:
f['show_na_osnovanii'].widget.attrs['disabled'] = 'disabled'
if not profile.name: f['show_name'].widget.attrs['disabled'] = 'disabled'
if not profile.full_name: f['show_full_name'].widget.attrs['disabled'] = 'disabled'
if not profile.kpp: f['show_kpp'].widget.attrs['disabled'] = 'disabled'
if not profile.boss_title: f['show_org_boss_title_and_fio'].widget.attrs['disabled'] = 'disabled'
if not profile.na_osnovanii: f['show_na_osnovanii'].widget.attrs['disabled'] = 'disabled'
# блокировать чекбоксы, т.к.эти реквизиты юзеру выключать нельзя
if self._profile_type == consts.IP_PROFILE:
@ -461,24 +398,24 @@ class IpUserProfileFiltersForm(UserProfileFiltersForm):
class Meta(UserProfileFiltersForm.Meta):
fields = (
'show_profile_type',
'show_ip_boss_fio',
'show_inn',
'show_ogrn',
'show_okpo',
'show_svid_gos_reg',
'show_ip_reg_date',
'show_ip_boss_fio',
'show_inn',
'show_ogrn',
'show_okpo',
'show_svid_gos_reg',
'show_ip_reg_date',
'show_glavbuh',
'show_bank_account',
'bank_account',
'bank_account',
'show_contact_info',
'show_address',
'show_jur_address',
'show_real_address',
'show_phone',
'show_fax',
'show_email',
'show_site',
'show_logo',
'show_address',
'show_jur_address',
'show_real_address',
'show_phone',
'show_fax',
'show_email',
'show_site',
'show_logo',
)
@ -494,41 +431,47 @@ class OrgUserProfileFiltersForm(UserProfileFiltersForm):
class Meta(UserProfileFiltersForm.Meta):
fields = (
'show_profile_type',
'show_name',
'show_full_name',
'show_inn',
'show_kpp',
'show_ogrn',
'show_okpo',
'show_name',
'show_full_name',
'show_inn',
'show_kpp',
'show_ogrn',
'show_okpo',
'show_org_boss_title_and_fio',
'show_na_osnovanii',
'show_na_osnovanii',
'show_glavbuh',
'show_bank_account',
'bank_account',
'bank_account',
'show_contact_info',
'show_address',
'show_jur_address',
'show_real_address',
'show_phone',
'show_fax',
'show_email',
'show_site',
'show_logo',
'show_address',
'show_jur_address',
'show_real_address',
'show_phone',
'show_fax',
'show_email',
'show_site',
'show_logo',
)
# -----------------------------------------------------------------------------
class EmailProfileForm(forms.Form):
"""Форма отправки реквизитов пользователя по email."""
to = forms.EmailField(label=u'E-mail получателя')
body = forms.CharField(label=u'Текст сообщения', max_length=1000, required=False,
widget=forms.Textarea(attrs={'cols': 80, 'rows': 3}))
widget=forms.Textarea(attrs={'cols': 80, 'rows': 3}))
class LicenseForm(forms.Form):
"""Форма продления лицензии
"""
term = forms.ModelChoiceField(queryset=models.LicensePrice.objects.all(),
widget=forms.RadioSelect, label=u'Срок лицензии',
empty_label=None)
payform = forms.ChoiceField(choices=consts.PAYFORMS[2:], widget=forms.RadioSelect,
label=u'Форма оплаты')
widget=forms.RadioSelect, label=u'Срок лицензии', empty_label = None)
payform = forms.ChoiceField(choices=consts.PAYFORMS[1:], widget=forms.RadioSelect,
label=u'Форма оплаты')
class YaForm(PaymentForm):
def get_display_field_names(self):
return ()

@ -23,38 +23,29 @@ class UserProfileFiltersManager(models.Manager):
try:
filters = self.get(company=user.profile)
except ObjectDoesNotExist:
filters = self.create_filters(user)
filters = self.create_filters(company=user.profile)
filters.save()
return filters
class BankAccountManager(models.Manager):
def get_main(self, company):
"""
Возвращает основной расчетный счет пользователя или None,
если у него еще нет расчетных счетов.
"""
"""Возвращает основной расчетный счет пользователя или None если у него еще нет расчетных счетов."""
try:
return self.filter(company=company).order_by('-is_main', 'created_at')[0]
except IndexError:
return None
def get_all(self, company):
"""
Возвращает все расчетные счета пользователя.
Отсортированы так, что первым идет основной счет,
а потом остальные в порядке их добавления.
"""
"""Возвращает все расчетные счета пользователя.
Отсортированы так, что первым идет основной счет, а потом остальные в порядке их добавления."""
try:
return self.filter(company=company).order_by('-is_main', 'created_at')
except:
return self.none()
def have_main(self, company):
"""
Возвращает True, если у пользователя есть основной расчетный счет,
и False в противном случае.
"""
"""Возвращает True, если у пользователя есть основной расчетный счет, и False в противном случае."""
return True if self.filter(company=company, is_main=True) else False
def force_main(self, company):
@ -64,7 +55,7 @@ class BankAccountManager(models.Manager):
if not self.have_main(company=company):
try:
accounts = self.get_all(company=company)[0]
accounts.is_main = True
accounts.is_main=True
accounts.save()
except IndexError:
pass

@ -3,7 +3,7 @@ from django.utils.functional import SimpleLazyObject
from django.contrib.auth.views import logout as django_logout
from django.contrib import messages
from customer.models import get_profile
from .models import get_profile
def _get_profile(request):
@ -13,22 +13,15 @@ def _get_profile(request):
class ProfileMiddleware(object):
def process_request(self, request):
assert hasattr(request, 'user'),\
"The Profile middleware requires authentication middleware to be installed. " \
"Edit your MIDDLEWARE_CLASSES setting to insert " \
"'django.contrib.auth.middleware.AuthenticationMiddleware'."
assert hasattr(request, 'user'), "The Profile middleware requires authentication middleware to be installed. Edit your MIDDLEWARE_CLASSES setting to insert 'django.contrib.auth.middleware.AuthenticationMiddleware'."
request.profile = SimpleLazyObject(lambda: _get_profile(request))
try:
if request.user.is_authenticated() and request.session and request.user:
if request.user.profile.user_session_key != request.session.session_key and \
request.profile.user_session_key != '':
django_logout(request)
messages.add_message(
request, messages.ERROR,
'Другой пользователь вошёл под этим логином.'
)
messages.add_message(request, messages.ERROR, u'Другой пользователь вошёл под этим логином.')
except:
# print 'no user profile'
pass

@ -0,0 +1,289 @@
# -*- coding: utf-8 -*-
from south.utils import datetime_utils as datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
class Migration(SchemaMigration):
def forwards(self, orm):
# Adding model 'UserProfile'
db.create_table(u'customer_userprofile', (
(u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
('profile_type', self.gf('django.db.models.fields.PositiveSmallIntegerField')()),
('boss_surname', self.gf('django.db.models.fields.CharField')(default='', max_length=30)),
('boss_name', self.gf('django.db.models.fields.CharField')(default='', max_length=30)),
('boss_midname', self.gf('django.db.models.fields.CharField')(default='', max_length=30)),
('inn', self.gf('django.db.models.fields.CharField')(default='', max_length=12)),
('ogrn', self.gf('django.db.models.fields.CharField')(default='', max_length=15)),
('okpo', self.gf('django.db.models.fields.CharField')(default='', max_length=10, blank=True)),
('glavbuh_surname', self.gf('django.db.models.fields.CharField')(default='', max_length=30, blank=True)),
('glavbuh_name', self.gf('django.db.models.fields.CharField')(default='', max_length=30, blank=True)),
('glavbuh_midname', self.gf('django.db.models.fields.CharField')(default='', max_length=30, blank=True)),
('address', self.gf('django.db.models.fields.CharField')(default='', max_length=256)),
('real_address', self.gf('django.db.models.fields.CharField')(default='', max_length=256, blank=True)),
('phone_code', self.gf('django.db.models.fields.CharField')(default='', max_length=10, blank=True)),
('phone', self.gf('django.db.models.fields.CharField')(default='', max_length=20, blank=True)),
('fax_code', self.gf('django.db.models.fields.CharField')(default='', max_length=10, blank=True)),
('fax', self.gf('django.db.models.fields.CharField')(default='', max_length=20, blank=True)),
('email', self.gf('django.db.models.fields.EmailField')(default='', max_length=75, blank=True)),
('site', self.gf('django.db.models.fields.CharField')(default='', max_length=256, blank=True)),
('svid_gos_reg', self.gf('django.db.models.fields.CharField')(default='', max_length=256, blank=True)),
('ip_reg_date', self.gf('django.db.models.fields.DateField')(null=True, blank=True)),
('name', self.gf('django.db.models.fields.CharField')(default='', max_length=256)),
('full_name', self.gf('django.db.models.fields.CharField')(default='', max_length=256, blank=True)),
('kpp', self.gf('django.db.models.fields.CharField')(default='', max_length=9)),
('jur_address', self.gf('django.db.models.fields.CharField')(default='', max_length=256, blank=True)),
('boss_title', self.gf('django.db.models.fields.CharField')(default='', max_length=256, blank=True)),
('na_osnovanii', self.gf('django.db.models.fields.CharField')(default='', max_length=256, blank=True)),
('boss_sign', self.gf('django.db.models.fields.files.ImageField')(default='', max_length=100, blank=True)),
('glavbuh_sign', self.gf('django.db.models.fields.files.ImageField')(default='', max_length=100, blank=True)),
('stamp', self.gf('django.db.models.fields.files.ImageField')(default='', max_length=100, blank=True)),
('logo', self.gf('django.db.models.fields.files.ImageField')(default='', max_length=100, blank=True)),
('created_at', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)),
('updated_at', self.gf('django.db.models.fields.DateTimeField')(auto_now=True, blank=True)),
('active', self.gf('django.db.models.fields.BooleanField')(default=False)),
('confirmed', self.gf('django.db.models.fields.BooleanField')(default=False)),
('user_session_key', self.gf('django.db.models.fields.CharField')(default='', max_length=256, blank=True)),
))
db.send_create_signal(u'customer', ['UserProfile'])
# Adding model 'BankAccount'
db.create_table(u'customer_bankaccount', (
(u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
('company', self.gf('django.db.models.fields.related.ForeignKey')(related_name='bank_accounts', to=orm['customer.UserProfile'])),
('bik', self.gf('django.db.models.fields.CharField')(max_length=10)),
('name', self.gf('django.db.models.fields.CharField')(max_length=256)),
('address', self.gf('django.db.models.fields.CharField')(max_length=256)),
('korr_account', self.gf('django.db.models.fields.CharField')(max_length=20)),
('account', self.gf('django.db.models.fields.CharField')(max_length=20)),
('is_main', self.gf('django.db.models.fields.BooleanField')(default=False)),
('created_at', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)),
('updated_at', self.gf('django.db.models.fields.DateTimeField')(auto_now=True, blank=True)),
))
db.send_create_signal(u'customer', ['BankAccount'])
# Adding model 'Client'
db.create_table(u'customer_client', (
(u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
('company', self.gf('django.db.models.fields.related.ForeignKey')(related_name='clients', to=orm['customer.UserProfile'])),
('name', self.gf('django.db.models.fields.CharField')(max_length=256, db_index=True)),
('inn', self.gf('django.db.models.fields.CharField')(max_length=12)),
('kpp', self.gf('django.db.models.fields.CharField')(default='', max_length=9, blank=True)),
('okpo', self.gf('django.db.models.fields.CharField')(default='', max_length=10, blank=True)),
('address', self.gf('django.db.models.fields.CharField')(max_length=256)),
('bank_bik', self.gf('django.db.models.fields.CharField')(default='', max_length=10, blank=True)),
('bank_name', self.gf('django.db.models.fields.CharField')(default='', max_length=256, blank=True)),
('bank_address', self.gf('django.db.models.fields.CharField')(default='', max_length=256, blank=True)),
('bank_korr_account', self.gf('django.db.models.fields.CharField')(default='', max_length=20, blank=True)),
('bank_account', self.gf('django.db.models.fields.CharField')(default='', max_length=20, blank=True)),
('contact_name', self.gf('django.db.models.fields.CharField')(default='', max_length=50, blank=True)),
('contact_email', self.gf('django.db.models.fields.EmailField')(default='', max_length=50, blank=True)),
('contact_phone', self.gf('django.db.models.fields.CharField')(default='', max_length=50, blank=True)),
('contact_icq', self.gf('django.db.models.fields.CharField')(default='', max_length=20, blank=True)),
('contact_skype', self.gf('django.db.models.fields.CharField')(default='', max_length=20, blank=True)),
('contact_other', self.gf('django.db.models.fields.CharField')(default='', max_length=256, blank=True)),
('created_at', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)),
('updated_at', self.gf('django.db.models.fields.DateTimeField')(auto_now=True, blank=True)),
))
db.send_create_signal(u'customer', ['Client'])
# Adding model 'UserProfileFilters'
db.create_table(u'customer_userprofilefilters', (
('company', self.gf('django.db.models.fields.related.OneToOneField')(related_name='profile_filters', unique=True, primary_key=True, to=orm['customer.UserProfile'])),
('show_profile_type', self.gf('django.db.models.fields.BooleanField')(default=True)),
('show_inn', self.gf('django.db.models.fields.BooleanField')(default=True)),
('show_ogrn', self.gf('django.db.models.fields.BooleanField')(default=True)),
('show_okpo', self.gf('django.db.models.fields.BooleanField')(default=True)),
('show_glavbuh', self.gf('django.db.models.fields.BooleanField')(default=True)),
('show_bank_account', self.gf('django.db.models.fields.BooleanField')(default=True)),
('bank_account', self.gf('django.db.models.fields.related.ForeignKey')(default=None, related_name='+', null=True, blank=True, to=orm['customer.BankAccount'])),
('show_contact_info', self.gf('django.db.models.fields.BooleanField')(default=True)),
('show_real_address', self.gf('django.db.models.fields.BooleanField')(default=True)),
('show_phone', self.gf('django.db.models.fields.BooleanField')(default=True)),
('show_fax', self.gf('django.db.models.fields.BooleanField')(default=True)),
('show_email', self.gf('django.db.models.fields.BooleanField')(default=True)),
('show_site', self.gf('django.db.models.fields.BooleanField')(default=True)),
('show_ip_boss_fio', self.gf('django.db.models.fields.BooleanField')(default=True)),
('show_svid_gos_reg', self.gf('django.db.models.fields.BooleanField')(default=True)),
('show_ip_reg_date', self.gf('django.db.models.fields.BooleanField')(default=True)),
('show_name', self.gf('django.db.models.fields.BooleanField')(default=True)),
('show_full_name', self.gf('django.db.models.fields.BooleanField')(default=True)),
('show_kpp', self.gf('django.db.models.fields.BooleanField')(default=True)),
('show_org_boss_title_and_fio', self.gf('django.db.models.fields.BooleanField')(default=True)),
('show_na_osnovanii', self.gf('django.db.models.fields.BooleanField')(default=True)),
('show_jur_address', self.gf('django.db.models.fields.BooleanField')(default=True)),
))
db.send_create_signal(u'customer', ['UserProfileFilters'])
# Adding model 'License'
db.create_table(u'customer_license', (
(u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
('company', self.gf('django.db.models.fields.related.ForeignKey')(related_name='licenses', to=orm['customer.UserProfile'])),
('term', self.gf('django.db.models.fields.IntegerField')()),
('date_from', self.gf('django.db.models.fields.DateField')(null=True, blank=True)),
('date_to', self.gf('django.db.models.fields.DateField')(null=True, blank=True)),
('payform', self.gf('django.db.models.fields.IntegerField')(default=0)),
('status', self.gf('django.db.models.fields.IntegerField')(default=0)),
('order_date', self.gf('django.db.models.fields.DateField')(auto_now_add=True, blank=True)),
('paid_date', self.gf('django.db.models.fields.DateField')(null=True, blank=True)),
('pay_sum', self.gf('django.db.models.fields.IntegerField')()),
('deleted', self.gf('django.db.models.fields.BooleanField')(default=False)),
))
db.send_create_signal(u'customer', ['License'])
# Adding model 'LicensePrice'
db.create_table(u'customer_licenseprice', (
(u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
('term', self.gf('django.db.models.fields.IntegerField')()),
('price', self.gf('django.db.models.fields.IntegerField')()),
))
db.send_create_signal(u'customer', ['LicensePrice'])
def backwards(self, orm):
# Deleting model 'UserProfile'
db.delete_table(u'customer_userprofile')
# Deleting model 'BankAccount'
db.delete_table(u'customer_bankaccount')
# Deleting model 'Client'
db.delete_table(u'customer_client')
# Deleting model 'UserProfileFilters'
db.delete_table(u'customer_userprofilefilters')
# Deleting model 'License'
db.delete_table(u'customer_license')
# Deleting model 'LicensePrice'
db.delete_table(u'customer_licenseprice')
models = {
u'customer.bankaccount': {
'Meta': {'ordering': "['-created_at']", 'object_name': 'BankAccount'},
'account': ('django.db.models.fields.CharField', [], {'max_length': '20'}),
'address': ('django.db.models.fields.CharField', [], {'max_length': '256'}),
'bik': ('django.db.models.fields.CharField', [], {'max_length': '10'}),
'company': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'bank_accounts'", 'to': u"orm['customer.UserProfile']"}),
'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_main': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'korr_account': ('django.db.models.fields.CharField', [], {'max_length': '20'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '256'}),
'updated_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'})
},
u'customer.client': {
'Meta': {'ordering': "['name', '-created_at']", 'object_name': 'Client'},
'address': ('django.db.models.fields.CharField', [], {'max_length': '256'}),
'bank_account': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '20', 'blank': 'True'}),
'bank_address': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256', 'blank': 'True'}),
'bank_bik': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '10', 'blank': 'True'}),
'bank_korr_account': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '20', 'blank': 'True'}),
'bank_name': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256', 'blank': 'True'}),
'company': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'clients'", 'to': u"orm['customer.UserProfile']"}),
'contact_email': ('django.db.models.fields.EmailField', [], {'default': "''", 'max_length': '50', 'blank': 'True'}),
'contact_icq': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '20', 'blank': 'True'}),
'contact_name': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '50', 'blank': 'True'}),
'contact_other': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256', 'blank': 'True'}),
'contact_phone': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '50', 'blank': 'True'}),
'contact_skype': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '20', 'blank': 'True'}),
'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'inn': ('django.db.models.fields.CharField', [], {'max_length': '12'}),
'kpp': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '9', 'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '256', 'db_index': 'True'}),
'okpo': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '10', 'blank': 'True'}),
'updated_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'})
},
u'customer.license': {
'Meta': {'object_name': 'License'},
'company': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'licenses'", 'to': u"orm['customer.UserProfile']"}),
'date_from': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
'date_to': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'order_date': ('django.db.models.fields.DateField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'paid_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
'pay_sum': ('django.db.models.fields.IntegerField', [], {}),
'payform': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'status': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'term': ('django.db.models.fields.IntegerField', [], {})
},
u'customer.licenseprice': {
'Meta': {'object_name': 'LicensePrice'},
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'price': ('django.db.models.fields.IntegerField', [], {}),
'term': ('django.db.models.fields.IntegerField', [], {})
},
u'customer.userprofile': {
'Meta': {'object_name': 'UserProfile'},
'active': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'address': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256'}),
'boss_midname': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '30'}),
'boss_name': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '30'}),
'boss_sign': ('django.db.models.fields.files.ImageField', [], {'default': "''", 'max_length': '100', 'blank': 'True'}),
'boss_surname': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '30'}),
'boss_title': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256', 'blank': 'True'}),
'confirmed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'email': ('django.db.models.fields.EmailField', [], {'default': "''", 'max_length': '75', 'blank': 'True'}),
'fax': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '20', 'blank': 'True'}),
'fax_code': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '10', 'blank': 'True'}),
'full_name': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256', 'blank': 'True'}),
'glavbuh_midname': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '30', 'blank': 'True'}),
'glavbuh_name': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '30', 'blank': 'True'}),
'glavbuh_sign': ('django.db.models.fields.files.ImageField', [], {'default': "''", 'max_length': '100', 'blank': 'True'}),
'glavbuh_surname': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '30', 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'inn': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '12'}),
'ip_reg_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
'jur_address': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256', 'blank': 'True'}),
'kpp': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '9'}),
'logo': ('django.db.models.fields.files.ImageField', [], {'default': "''", 'max_length': '100', 'blank': 'True'}),
'na_osnovanii': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256', 'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256'}),
'ogrn': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '15'}),
'okpo': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '10', 'blank': 'True'}),
'phone': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '20', 'blank': 'True'}),
'phone_code': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '10', 'blank': 'True'}),
'profile_type': ('django.db.models.fields.PositiveSmallIntegerField', [], {}),
'real_address': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256', 'blank': 'True'}),
'site': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256', 'blank': 'True'}),
'stamp': ('django.db.models.fields.files.ImageField', [], {'default': "''", 'max_length': '100', 'blank': 'True'}),
'svid_gos_reg': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256', 'blank': 'True'}),
'updated_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'user_session_key': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256', 'blank': 'True'})
},
u'customer.userprofilefilters': {
'Meta': {'object_name': 'UserProfileFilters'},
'bank_account': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'+'", 'null': 'True', 'blank': 'True', 'to': u"orm['customer.BankAccount']"}),
'company': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'profile_filters'", 'unique': 'True', 'primary_key': 'True', 'to': u"orm['customer.UserProfile']"}),
'show_bank_account': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'show_contact_info': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'show_email': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'show_fax': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'show_full_name': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'show_glavbuh': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'show_inn': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'show_ip_boss_fio': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'show_ip_reg_date': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'show_jur_address': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'show_kpp': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'show_na_osnovanii': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'show_name': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'show_ogrn': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'show_okpo': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'show_org_boss_title_and_fio': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'show_phone': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'show_profile_type': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'show_real_address': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'show_site': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'show_svid_gos_reg': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
}
}
complete_apps = ['customer']

@ -0,0 +1,147 @@
# -*- coding: utf-8 -*-
from south.utils import datetime_utils as datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
class Migration(SchemaMigration):
def forwards(self, orm):
# Adding field 'UserProfileFilters.show_logo'
db.add_column(u'customer_userprofilefilters', 'show_logo',
self.gf('django.db.models.fields.BooleanField')(default=True),
keep_default=False)
def backwards(self, orm):
# Deleting field 'UserProfileFilters.show_logo'
db.delete_column(u'customer_userprofilefilters', 'show_logo')
models = {
u'customer.bankaccount': {
'Meta': {'ordering': "['-created_at']", 'object_name': 'BankAccount'},
'account': ('django.db.models.fields.CharField', [], {'max_length': '20'}),
'address': ('django.db.models.fields.CharField', [], {'max_length': '256'}),
'bik': ('django.db.models.fields.CharField', [], {'max_length': '10'}),
'company': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'bank_accounts'", 'to': u"orm['customer.UserProfile']"}),
'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_main': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'korr_account': ('django.db.models.fields.CharField', [], {'max_length': '20'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '256'}),
'updated_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'})
},
u'customer.client': {
'Meta': {'ordering': "['name', '-created_at']", 'object_name': 'Client'},
'address': ('django.db.models.fields.CharField', [], {'max_length': '256'}),
'bank_account': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '20', 'blank': 'True'}),
'bank_address': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256', 'blank': 'True'}),
'bank_bik': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '10', 'blank': 'True'}),
'bank_korr_account': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '20', 'blank': 'True'}),
'bank_name': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256', 'blank': 'True'}),
'company': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'clients'", 'to': u"orm['customer.UserProfile']"}),
'contact_email': ('django.db.models.fields.EmailField', [], {'default': "''", 'max_length': '50', 'blank': 'True'}),
'contact_icq': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '20', 'blank': 'True'}),
'contact_name': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '50', 'blank': 'True'}),
'contact_other': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256', 'blank': 'True'}),
'contact_phone': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '50', 'blank': 'True'}),
'contact_skype': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '20', 'blank': 'True'}),
'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'inn': ('django.db.models.fields.CharField', [], {'max_length': '12'}),
'kpp': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '9', 'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '256', 'db_index': 'True'}),
'okpo': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '10', 'blank': 'True'}),
'updated_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'})
},
u'customer.license': {
'Meta': {'object_name': 'License'},
'company': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'licenses'", 'to': u"orm['customer.UserProfile']"}),
'date_from': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
'date_to': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'order_date': ('django.db.models.fields.DateField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'paid_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
'pay_sum': ('django.db.models.fields.IntegerField', [], {}),
'payform': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'status': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'term': ('django.db.models.fields.IntegerField', [], {})
},
u'customer.licenseprice': {
'Meta': {'object_name': 'LicensePrice'},
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'price': ('django.db.models.fields.IntegerField', [], {}),
'term': ('django.db.models.fields.IntegerField', [], {})
},
u'customer.userprofile': {
'Meta': {'object_name': 'UserProfile'},
'active': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'address': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256'}),
'boss_midname': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '30'}),
'boss_name': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '30'}),
'boss_sign': ('django.db.models.fields.files.ImageField', [], {'default': "''", 'max_length': '100', 'blank': 'True'}),
'boss_surname': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '30'}),
'boss_title': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256', 'blank': 'True'}),
'confirmed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'email': ('django.db.models.fields.EmailField', [], {'default': "''", 'max_length': '75', 'blank': 'True'}),
'fax': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '20', 'blank': 'True'}),
'fax_code': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '10', 'blank': 'True'}),
'full_name': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256', 'blank': 'True'}),
'glavbuh_midname': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '30', 'blank': 'True'}),
'glavbuh_name': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '30', 'blank': 'True'}),
'glavbuh_sign': ('django.db.models.fields.files.ImageField', [], {'default': "''", 'max_length': '100', 'blank': 'True'}),
'glavbuh_surname': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '30', 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'inn': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '12'}),
'ip_reg_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
'jur_address': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256', 'blank': 'True'}),
'kpp': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '9'}),
'logo': ('django.db.models.fields.files.ImageField', [], {'default': "''", 'max_length': '100', 'blank': 'True'}),
'na_osnovanii': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256', 'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256'}),
'ogrn': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '15'}),
'okpo': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '10', 'blank': 'True'}),
'phone': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '20', 'blank': 'True'}),
'phone_code': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '10', 'blank': 'True'}),
'profile_type': ('django.db.models.fields.PositiveSmallIntegerField', [], {}),
'real_address': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256', 'blank': 'True'}),
'site': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256', 'blank': 'True'}),
'stamp': ('django.db.models.fields.files.ImageField', [], {'default': "''", 'max_length': '100', 'blank': 'True'}),
'svid_gos_reg': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256', 'blank': 'True'}),
'updated_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'user_session_key': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256', 'blank': 'True'})
},
u'customer.userprofilefilters': {
'Meta': {'object_name': 'UserProfileFilters'},
'bank_account': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'+'", 'null': 'True', 'blank': 'True', 'to': u"orm['customer.BankAccount']"}),
'company': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'profile_filters'", 'unique': 'True', 'primary_key': 'True', 'to': u"orm['customer.UserProfile']"}),
'show_bank_account': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'show_contact_info': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'show_email': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'show_fax': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'show_full_name': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'show_glavbuh': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'show_inn': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'show_ip_boss_fio': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'show_ip_reg_date': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'show_jur_address': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'show_kpp': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'show_logo': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'show_na_osnovanii': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'show_name': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'show_ogrn': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'show_okpo': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'show_org_boss_title_and_fio': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'show_phone': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'show_profile_type': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'show_real_address': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'show_site': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'show_svid_gos_reg': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
}
}
complete_apps = ['customer']

@ -0,0 +1,148 @@
# -*- coding: utf-8 -*-
from south.utils import datetime_utils as datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
class Migration(SchemaMigration):
def forwards(self, orm):
# Adding field 'UserProfileFilters.show_address'
db.add_column(u'customer_userprofilefilters', 'show_address',
self.gf('django.db.models.fields.BooleanField')(default=True),
keep_default=False)
def backwards(self, orm):
# Deleting field 'UserProfileFilters.show_address'
db.delete_column(u'customer_userprofilefilters', 'show_address')
models = {
u'customer.bankaccount': {
'Meta': {'ordering': "['-created_at']", 'object_name': 'BankAccount'},
'account': ('django.db.models.fields.CharField', [], {'max_length': '20'}),
'address': ('django.db.models.fields.CharField', [], {'max_length': '256'}),
'bik': ('django.db.models.fields.CharField', [], {'max_length': '10'}),
'company': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'bank_accounts'", 'to': u"orm['customer.UserProfile']"}),
'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_main': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'korr_account': ('django.db.models.fields.CharField', [], {'max_length': '20'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '256'}),
'updated_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'})
},
u'customer.client': {
'Meta': {'ordering': "['name', '-created_at']", 'object_name': 'Client'},
'address': ('django.db.models.fields.CharField', [], {'max_length': '256'}),
'bank_account': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '20', 'blank': 'True'}),
'bank_address': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256', 'blank': 'True'}),
'bank_bik': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '10', 'blank': 'True'}),
'bank_korr_account': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '20', 'blank': 'True'}),
'bank_name': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256', 'blank': 'True'}),
'company': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'clients'", 'to': u"orm['customer.UserProfile']"}),
'contact_email': ('django.db.models.fields.EmailField', [], {'default': "''", 'max_length': '50', 'blank': 'True'}),
'contact_icq': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '20', 'blank': 'True'}),
'contact_name': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '50', 'blank': 'True'}),
'contact_other': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256', 'blank': 'True'}),
'contact_phone': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '50', 'blank': 'True'}),
'contact_skype': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '20', 'blank': 'True'}),
'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'inn': ('django.db.models.fields.CharField', [], {'max_length': '12'}),
'kpp': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '9', 'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '256', 'db_index': 'True'}),
'okpo': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '10', 'blank': 'True'}),
'updated_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'})
},
u'customer.license': {
'Meta': {'object_name': 'License'},
'company': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'licenses'", 'to': u"orm['customer.UserProfile']"}),
'date_from': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
'date_to': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'order_date': ('django.db.models.fields.DateField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'paid_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
'pay_sum': ('django.db.models.fields.IntegerField', [], {}),
'payform': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'status': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'term': ('django.db.models.fields.IntegerField', [], {})
},
u'customer.licenseprice': {
'Meta': {'object_name': 'LicensePrice'},
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'price': ('django.db.models.fields.IntegerField', [], {}),
'term': ('django.db.models.fields.IntegerField', [], {})
},
u'customer.userprofile': {
'Meta': {'object_name': 'UserProfile'},
'active': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'address': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256'}),
'boss_midname': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '30'}),
'boss_name': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '30'}),
'boss_sign': ('django.db.models.fields.files.ImageField', [], {'default': "''", 'max_length': '100', 'blank': 'True'}),
'boss_surname': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '30'}),
'boss_title': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256', 'blank': 'True'}),
'confirmed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'email': ('django.db.models.fields.EmailField', [], {'default': "''", 'max_length': '75', 'blank': 'True'}),
'fax': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '20', 'blank': 'True'}),
'fax_code': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '10', 'blank': 'True'}),
'full_name': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256', 'blank': 'True'}),
'glavbuh_midname': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '30', 'blank': 'True'}),
'glavbuh_name': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '30', 'blank': 'True'}),
'glavbuh_sign': ('django.db.models.fields.files.ImageField', [], {'default': "''", 'max_length': '100', 'blank': 'True'}),
'glavbuh_surname': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '30', 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'inn': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '12'}),
'ip_reg_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
'jur_address': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256', 'blank': 'True'}),
'kpp': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '9'}),
'logo': ('django.db.models.fields.files.ImageField', [], {'default': "''", 'max_length': '100', 'blank': 'True'}),
'na_osnovanii': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256', 'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256'}),
'ogrn': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '15'}),
'okpo': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '10', 'blank': 'True'}),
'phone': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '20', 'blank': 'True'}),
'phone_code': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '10', 'blank': 'True'}),
'profile_type': ('django.db.models.fields.PositiveSmallIntegerField', [], {}),
'real_address': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256', 'blank': 'True'}),
'site': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256', 'blank': 'True'}),
'stamp': ('django.db.models.fields.files.ImageField', [], {'default': "''", 'max_length': '100', 'blank': 'True'}),
'svid_gos_reg': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256', 'blank': 'True'}),
'updated_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'user_session_key': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256', 'blank': 'True'})
},
u'customer.userprofilefilters': {
'Meta': {'object_name': 'UserProfileFilters'},
'bank_account': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'+'", 'null': 'True', 'blank': 'True', 'to': u"orm['customer.BankAccount']"}),
'company': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'profile_filters'", 'unique': 'True', 'primary_key': 'True', 'to': u"orm['customer.UserProfile']"}),
'show_address': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'show_bank_account': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'show_contact_info': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'show_email': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'show_fax': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'show_full_name': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'show_glavbuh': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'show_inn': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'show_ip_boss_fio': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'show_ip_reg_date': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'show_jur_address': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'show_kpp': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'show_logo': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'show_na_osnovanii': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'show_name': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'show_ogrn': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'show_okpo': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'show_org_boss_title_and_fio': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'show_phone': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'show_profile_type': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'show_real_address': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'show_site': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'show_svid_gos_reg': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
}
}
complete_apps = ['customer']

@ -0,0 +1,156 @@
# -*- coding: utf-8 -*-
from south.utils import datetime_utils as datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
class Migration(SchemaMigration):
def forwards(self, orm):
# Deleting field 'Client.contact_icq'
db.delete_column(u'customer_client', 'contact_icq')
# Adding field 'Client.ogrn'
db.add_column(u'customer_client', 'ogrn',
self.gf('django.db.models.fields.CharField')(default='', max_length=15),
keep_default=False)
def backwards(self, orm):
# Adding field 'Client.contact_icq'
db.add_column(u'customer_client', 'contact_icq',
self.gf('django.db.models.fields.CharField')(default='', max_length=20, blank=True),
keep_default=False)
# Deleting field 'Client.ogrn'
db.delete_column(u'customer_client', 'ogrn')
models = {
u'customer.bankaccount': {
'Meta': {'ordering': "['-created_at']", 'object_name': 'BankAccount'},
'account': ('django.db.models.fields.CharField', [], {'max_length': '20'}),
'address': ('django.db.models.fields.CharField', [], {'max_length': '256'}),
'bik': ('django.db.models.fields.CharField', [], {'max_length': '10'}),
'company': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'bank_accounts'", 'to': u"orm['customer.UserProfile']"}),
'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_main': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'korr_account': ('django.db.models.fields.CharField', [], {'max_length': '20'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '256'}),
'updated_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'})
},
u'customer.client': {
'Meta': {'ordering': "['name', '-created_at']", 'object_name': 'Client'},
'address': ('django.db.models.fields.CharField', [], {'max_length': '256'}),
'bank_account': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '20', 'blank': 'True'}),
'bank_address': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256', 'blank': 'True'}),
'bank_bik': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '10', 'blank': 'True'}),
'bank_korr_account': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '20', 'blank': 'True'}),
'bank_name': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256', 'blank': 'True'}),
'company': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'clients'", 'to': u"orm['customer.UserProfile']"}),
'contact_email': ('django.db.models.fields.EmailField', [], {'default': "''", 'max_length': '50', 'blank': 'True'}),
'contact_name': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '50', 'blank': 'True'}),
'contact_other': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256', 'blank': 'True'}),
'contact_phone': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '50', 'blank': 'True'}),
'contact_skype': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '20', 'blank': 'True'}),
'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'inn': ('django.db.models.fields.CharField', [], {'max_length': '12'}),
'kpp': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '9', 'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '256', 'db_index': 'True'}),
'ogrn': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '15'}),
'okpo': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '10', 'blank': 'True'}),
'updated_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'})
},
u'customer.license': {
'Meta': {'object_name': 'License'},
'company': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'licenses'", 'to': u"orm['customer.UserProfile']"}),
'date_from': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
'date_to': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'order_date': ('django.db.models.fields.DateField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'paid_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
'pay_sum': ('django.db.models.fields.IntegerField', [], {}),
'payform': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'status': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'term': ('django.db.models.fields.IntegerField', [], {})
},
u'customer.licenseprice': {
'Meta': {'object_name': 'LicensePrice'},
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'price': ('django.db.models.fields.IntegerField', [], {}),
'term': ('django.db.models.fields.IntegerField', [], {})
},
u'customer.userprofile': {
'Meta': {'object_name': 'UserProfile'},
'active': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'address': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256'}),
'boss_midname': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '30'}),
'boss_name': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '30'}),
'boss_sign': ('django.db.models.fields.files.ImageField', [], {'default': "''", 'max_length': '100', 'blank': 'True'}),
'boss_surname': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '30'}),
'boss_title': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256', 'blank': 'True'}),
'confirmed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'email': ('django.db.models.fields.EmailField', [], {'default': "''", 'max_length': '75', 'blank': 'True'}),
'fax': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '20', 'blank': 'True'}),
'fax_code': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '10', 'blank': 'True'}),
'full_name': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256', 'blank': 'True'}),
'glavbuh_midname': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '30', 'blank': 'True'}),
'glavbuh_name': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '30', 'blank': 'True'}),
'glavbuh_sign': ('django.db.models.fields.files.ImageField', [], {'default': "''", 'max_length': '100', 'blank': 'True'}),
'glavbuh_surname': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '30', 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'inn': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '12'}),
'ip_reg_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
'jur_address': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256', 'blank': 'True'}),
'kpp': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '9'}),
'logo': ('django.db.models.fields.files.ImageField', [], {'default': "''", 'max_length': '100', 'blank': 'True'}),
'na_osnovanii': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256', 'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256'}),
'ogrn': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '15'}),
'okpo': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '10', 'blank': 'True'}),
'phone': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '20', 'blank': 'True'}),
'phone_code': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '10', 'blank': 'True'}),
'profile_type': ('django.db.models.fields.PositiveSmallIntegerField', [], {}),
'real_address': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256', 'blank': 'True'}),
'site': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256', 'blank': 'True'}),
'stamp': ('django.db.models.fields.files.ImageField', [], {'default': "''", 'max_length': '100', 'blank': 'True'}),
'svid_gos_reg': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256', 'blank': 'True'}),
'updated_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'user_session_key': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256', 'blank': 'True'})
},
u'customer.userprofilefilters': {
'Meta': {'object_name': 'UserProfileFilters'},
'bank_account': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'+'", 'null': 'True', 'blank': 'True', 'to': u"orm['customer.BankAccount']"}),
'company': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'profile_filters'", 'unique': 'True', 'primary_key': 'True', 'to': u"orm['customer.UserProfile']"}),
'show_address': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'show_bank_account': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'show_contact_info': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'show_email': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'show_fax': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'show_full_name': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'show_glavbuh': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'show_inn': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'show_ip_boss_fio': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'show_ip_reg_date': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'show_jur_address': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'show_kpp': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'show_logo': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'show_na_osnovanii': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'show_name': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'show_ogrn': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'show_okpo': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'show_org_boss_title_and_fio': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'show_phone': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'show_profile_type': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'show_real_address': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'show_site': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'show_svid_gos_reg': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
}
}
complete_apps = ['customer']

@ -0,0 +1,149 @@
# -*- coding: utf-8 -*-
from south.utils import datetime_utils as datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
class Migration(SchemaMigration):
def forwards(self, orm):
# Adding field 'BankAccount.short_name'
db.add_column(u'customer_bankaccount', 'short_name',
self.gf('django.db.models.fields.CharField')(default='', max_length=100, blank=True),
keep_default=False)
def backwards(self, orm):
# Deleting field 'BankAccount.short_name'
db.delete_column(u'customer_bankaccount', 'short_name')
models = {
u'customer.bankaccount': {
'Meta': {'ordering': "['-created_at']", 'object_name': 'BankAccount'},
'account': ('django.db.models.fields.CharField', [], {'max_length': '20'}),
'address': ('django.db.models.fields.CharField', [], {'max_length': '256'}),
'bik': ('django.db.models.fields.CharField', [], {'max_length': '10'}),
'company': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'bank_accounts'", 'to': u"orm['customer.UserProfile']"}),
'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_main': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'korr_account': ('django.db.models.fields.CharField', [], {'max_length': '20'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '256'}),
'short_name': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '100', 'blank': 'True'}),
'updated_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'})
},
u'customer.client': {
'Meta': {'ordering': "['name', '-created_at']", 'object_name': 'Client'},
'address': ('django.db.models.fields.CharField', [], {'max_length': '256'}),
'bank_account': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '20', 'blank': 'True'}),
'bank_address': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256', 'blank': 'True'}),
'bank_bik': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '10', 'blank': 'True'}),
'bank_korr_account': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '20', 'blank': 'True'}),
'bank_name': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256', 'blank': 'True'}),
'company': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'clients'", 'to': u"orm['customer.UserProfile']"}),
'contact_email': ('django.db.models.fields.EmailField', [], {'default': "''", 'max_length': '50', 'blank': 'True'}),
'contact_name': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '50', 'blank': 'True'}),
'contact_other': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256', 'blank': 'True'}),
'contact_phone': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '50', 'blank': 'True'}),
'contact_skype': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '20', 'blank': 'True'}),
'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'inn': ('django.db.models.fields.CharField', [], {'max_length': '12'}),
'kpp': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '9', 'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '256', 'db_index': 'True'}),
'ogrn': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '15'}),
'okpo': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '10', 'blank': 'True'}),
'updated_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'})
},
u'customer.license': {
'Meta': {'object_name': 'License'},
'company': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'licenses'", 'to': u"orm['customer.UserProfile']"}),
'date_from': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
'date_to': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'order_date': ('django.db.models.fields.DateField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'paid_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
'pay_sum': ('django.db.models.fields.IntegerField', [], {}),
'payform': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'status': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'term': ('django.db.models.fields.IntegerField', [], {})
},
u'customer.licenseprice': {
'Meta': {'object_name': 'LicensePrice'},
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'price': ('django.db.models.fields.IntegerField', [], {}),
'term': ('django.db.models.fields.IntegerField', [], {})
},
u'customer.userprofile': {
'Meta': {'object_name': 'UserProfile'},
'active': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'address': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256'}),
'boss_midname': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '30'}),
'boss_name': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '30'}),
'boss_sign': ('django.db.models.fields.files.ImageField', [], {'default': "''", 'max_length': '100', 'blank': 'True'}),
'boss_surname': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '30'}),
'boss_title': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256', 'blank': 'True'}),
'confirmed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'email': ('django.db.models.fields.EmailField', [], {'default': "''", 'max_length': '75', 'blank': 'True'}),
'fax': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '20', 'blank': 'True'}),
'fax_code': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '10', 'blank': 'True'}),
'full_name': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256', 'blank': 'True'}),
'glavbuh_midname': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '30', 'blank': 'True'}),
'glavbuh_name': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '30', 'blank': 'True'}),
'glavbuh_sign': ('django.db.models.fields.files.ImageField', [], {'default': "''", 'max_length': '100', 'blank': 'True'}),
'glavbuh_surname': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '30', 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'inn': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '12'}),
'ip_reg_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
'jur_address': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256', 'blank': 'True'}),
'kpp': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '9'}),
'logo': ('django.db.models.fields.files.ImageField', [], {'default': "''", 'max_length': '100', 'blank': 'True'}),
'na_osnovanii': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256', 'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256'}),
'ogrn': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '15'}),
'okpo': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '10', 'blank': 'True'}),
'phone': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '20', 'blank': 'True'}),
'phone_code': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '10', 'blank': 'True'}),
'profile_type': ('django.db.models.fields.PositiveSmallIntegerField', [], {}),
'real_address': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256', 'blank': 'True'}),
'site': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256', 'blank': 'True'}),
'stamp': ('django.db.models.fields.files.ImageField', [], {'default': "''", 'max_length': '100', 'blank': 'True'}),
'svid_gos_reg': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256', 'blank': 'True'}),
'updated_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'user_session_key': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256', 'blank': 'True'})
},
u'customer.userprofilefilters': {
'Meta': {'object_name': 'UserProfileFilters'},
'bank_account': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'+'", 'null': 'True', 'blank': 'True', 'to': u"orm['customer.BankAccount']"}),
'company': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'profile_filters'", 'unique': 'True', 'primary_key': 'True', 'to': u"orm['customer.UserProfile']"}),
'show_address': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'show_bank_account': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'show_contact_info': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'show_email': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'show_fax': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'show_full_name': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'show_glavbuh': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'show_inn': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'show_ip_boss_fio': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'show_ip_reg_date': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'show_jur_address': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'show_kpp': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'show_logo': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'show_na_osnovanii': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'show_name': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'show_ogrn': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'show_okpo': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'show_org_boss_title_and_fio': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'show_phone': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'show_profile_type': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'show_real_address': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'show_site': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'show_svid_gos_reg': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
}
}
complete_apps = ['customer']

@ -0,0 +1,516 @@
# -*- coding: utf-8 -*-
import os
from datetime import datetime, timedelta
from PIL import Image
from pytils import numeral
from dateutil.relativedelta import relativedelta
from django.db import models
from django.conf import settings
from django.db.models import Max
from django.core.urlresolvers import reverse
from . import consts, managers, utils
from project.myauth.models import DokUser
from project.commons.utils import only_numerics
PROFILE_IMAGES_UPLOAD_DIR = 'customer/profile/' # куда сохранять загруженные изображения
BOSS_SIGN_IMG_SIZE = (159, 65)
GLAVBUH_SIGN_IMG_SIZE = (159, 65)
STAMP_IMG_SIZE = (180, 180)
LOGO_SIZE = (351, 121)
def get_profile(user):
"""Возвращает профиль пользователя или None."""
try:
return user.profile
except:
return None
def upload_to(path, new_filename=None):
"""Куда и под каким именем сохранить загруженный файл."""
def get_upload_path(instance, filename):
filename = new_filename or filename
try:
profile_dir = instance.get_first_user().username
except:
profile_dir = 'NoUser'
return os.path.join(path, profile_dir, filename)
return get_upload_path
class UserProfile(models.Model):
"""Профиль пользователя."""
profile_type = models.PositiveSmallIntegerField(u'Тип профиля', choices=consts.PROFILE_TYPES)
# общие поля
boss_surname = models.CharField(u'Фамилия', max_length=30, default='',
help_text=u'Используется для строки "подпись" в документах.')
boss_name = models.CharField(u'Имя', max_length=30, default='')
boss_midname = models.CharField(u'Отчество', max_length=30, default='')
inn = models.CharField(u'ИНН', max_length=12, default='') # длина: 10 для организаций, 12 для ИП
ogrn = models.CharField(u'ОГРН/ОГРНИП', max_length=15, default='') # длина: 13 для организаций, 15 для ИП
okpo = models.CharField(u'ОКПО', max_length=10, blank=True, default='') # длина: 8 для организаций, 8 или 10 для ИП
glavbuh_surname = models.CharField(u'Фамилия', max_length=30, blank=True, default='',
help_text=u'Используется для строки "подпись" в документах.')
glavbuh_name = models.CharField(u'Имя', max_length=30, blank=True, default='')
glavbuh_midname = models.CharField(u'Отчество', max_length=30, blank=True, default='')
address = models.CharField(u'Фактический адрес', max_length=256, default='',
help_text=u'Будет подставляться в создаваемые счета, акты и накладные.')
jur_address = models.CharField(u'Юридический адрес', max_length=256, blank=True, default='',
help_text=u'Как в учредительных документах.')
real_address = models.CharField(u'Почтовый адрес', max_length=256, blank=True, default='',
help_text=u'Используется только для карточки компании.')
phone_code = models.CharField(u'Код города', max_length=10, blank=True, default='')
phone = models.CharField(u'Номер телефона', max_length=20, blank=True, default='')
fax_code = models.CharField(u'Код города', max_length=10, blank=True, default='')
fax = models.CharField(u'Номер телефона', max_length=20, blank=True, default='')
email = models.EmailField(u'Электронная почта', max_length=75, blank=True, default='')
site = models.CharField(u'Сайт', max_length=256, blank=True, default='')
# поля, только для ИП
svid_gos_reg = models.CharField(u'Свид-во о гос. регистрации', max_length=256, blank=True, default='',
help_text=u'Требуется для счет-фактуры.')
ip_reg_date = models.DateField(u'Дата регистрации ИП', blank=True, null=True)
# поля, только для Организации
name = models.CharField(u'Краткое название организации', max_length=256, default='',
help_text=u'Будет подставляться в создаваемые документы.')
full_name = models.CharField(u'Полное название организации', max_length=256, blank=True, default='',
help_text=u'Как в учредительных документах.')
kpp = models.CharField(u'КПП', max_length=9, default='')
boss_title = models.CharField(u'Должность руководителя', max_length=256, blank=True, default='')
na_osnovanii = models.CharField(u'Действует на основании', max_length=256, blank=True, default='')
# подписи, печать и логотип
boss_sign = models.ImageField(u'Подпись руководителя', blank=True, default='',
upload_to=upload_to(PROFILE_IMAGES_UPLOAD_DIR, 'boss_sign.bmp'))
glavbuh_sign = models.ImageField(u'Подпись бухгалтера', blank=True, default='',
upload_to=upload_to(PROFILE_IMAGES_UPLOAD_DIR, 'glavbuh_sign.bmp'))
stamp = models.ImageField(u'Печать', blank=True, default='',
upload_to=upload_to(PROFILE_IMAGES_UPLOAD_DIR, 'stamp.bmp'))
logo = models.ImageField(u'Логотип', blank=True, default='',
upload_to=upload_to(PROFILE_IMAGES_UPLOAD_DIR, 'logo.bmp'))
created_at = models.DateTimeField(u'Создан', auto_now_add=True)
updated_at = models.DateTimeField(u'Изменен', auto_now=True)
active = models.BooleanField(u'Активен', default=False)
confirmed = models.BooleanField(u'Подтверждён', default=False)
user_session_key = models.CharField(u'Ключ сессии (служебная информация)', max_length=256, blank=True, default='',
help_text=u'Руками не тро...')
objects = managers.UserProfileManager()
class Meta:
verbose_name = u'Реквизиты (профиль)'
verbose_name_plural = u'Реквизиты (профили)'
def __unicode__(self):
return u'%s, ИНН %s' % (self.get_company_name()[0:30], self.inn or u'не указан')
def save(self, *args, **kwargs):
self.inn = only_numerics(self.inn)
self.ogrn = only_numerics(self.ogrn)
self.okpo = only_numerics(self.okpo)
self.kpp = only_numerics(self.kpp)
def process_img(orig_img, size):
w = orig_img.width
h = orig_img.height
if w > size[0] or h > size[1]:
filename = str(orig_img.path)
img = Image.open(filename).convert("RGB")
img.thumbnail(size, Image.ANTIALIAS)
img.save(filename, 'BMP')
super(UserProfile, self).save(*args, **kwargs)
if self.boss_sign:
process_img(self.boss_sign, size=BOSS_SIGN_IMG_SIZE)
if self.glavbuh_sign:
process_img(self.glavbuh_sign, size=GLAVBUH_SIGN_IMG_SIZE)
if self.stamp:
process_img(self.stamp, size=STAMP_IMG_SIZE)
if self.logo:
process_img(self.logo, size=LOGO_SIZE)
def is_ip(self):
return self.profile_type == consts.IP_PROFILE
def is_org(self):
return self.profile_type == consts.ORG_PROFILE
def check_name_not_filled(self):
"""`ИП ФИО` или `Название Организации`."""
if self.is_ip():
return self.get_boss_full_fio().strip() == ''
elif self.is_org():
return self.name.strip() == ''
return False
def get_main_bank_account(self):
try:
bank_accounts = BankAccount.objects.filter(company=self, is_main=True)[0]
except:
return None
def get_first_user(self):
try:
first_user = DokUser.objects.filter(profile=self)[0]
return first_user
except:
return None
def check_main_reqs_not_filled(self):
result = self.check_name_not_filled() or self.inn == '' or self.address == '' or \
self.get_boss_fio() == '' or self.get_main_bank_account() == ''
if result:
return True
if self.is_ip():
return self.ogrn == ''
elif self.is_org():
return self.kpp == ''
def get_company_name(self):
"""`ИП ФИО` или `Название Организации`."""
if self.profile_type == consts.IP_PROFILE:
return u'ИП %s' % self.get_boss_full_fio()
elif self.profile_type == consts.ORG_PROFILE:
return self.name.strip()
return u''
def get_inn_and_kpp(self):
"""Возвращает пару ИНН/КПП или только ИНН, если это ИП или КПП не заполнен."""
if self.profile_type == consts.ORG_PROFILE:
kpp = self.kpp.strip()
if kpp:
return u'%s/%s' % (self.inn, kpp,)
return self.inn
def get_boss_title(self):
"""Текст 'Индивидуальный предприниматель' или 'Руководитель организации'."""
if self.profile_type == consts.IP_PROFILE:
return u'Индивидуальный предприниматель'
elif self.profile_type == consts.ORG_PROFILE:
return u'Руководитель организации'
return u''
def get_boss_fio(self):
"""Фамилия и инициалы руководителя ИП/организации."""
if self.boss_surname and self.boss_name and self.boss_midname:
return u'%s %s.%s.' % (self.boss_surname, self.boss_name[0], self.boss_midname[0],)
return u''
def get_boss_full_fio(self):
"""Полное ФИО руководителя ИП/организации."""
return (u'%s %s %s' % (self.boss_surname, self.boss_name, self.boss_midname,)).strip()
def get_glavbuh_fio(self):
"""Фамилия и инициалы главного бухгалтера."""
if self.glavbuh_surname and self.glavbuh_name and self.glavbuh_midname:
return (u'%s %s. %s.' % (self.glavbuh_surname, self.glavbuh_name[0], self.glavbuh_midname[0],)).strip()
return u''
def get_glavbuh_full_fio(self):
"""Полное ФИО главного бухгалтера."""
return (u'%s %s %s' % (self.glavbuh_surname, self.glavbuh_name, self.glavbuh_midname,)).strip()
def get_full_phone(self):
"""(Код города) Номер телефона."""
phone_code = self.phone_code.strip('() ')
phone_code = u'(%s)' % phone_code if phone_code else phone_code
return (u'%s %s' % (phone_code, self.phone,)).strip()
def get_email(self):
try:
return self.get_first_user().email
except:
return None
def get_full_fax(self):
"""(Код города) Номер факса."""
fax_code = self.fax_code.strip('() ')
fax_code = u'(%s)' % fax_code if fax_code else fax_code
return (u'%s %s' % (fax_code, self.fax,)).strip()
class BankAccount(models.Model):
"""Расчетные счета."""
company = models.ForeignKey(UserProfile, related_name='bank_accounts')
bik = models.CharField(u'БИК', max_length=10)
name = models.CharField(u'Наименование банка', max_length=256)
short_name = models.CharField(u'Сокращенное название банка', max_length=100, blank=True, default='')
address = models.CharField(u'Местонахождение', max_length=256, blank=True, default='') # TODO delete field?
korr_account = models.CharField(u'Корр. счет', max_length=20)
account = models.CharField(u'Расчетный счет', max_length=20)
is_main = models.BooleanField(u'Основной счет', default=False)
created_at = models.DateTimeField(u'Создан', auto_now_add=True)
updated_at = models.DateTimeField(u'Изменен', auto_now=True)
objects = managers.BankAccountManager()
class Meta:
verbose_name = u'Расчётный счет'
verbose_name_plural = u'Расчётные счета'
ordering = ['-created_at']
def __unicode__(self):
return (u'%s, %s' % (self.account, self.short_name[0:30] or self.name[0:30],)).strip()
def save(self, *args, **kwargs):
self.bik = only_numerics(self.bik)
self.korr_account = only_numerics(self.korr_account)
self.account = only_numerics(self.account)
super(BankAccount, self).save(*args, **kwargs)
if self.is_main:
# если задано, что это будет основной счет, то сбросить у остальных счетов пользователя этот признак
BankAccount.objects.filter(company=self.company, is_main=True).exclude(pk=self.pk).update(is_main=False)
else:
# если нет основного счета, то установить его принудительно
BankAccount.objects.force_main(company=self.company)
def delete(self, *args, **kwargs):
super(BankAccount, self).delete(*args, **kwargs)
# если нет основного счета, то установить его принудительно
BankAccount.objects.force_main(company=self.company)
class Client(models.Model):
"""Контрагенты."""
company = models.ForeignKey(UserProfile, related_name='clients')
name = models.CharField(u'Наименование', max_length=256, db_index=True)
inn = models.CharField(u'ИНН', max_length=12)
kpp = models.CharField(u'КПП', max_length=9, blank=True, default='') # Организация
ogrn = models.CharField(u'ОГРН', max_length=15, default='')
okpo = models.CharField(u'ОКПО', max_length=10, blank=True, default='') # ИП
address = models.CharField(u'Юр. адрес', max_length=256)
# банковские реквизиты
bank_bik = models.CharField(u'БИК', max_length=10, blank=True, default='')
bank_name = models.CharField(u'Наименование банка', max_length=256, blank=True, default='')
bank_address = models.CharField(u'Местонахождение', max_length=256, blank=True, default='') # TODO delete field?
bank_korr_account = models.CharField(u'Корр. счет', max_length=20, blank=True, default='')
bank_account = models.CharField(u'Расчетный счет', max_length=20, blank=True, default='')
# контакты
contact_name = models.CharField(u'Имя', max_length=50, blank=True, default='')
contact_email = models.EmailField(u'E-mail', max_length=50, blank=True, default='')
contact_phone = models.CharField(u'Телефон', max_length=50, blank=True, default='')
contact_skype = models.CharField(u'Skype', max_length=20, blank=True, default='')
contact_other = models.CharField(u'Другое', max_length=256, blank=True, default='')
created_at = models.DateTimeField(u'Создан', auto_now_add=True)
updated_at = models.DateTimeField(u'Изменен', auto_now=True)
objects = managers.ClientManager()
class Meta:
verbose_name = u'Контрагент'
verbose_name_plural = u'Контрагенты'
ordering = ['name', '-created_at']
def __unicode__(self):
return (u'%s, ИНН %s' % (self.name[0:30], self.inn or u'не указан',)).strip()
def save(self, *args, **kwargs):
self.inn = only_numerics(self.inn)
self.kpp = only_numerics(self.kpp)
self.ogrn = only_numerics(self.ogrn)
self.okpo = only_numerics(self.okpo)
self.bank_bik = only_numerics(self.bank_bik)
self.bank_korr_account = only_numerics(self.bank_korr_account)
self.bank_account = only_numerics(self.bank_account)
super(Client, self).save(*args, **kwargs)
def get_inn_and_kpp(self):
"""Возвращает пару ИНН/КПП или только ИНН, если КПП не заполнен."""
kpp = self.kpp.strip()
if kpp:
return u'%s/%s' % (self.inn, kpp,)
return self.inn
class UserProfileFilters(models.Model):
"""Фильтрация реквизитов: какие данные показывать/скрывать при генерации карточки компании."""
company = models.OneToOneField(UserProfile, related_name='profile_filters', primary_key=True)
# общие фильтры
show_profile_type = models.BooleanField(u'Тип профиля', default=True)
show_inn = models.BooleanField(u'ИНН', default=True)
show_ogrn = models.BooleanField(u'ОГРН/ОГРНИП', default=True)
show_okpo = models.BooleanField(u'ОКПО', default=True)
show_glavbuh = models.BooleanField(u'Главный бухгалтер', default=True)
show_bank_account = models.BooleanField(u'Банковские реквизиты', default=True)
bank_account = models.ForeignKey(BankAccount, related_name='+', verbose_name=u'Расчетный счет', blank=True,
null=True, default=None)
show_contact_info = models.BooleanField(u'Контактная информация', default=True)
show_address = models.BooleanField(u'Фактический адрес', default=True)
show_jur_address = models.BooleanField(u'Юридический адрес', default=True)
show_real_address = models.BooleanField(u'Почтовый адрес', default=True)
show_phone = models.BooleanField(u'Телефон', default=True)
show_fax = models.BooleanField(u'Факс', default=True)
show_email = models.BooleanField(u'Электронная почта', default=True)
show_site = models.BooleanField(u'Сайт', default=True)
show_logo = models.BooleanField(u'Логотип', default=True)
# только для ИП
show_ip_boss_fio = models.BooleanField(u'Фамилия, Имя, Отчество', default=True)
show_svid_gos_reg = models.BooleanField(u'Свид-во о гос. регистрации', default=True)
show_ip_reg_date = models.BooleanField(u'Дата регистрации ИП', default=True)
# только для Организации
show_name = models.BooleanField(u'Краткое название организации', default=True)
show_full_name = models.BooleanField(u'Полное название организации', default=True)
show_kpp = models.BooleanField(u'КПП', default=True)
show_org_boss_title_and_fio = models.BooleanField(u'Руководитель (Должность, ФИО)', default=True)
show_na_osnovanii = models.BooleanField(u'Действует на основании', default=True)
objects = managers.UserProfileFiltersManager()
class Meta:
verbose_name = u'Фильтры реквизитов'
verbose_name_plural = u'Фильтры реквизитов'
def __unicode__(self):
return u'%s' % self.user.email
def save(self, *args, **kwargs):
# всегда включены
self.show_ip_boss_fio = True
self.show_name = True
super(UserProfileFilters, self).save(*args, **kwargs)
class License(models.Model):
company = models.ForeignKey(UserProfile, related_name='licenses', verbose_name=u'пользователь')
term = models.IntegerField(verbose_name=u'срок лицензии')
date_from = models.DateField(u'дата начала', null=True, blank=True)
date_to = models.DateField(u'дата окончания', null=True, blank=True)
payform = models.IntegerField(verbose_name=u'форма оплаты',
choices=consts.PAYFORMS, default=0)
status = models.IntegerField(verbose_name=u'статус лицензии',
choices=consts.LICENSE_STATUSES, default=0)
order_date = models.DateField(verbose_name=u'дата заказа', auto_now_add=True)
paid_date = models.DateField(verbose_name=u'дата оплаты', null=True, blank=True)
pay_sum= models.IntegerField(verbose_name=u'сумма оплаты')
deleted = models.BooleanField(u'удалено', default=False)
def __init__(self, *args, **kwargs):
super(License, self).__init__(*args, **kwargs)
self.__prev_date = self.paid_date
def __unicode__(self):
return u'%s - %s %s (%d %s)' % (
self.company.get_company_name(),
self.term,
numeral.choose_plural(self.term, u"месяц, месяца, месяцев"),
self.pay_sum,
numeral.choose_plural(self.pay_sum, u"рубль, рубля, рублей"),
)
def save(self, *args, **kwargs):
if not self.__prev_date and self.paid_date:
max_date_license = License.objects.filter(company=self.company).aggregate(Max('date_to'))['date_to__max']
today = datetime.now().date()
if max_date_license < today:
max_date_license = today - timedelta(1)
self.date_from = max_date_license + relativedelta(days=1)
self.date_to = self.date_from + relativedelta(months=self.term, days=-1)
self.company.active = True
self.company.save()
self.status = 1
utils.check_one_profile(self.company, License, datetime.now(), manual=True)
super(License, self).save(*args, **kwargs)
def get_company(self):
return self.company.get_company_name()
def get_action_link(self):
if self.status == 0:
if self.payform == 0:
return u'<a href="%s">Скачать счёт</a>' % reverse('customer_license_get_doc', kwargs={'order_num': self.id})
elif self.payform == 1:
return u'Оплатить счёт'
elif self.payform == 2:
return u'<a href="%s">Скачать квитанцию</a>' % reverse('customer_license_get_doc', kwargs={'order_num': self.id})
elif self.status in [1, 2]:
return u'История операций'
else:
return ''
def get_term(self):
if self.term == 0:
return u'45 дней'
else:
return u'%s %s' % (self.term,
numeral.choose_plural(self.term, u"месяц, месяца, месяцев"),
)
def get_paid_status(self):
if self.status == 1:
return u'Лицензия оплачена, ещё не активирована'
elif self.status in [2, -1]:
left = relativedelta(self.date_to, datetime.today())
if left.months:
left_str = '%d %s %d %s' % (left.months,
numeral.choose_plural(left.months, u"месяц, месяца, месяцев"),
left.days,
numeral.choose_plural(left.days, u"день, дня, дней"),
)
else:
left_str = '%d %s' % (
left.days,
numeral.choose_plural(left.days, u"день, дня, дней"),
)
return u'Лицензия активирована: осталось %s' % left_str
elif self.status == 3:
return u'Время истекло'
else:
return None
class LicensePrice(models.Model):
term = models.IntegerField(verbose_name=u'срок лицензии',
choices=consts.TERMS)
price = models.IntegerField(verbose_name=u'сумма оплаты')
def __unicode__(self):
return u'%s %s (%d %s)' % (self.term,
numeral.choose_plural(self.term, u"месяц, месяца, месяцев"),
self.price,
numeral.choose_plural(self.price, u"рубль, рубля, рублей"),
)

@ -0,0 +1,33 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import
from datetime import datetime, timedelta
import traceback
from django.core.mail import mail_admins
from celery import shared_task
from .models import License, UserProfile
from .utils import check_one_profile
@shared_task
def check_license():
profiles = UserProfile.objects.all()
now = datetime.today()
#licenses = License.objects.filter(date_to__lt=now, status__in=[-1, 2], deleted=False)
#licenses.update(status=3)
licenses = License.objects.filter(order_date__lte=now - timedelta(10), status=0, deleted=False)
licenses.update(status=4)
for profile in profiles:
try:
check_one_profile(profile, License, now)
except Exception as e:
mail_admins(subject=u'customer: check_license error',
message=u'Profile id=%s.\n\n%s' % (profile.pk, traceback.format_exc(e))
)
return None

@ -0,0 +1,16 @@
"""
This file demonstrates writing tests using the unittest module. These will pass
when you run "manage.py test".
Replace this with more appropriate tests for your application.
"""
from django.test import TestCase
class SimpleTest(TestCase):
def test_basic_addition(self):
"""
Tests that 1 + 1 always equals 2.
"""
self.assertEqual(1 + 1, 2)

@ -0,0 +1,63 @@
# -*- coding: utf-8 -*-
from django.conf.urls import *
from . import views
from .views import profile, profile_ajax, license, docs
from .views import bank_accounts,bank_accounts_ajax
from .views import clients, clients_ajax
urlpatterns = patterns('',
# личный кабинет
url(r'^$', views.customer_index, name='customer_index'),
# --- профиль
url(r'^profile/$', profile.profile_view, name='customer_profile_view'),
url(r'^profile/edit/$', profile.profile_edit, name='customer_profile_edit'),
url(r'^profile/email/$', profile.profile_email, name='customer_profile_email'),
url(r'^license/$', license.order_license, name='customer_order_license'),
url(r'^delete_license/(?P<pk>\d+)/$', license.delete_license, name='customer_delete_license'),
url(r'^get_doc/(?P<order_num>\d+)/$', docs.get_doc, name='customer_license_get_doc'),
url(r'^payment/confirm/(?P<payment_id>\d+)$', license.yandex_pay, name='yamoney_confirm'),
url(r'^payment/result/$', license.payment_result, name='yamoney_result'),
url(r'^payment/success/$', license.payment_success, name='yamoney_success'),
url(r'^payment/fail/$', license.payment_fail, name='yamoney_fail'),
url(r'^license_list/$', license.license_list, name='customer_license_list'),
url(r'^paid_list/$', license.paid_list, name='customer_paid_list'),
# --- профиль AJAX
url(r'^profile/filters/edit/ajax/$', profile_ajax.profile_filters_edit_ajax, name='customer_profile_filters_edit_ajax'),
url(r'^profile/email/ajax/$', profile_ajax.profile_email_ajax, name='customer_profile_email_ajax'),
# --- расчетные счета
url(r'^bank-accounts/$', bank_accounts.bank_accounts_list, name='customer_bank_accounts_list'),
url(r'^bank-accounts/page/(?P<page_num>[0-9]+)/$', bank_accounts.bank_accounts_list, name='customer_bank_accounts_list'),
url(r'^bank-accounts/add/$', bank_accounts.bank_accounts_add, name='customer_bank_accounts_add'),
url(r'^bank-accounts/(?P<id>\d+)/edit/$', bank_accounts.bank_accounts_edit, name='customer_bank_accounts_edit'),
url(r'^bank-accounts/(?P<id>\d+)/delete/$', bank_accounts.bank_accounts_delete, name='customer_bank_accounts_delete'),
# --- расчетные счета AJAX
url(r'^bank-accounts/ajax/$', bank_accounts_ajax.bank_accounts_list_ajax, name='customer_bank_accounts_list_ajax'),
url(r'^bank-accounts/(?P<id>\d+)/get/ajax/$', bank_accounts_ajax.bank_accounts_get_ajax,
name='customer_bank_accounts_get_ajax'),
url(r'^bank-accounts/add/ajax/$', bank_accounts_ajax.bank_accounts_add_ajax, name='customer_bank_accounts_add_ajax'),
url(r'^bank-accounts/(?P<id>\d+)/edit/ajax/$', bank_accounts_ajax.bank_accounts_edit_ajax,
name='customer_bank_accounts_edit_ajax'),
url(r'^bank-accounts/(?P<id>\d+)/delete/ajax/$', bank_accounts_ajax.bank_accounts_delete_ajax,
name='customer_bank_accounts_delete_ajax'),
# --- контрагенты
url(r'^clients/$', clients.clients_list, name='customer_clients_list'),
url(r'^clients/page/(?P<page_num>[0-9]+)/$', clients.clients_list, name='customer_clients_list'),
url(r'^clients/add/$', clients.clients_add, name='customer_clients_add'),
url(r'^clients/(?P<id>\d+)/edit/$', clients.clients_edit, name='customer_clients_edit'),
url(r'^clients/(?P<id>\d+)/delete/$', clients.clients_delete, name='customer_clients_delete'),
# --- контрагенты AJAX
url(r'^clients/(?P<id>\d+)/get/ajax/$', clients_ajax.clients_get_ajax, name='customer_clients_get_ajax'),
url(r'^clients/add/ajax/$', clients_ajax.clients_add_ajax, name='customer_clients_add_ajax'),
url(r'^clients/(?P<id>\d+)/edit/ajax/$', clients_ajax.clients_edit_ajax, name='customer_clients_edit_ajax'),
url(r'^clients/(?P<id>\d+)/delete/ajax/$', clients_ajax.clients_delete_ajax, name='customer_clients_delete_ajax'),
url(r'^tmp_upload/ajax/$', profile.tmp_upload, name='upload_tmp_file'),
)

@ -0,0 +1,86 @@
# -*- coding: utf-8 -*-
from datetime import timedelta
from django.conf import settings
from django.core.mail import EmailMessage
from django.template.loader import render_to_string
SUPPORT_EMAIL = getattr(settings, 'SUPPORT_EMAIL')
def check_one_profile(profile, License, now, manual=False):
profile_is_active = profile.active
licenses = License.objects.filter(company=profile, date_from__lte=now, date_to__gte=now, status__in=[-1, 1, 2], deleted=False)
licenses.filter(status=1).update(status=2)
if licenses:
profile.active = True
else:
profile.active = False
profile.save()
user_email = profile.users.get().email
if profile.active and not profile_is_active:
template_name = 'myauth/license_activated.txt'
subject = u'Документор: Профиль активирован'
dict_context = {'user_email': user_email,
'support_email': SUPPORT_EMAIL,
'license_starts': licenses[0].date_from,
'license_ends': licenses[0].date_to,
}
email_body = render_to_string(template_name, dict_context)
email = EmailMessage(subject=subject, to=(user_email,), body=email_body)
email.send()
licenses_remain = License.objects.filter(company=profile, date_from__gt=now + timedelta(1), status=1, deleted=False)
licenses_to_pay = License.objects.filter(company=profile, status=0, deleted=False)
if not licenses_remain and not manual:
licenses = License.objects.filter(company=profile, date_to__lt=now, status__in=[-1, 2], deleted=False)
licenses.update(status=3)
if licenses:
template_name = 'myauth/license_ended.txt'
subject = u'Документор: срок действия лицензии окончен'
dict_context = {'user_email': user_email,
'support_email': SUPPORT_EMAIL,
'license_ends': licenses[0].date_to,
'licenses_to_pay': licenses_to_pay,
}
email_body = render_to_string(template_name, dict_context)
email = EmailMessage(subject=subject, to=(user_email,), body=email_body)
email.send()
licenses = License.objects.filter(company=profile, date_to=now + timedelta(1), status__in=[-1, 2], deleted=False)
if licenses:
template_name = 'myauth/license_ends.txt'
subject = u'Документор: окончание срока действия лицензии'
dict_context = {'user_email': user_email,
'support_email': SUPPORT_EMAIL,
'license_ends': licenses[0].date_to,
'licenses_to_pay': licenses_to_pay,
}
email_body = render_to_string(template_name, dict_context)
email = EmailMessage(subject=subject, to=(user_email,), body=email_body)
email.send()
if not manual:
licenses = License.objects.filter(company=profile, order_date=now - timedelta(9), status=0, deleted=False)
if licenses:
template_name = 'myauth/license_to_pay.txt'
subject = u'Документор: есть неоплаченные счета'
dict_context = {'user_email': user_email,
'support_email': SUPPORT_EMAIL,
}
email_body = render_to_string(template_name, dict_context)
email = EmailMessage(subject=subject, to=(user_email,), body=email_body)
email.send()
def raise_if_no_profile(request):
if not request.user.profile:
raise Exception(u"Profile not found for user: %d, '%s'" % (request.user.pk, request.user.username))

@ -4,9 +4,10 @@ from django.core.urlresolvers import reverse
from django.views.decorators.csrf import csrf_protect
from django.contrib.auth.decorators import login_required
from commons.paginator import pagination, save_per_page_value
from project.commons.paginator import pagination, save_per_page_value
from .. import models, forms
from ..decorators import license_required
from ..utils import raise_if_no_profile
@ -19,7 +20,7 @@ def bank_accounts_list(request, page_num=None):
template_name = 'customer/bank_accounts/list.html'
account_list = models.BankAccount.objects.get_all(request.user.profile)
page, pagination_form = pagination(request, account_list, page_num)
return render(request, template_name, {'page': page, 'pagination_form': pagination_form})
return render(request, template_name, {'page': page, 'pagination_form': pagination_form,})
@login_required
@ -28,7 +29,7 @@ def bank_accounts_add(request):
"""Добавить расчетный счет."""
raise_if_no_profile(request)
template_name = 'customer/bank_accounts/add.html'
template_name='customer/bank_accounts/add.html'
form_class = forms.BankAccountForm
success_url = 'customer_bank_accounts_list'
@ -97,7 +98,7 @@ def bank_accounts_delete(request, id):
"""Удалить расчетный счет."""
raise_if_no_profile(request)
template_name = 'customer/bank_accounts/delete.html'
template_name='customer/bank_accounts/delete.html'
success_url = 'customer_bank_accounts_list'
referer = request.POST.get('referer')

@ -8,10 +8,10 @@ from django.views.decorators.csrf import csrf_protect
from django.contrib.auth.decorators import login_required
from django.core.urlresolvers import reverse
from docs.models import Invoice, Faktura, AktRabot, Nakladn, Platejka
from commons.utils import dthandler
from project.commons.utils import dthandler
from .. import models, forms
from ..decorators import license_required
from ..utils import raise_if_no_profile
@ -23,25 +23,22 @@ def bank_accounts_list_ajax(request):
raise_if_no_profile(request)
fields_list = ['pk', 'bik', 'name', 'korr_account', 'account', 'is_main']
accounts = models.BankAccount.objects.get_all(
company=request.user.profile).values(*fields_list)
fields_list = ['pk', 'bik', 'name', 'address', 'korr_account', 'account', 'is_main',]
accounts = models.BankAccount.objects.get_all(company=request.user.profile).values(*fields_list)
for a in accounts:
a['edit_url'] = reverse('customer_bank_accounts_edit', kwargs={'id': a['pk']})
a['delete_url'] = reverse('customer_bank_accounts_delete', kwargs={'id': a['pk']})
a['edit_url'] = reverse('customer_bank_accounts_edit', kwargs={'id': a['pk'],})
a['delete_url'] = reverse('customer_bank_accounts_delete', kwargs={'id': a['pk'],})
data = json.dumps(list(accounts), default=dthandler())
return HttpResponse(data, content_type='application/json')
data = json.dumps(list(accounts), default=dthandler)
return HttpResponse(data, mimetype='application/json')
@login_required
def bank_accounts_get_ajax(request, id):
"""
Получить счёт - AJAX.
Если в форме редактирования счёта задан атрибут Meta.fields,
то дампит только поля, перечисленные в нём.
Иначе дампит вообще все поля, которые есть в модели.
"""Получить счёт - AJAX.
Если в форме редактирования счёта задан атрибут Meta.fields, то дампит только поля, перечисленные в нём.
Иначе дампит вообще все поля, которые есть в модели.
"""
if not request.is_ajax():
return HttpResponseBadRequest()
@ -53,11 +50,10 @@ def bank_accounts_get_ajax(request, id):
except AttributeError:
fields_list = []
account = get_object_or_404(models.BankAccount.objects.values(*fields_list),
pk=id, company=request.user.profile)
account = get_object_or_404(models.BankAccount.objects.values(*fields_list), pk=id, company=request.user.profile)
data = json.dumps(account, default=dthandler())
return HttpResponse(data, content_type='application/json')
data = json.dumps(account, default=dthandler)
return HttpResponse(data, mimetype='application/json')
@login_required
@ -80,16 +76,14 @@ def bank_accounts_add_ajax(request):
non_field_errors = form.non_field_errors()
if not form.is_valid():
non_field_errors.append('Заполните/исправьте выделенные поля.')
non_field_errors.append(u'Заполните/исправьте выделенные поля.')
data = {
'success': form.is_valid(),
# ошибки полей
'field_errors': form.errors,
# ошибки формы
'form_errors': non_field_errors,
'field_errors': form.errors, # ошибки полей
'form_errors': non_field_errors, # ошибки формы
}
return HttpResponse(json.dumps(data), content_type='application/json')
return HttpResponse(json.dumps(data), mimetype='application/json')
@login_required
@ -112,16 +106,14 @@ def bank_accounts_edit_ajax(request, id):
non_field_errors = form.non_field_errors()
if not form.is_valid():
non_field_errors.append('Заполните/исправьте выделенные поля.')
non_field_errors.append(u'Заполните/исправьте выделенные поля.')
data = {
'success': form.is_valid(),
# ошибки полей
'field_errors': form.errors,
# ошибки формы
'form_errors': non_field_errors,
'field_errors': form.errors, # ошибки полей
'form_errors': non_field_errors, # ошибки формы
}
return HttpResponse(json.dumps(data), content_type='application/json')
return HttpResponse(json.dumps(data), mimetype='application/json')
@login_required
@ -135,40 +127,11 @@ def bank_accounts_delete_ajax(request, id):
raise_if_no_profile(request)
account = get_object_or_404(models.BankAccount, pk=id, company=request.user.profile)
account.delete()
account_docs = []
doc_list = [
(Invoice, 'счета'),
(Faktura, 'счета-фактуры'),
(Nakladn, 'накладные'),
(AktRabot, 'акты выполненных работ'),
(Platejka, 'платёжные поручения')
]
for doc in doc_list:
docs = doc[0].objects.filter(bank_account=account)
if docs:
account_docs.append(doc[1])
if not account_docs:
account.delete()
success = True
message = {'title': 'Инфо',
'msg': 'Расчётный счёт удалён.'}
del_id = id
else:
success = True
message = {'title': 'Инфо',
'msg': 'Расчтный счет не удалён. Есть выписанные документы: %s.' % ','.join(
account_docs)}
del_id = None
# TODO обработать ошибки удаления
data = {
'success': success,
'message': message,
'reload': False,
'action': 'account-delete',
'id': del_id,
'success': True,
'message': {'title': 'Инфо', 'msg': 'Расчётный счёт удалён.',},
}
return HttpResponse(json.dumps(data), content_type='application/json')
return HttpResponse(json.dumps(data), mimetype='application/json')

@ -3,12 +3,11 @@ from django.shortcuts import render, redirect, get_object_or_404
from django.views.decorators.csrf import csrf_protect
from django.contrib.auth.decorators import login_required
from commons.paginator import pagination, save_per_page_value
from project.commons.paginator import pagination, save_per_page_value
from customer import models, forms
from customer.decorators import license_required
from customer.utils import raise_if_no_profile
from django.conf import settings
from .. import models, forms
from ..decorators import license_required
from ..utils import raise_if_no_profile
@login_required
@ -19,20 +18,17 @@ def clients_list(request, page_num=None):
raise_if_no_profile(request)
template_name = 'customer/clients/list.html'
template_name='customer/clients/list.html'
client_list = models.Client.objects.filter(company=request.user.profile).\
order_by('name', '-created_at')
client_list = models.Client.objects.filter(company=request.user.profile).order_by('name', '-created_at')
page, pagination_form = pagination(request, client_list, page_num)
client_form = forms.ClientForm()
dadata_api_key = settings.DADATA_API_KEY
dictionary = {
'page': page,
'pagination_form': pagination_form,
'client_form': client_form,
'dadata_api_key': dadata_api_key
}
return render(request, template_name, dictionary)
@ -44,7 +40,7 @@ def clients_add(request):
"""Добавить контрагента."""
raise_if_no_profile(request)
template_name = 'customer/clients/add.html'
template_name='customer/clients/add.html'
success_url = 'customer_clients_list'
form_class = forms.ClientForm
@ -62,7 +58,7 @@ def clients_add(request):
else:
form = form_class()
return render(request, template_name, {'form': form})
return render(request, template_name, {'form': form,})
@login_required
@ -72,7 +68,7 @@ def clients_edit(request, id):
"""Редактировать контрагента."""
raise_if_no_profile(request)
template_name = 'customer/clients/edit.html'
template_name='customer/clients/edit.html'
success_url = 'customer_clients_list'
if request.method == 'POST' and '_cancel' in request.POST:
@ -104,7 +100,7 @@ def clients_delete(request, id):
"""Удалить контрагента."""
raise_if_no_profile(request)
template_name = 'customer/clients/delete.html'
template_name='customer/clients/delete.html'
success_url = 'customer_clients_list'
if request.method == 'POST' and '_cancel' in request.POST:
@ -117,4 +113,4 @@ def clients_delete(request, id):
# TODO обработать ошибки удаления
return redirect(success_url)
return render(request, template_name, {'client': client})
return render(request, template_name, {'client': client,})

@ -9,17 +9,17 @@ from django.views.decorators.csrf import csrf_protect
from django.contrib.auth.decorators import login_required
from django.template.loader import render_to_string
from customer import models, forms
from docs.models import Invoice, Faktura, AktRabot, AktSverki, Nakladn, Platejka, Dover
from customer.utils import raise_if_no_profile
from .. import models, forms
from ...docs.models import Invoice, Faktura, AktRabot, AktSverki, Nakladn, Platejka, Dover
from ..decorators import license_required
from ..utils import raise_if_no_profile
@login_required
def clients_get_ajax(request, id):
"""Получить контрагента - AJAX.
Если в форме редактирования контрагента задан атрибут Meta.fields,
то дампит только поля, перечисленные в нём.
Иначе дампит вообще все поля, которые есть в модели.
Если в форме редактирования контрагента задан атрибут Meta.fields, то дампит только поля, перечисленные в нём.
Иначе дампит вообще все поля, которые есть в модели.
"""
if not request.is_ajax():
return HttpResponseBadRequest()
@ -31,10 +31,9 @@ def clients_get_ajax(request, id):
except AttributeError:
fields_list = []
client = get_object_or_404(models.Client.objects.values(*fields_list), pk=id,
company=request.user.profile)
client = get_object_or_404(models.Client.objects.values(*fields_list), pk=id, company=request.user.profile)
return HttpResponse(json.dumps(client), content_type='application/json')
return HttpResponse(json.dumps(client), mimetype='application/json')
@login_required
@ -47,25 +46,22 @@ def clients_add_ajax(request):
raise_if_no_profile(request)
form = forms.ClientForm(request.POST, request=request)
form_class = forms.ClientForm
new_client_id = None
new_client_str = None
form = form_class(data=request.POST)
new_client_id = None
html = ''
if form.is_valid():
new_client = form.save(commit=False)
new_client.company = request.user.profile
# TODO : to common function with __str__
if new_client.name_short_dadata:
new_client_str = '{}, {}'.format(new_client.name_short_dadata,
new_client.name_short_self)
else:
new_client_str = '{}, ИНН {}'.format(new_client.name, new_client.inn or 'не указан')
new_client_str = new_client.name
new_client.save()
new_client_id = new_client.id
# import ipdb;ipdb.set_trace()
html = render_to_string('customer/clients/list_item.html',
{'obj': new_client}, RequestContext(request))
html = render_to_string('customer/clients/list_item.html',
{'obj': new_client}, RequestContext(request))
non_field_errors = form.non_field_errors()
if not form.is_valid():
@ -73,15 +69,16 @@ def clients_add_ajax(request):
data = {
'success': form.is_valid(),
'field_errors': form.errors,
'form_errors': non_field_errors,
'field_errors': form.errors, # ошибки полей
'form_errors': non_field_errors, # ошибки формы
#'reload': form.is_valid() and 'reload_on_success' in request.GET
'reload': False,
'id': new_client_id,
'name': new_client_str,
'action': 'client-add',
'row_html': html,
}
return HttpResponse(json.dumps(data), content_type='application/json')
return HttpResponse(json.dumps(data), mimetype='application/json')
@login_required
@ -94,23 +91,24 @@ def clients_edit_ajax(request, id):
raise_if_no_profile(request)
client = get_object_or_404(models.Client, pk=id, company=request.user.profile)
form_class = forms.ClientForm
form = forms.ClientForm(request.POST, request=request, instance=client)
client = get_object_or_404(models.Client, pk=id, company=request.user.profile)
form = form_class(data=request.POST, instance=client)
if form.is_valid():
client = form.save()
non_field_errors = form.non_field_errors()
html = render_to_string('customer/clients/list_item.html',
{'obj': client}, RequestContext(request))
html = render_to_string('customer/clients/list_item.html',
{'obj': client}, RequestContext(request))
if not form.is_valid():
non_field_errors.append(u'Заполните/исправьте выделенные поля.')
data = {
'success': form.is_valid(),
'field_errors': form.errors,
'form_errors': non_field_errors,
'field_errors': form.errors, # ошибки полей
'form_errors': non_field_errors, # ошибки формы
'reload': False,
'id': client.id,
'name': client.name,
@ -119,7 +117,7 @@ def clients_edit_ajax(request, id):
'action': 'client-edit',
'row_html': html,
}
return HttpResponse(json.dumps(data), content_type='application/json')
return HttpResponse(json.dumps(data), mimetype='application/json')
@login_required
@ -134,18 +132,13 @@ def clients_delete_ajax(request, id):
client = get_object_or_404(models.Client, pk=id, company=request.user.profile)
client_docs = []
doc_list = [
(Invoice, u'счета'),
(Faktura, u'счета-фактуры'),
(Nakladn, u'накладные'),
(AktRabot, u'акты выполненных работ'),
(Platejka, u'платёжные поручения'),
(Dover, u'доверенности'), (AktSverki, u'акты сверки')
]
doc_list = [(Invoice, u'счета'), (Faktura, u'счета-фактуры'), (Nakladn, u'накладные'),
(AktRabot, u'акты выполненных работ'), (Platejka, u'платёжные поручения'),
(Dover, u'доверенности'), (AktSverki, u'акты сверки')]
for doc in doc_list:
docs = doc[0].objects.filter(client=client)
if docs:
client_docs.append(doc[1])
client_docs.append(doc[1])
if not client_docs:
client.delete()
@ -154,16 +147,14 @@ def clients_delete_ajax(request, id):
del_id = id
else:
success = True
message = {'title': u'Инфо',
'msg': u'Контрагент не удалён. Есть выписанные документы: %s.' % ','.join(
client_docs)}
message = {'title': u'Инфо', 'msg': u'Контрагент не удалён. Есть выписанные документы: %s.' % ','.join(client_docs)}
del_id = None
data = {
'success': success,
'message': message,
'reload': False,
'reload': False,
'action': 'client-delete',
'id': del_id,
}
return HttpResponse(json.dumps(data), content_type='application/json')
return HttpResponse(json.dumps(data), mimetype='application/json')

@ -0,0 +1,105 @@
# -*- coding: utf-8 -*-
import os
import re
from django.conf import settings
from django.http import Http404, HttpResponseRedirect, HttpResponseForbidden, HttpResponse
from xlwt import easyxf
from xlrd import open_workbook
from xlutils.copy import copy
from pytils.numeral import rubles
from ..models import License
from ..utils import raise_if_no_profile
XLS_ROOT = os.path.join(settings.TEMPLATE_DIRS[0], 'xls')
def get_doc(request, order_num=None):
if not request.user.is_authenticated():
raise HttpResponseForbidden
raise_if_no_profile(request)
license = License.objects.get(pk=order_num)
pm = license.payform
data = request.user.profile
if pm == 0:
tmp_name = 'bill.xls'
file_name = "Invoice No.%s.xls" % (order_num,)
elif pm == 2:
tmp_name = '4pd.xls'
file_name = "Kvitanciya na oplatu zakaza No.%s.xls" % (order_num,)
else:
raise Http404()
response = HttpResponse(mimetype="application/vnd.ms-excel")
response['Content-Disposition'] = 'attachment; filename=%s' % file_name
rb = open_workbook(os.path.join(XLS_ROOT, tmp_name), on_demand=True, formatting_info=True)
wb = copy(rb)
ws = wb.get_sheet(0)
# отключить колонтитулы
ws.show_headers = 0
ws.print_headers = 0
ws.header_str = ''
ws.footer_str = ''
if pm == 0: # заполняем счет
style0 = easyxf('font: name Times New Roman, height 280, bold True;')
style0_center = easyxf('font: name Times New Roman, height 280, bold True; align: horiz center')
style1 = easyxf('font: name Times New Roman, height 180;')
style2 = easyxf('font: name Times New Roman, height 180, bold True;')
style3 = easyxf('font: name Times New Roman, height 180;'
'border: top thin, left thin, right thin, bottom thin;'
)
style4 = easyxf('font: name Times New Roman, height 180, bold True;'
'border: top thin, left thin, right thin, bottom thin;'
)
style4.num_format_str = "#,##0.00"
ws.write(11, 0, u"СЧЕТ № %s от %s" % (order_num, license.order_date.strftime('%d.%m.%Y')), style0_center)
ws.write(13, 0, u"Покупатель: %s" % data.name, style1)
ws.write(16, 2, u"Лицензия Dokumentor.ru на %s" % (license.get_term()), style3)
style3.num_format_str = "#,##0.00"
ws.write(16, 36, license.pay_sum, style3)
ws.write(16, 44, license.pay_sum, style3)
ws.write(17, 44, license.pay_sum, style4)
ws.write(19, 44, license.pay_sum, style4)
ws.write(21, 0, u"Всего наименование 1, на сумму %s,00 руб." % license.pay_sum, style1)
ws.write(23, 0, u"%s." % rubles(license.pay_sum).capitalize(), style2)
ws.insert_bitmap(os.path.join(XLS_ROOT, 'stamp.bmp'), 26, 12, y=3, scale_y=1.1)
elif pm == 2: # заполняем квитанцию
style0 = easyxf('font: name Times New Roman, height 160, bold True;'
'border: bottom thin;'
)
style1 = easyxf('font: name Times New Roman, height 160, bold True;'
'border: bottom thin;'
'align: horiz center;'
)
# заполняем оригинал
ws.write(11, 4, u"Лицензия Dokumentor.ru на %s" % (license.get_term()), style1)
ws.write(13, 13, data.get_boss_fio(), style0)
ws.write(14, 13, re.sub("^\s+|\n|\r|\s+$", ' ', data.address), style0)
ws.write(15, 11, license.pay_sum, style1)
# заполняем копию
ws.write(31, 4, u"Лицензия Dokumentor.ru на %s" % (license.get_term()), style1)
ws.write(33, 13, data.get_boss_fio(), style0)
ws.write(34, 13, re.sub("^\s+|\n|\r|\s+$", ' ', data.address), style0)
ws.write(35, 11, license.pay_sum, style1)
else:
raise Http404()
wb.save(response)
return response

@ -0,0 +1,161 @@
# -*- coding: utf-8 -*-
import json
import hashlib
from django.shortcuts import render, redirect
from django.http import Http404, HttpResponseRedirect, HttpResponseForbidden, HttpResponse
from django.conf import settings
from django.views.decorators.csrf import csrf_exempt
from django.utils.http import urlquote
from django.contrib.auth.decorators import login_required
from django.template.response import TemplateResponse
from django.core.urlresolvers import reverse
from django.views.decorators.csrf import csrf_protect
from yandex_money.models import Payment
from ..models import License, LicensePrice
from ..consts import PAYFORMS
from ..forms import LicenseForm, YaForm
from ..utils import raise_if_no_profile
@login_required
@csrf_protect
def yandex_pay(request, payment_id):
template_name = 'customer/profile/yandex.html'
payment = Payment.objects.get(id=payment_id)
if payment.user != request.user:
raise
form = YaForm(instance=payment)
return render(request, template_name, {'form': form,
'ya_url': settings.YANDEX_MONEY_PAYMENT_URL})
@login_required
@csrf_protect
def order_license(request):
"""заказ лицензии
"""
raise_if_no_profile(request)
template_name = 'customer/profile/license.html'
form = LicenseForm(request.POST or None,
initial = {'term': LicensePrice.objects.all()[1], 'payform': 0})
dictionary = {
'form': form,
}
if form.is_valid():
new_license = License(company=request.user.profile,
term=form.cleaned_data['term'].term,
payform=form.cleaned_data['payform'],
pay_sum=form.cleaned_data['term'].price,
)
new_license.save()
if form.cleaned_data['payform'] == '1':
payment, _ = Payment.objects.get_or_create(order_amount=form.cleaned_data['term'].price,
payment_type=Payment.PAYMENT_TYPE.AC,
order_number=new_license.id,
)
payment.user=request.user
# payment.cps_email=request.user.email
payment.customer_number=request.user.email
# payment.customer_number=request.user.profile.get_company_name().strip()
payment.save()
return redirect(reverse('yamoney_confirm', kwargs={'payment_id': payment.id}))
return redirect(reverse('customer_license_list'))
return render(request, template_name, dictionary)
@login_required
def license_list(request):
"""Список счетов на лицензии
"""
raise_if_no_profile(request)
template_name = 'customer/profile/license_list.html'
licenses = License.objects.filter(company=request.user.profile, deleted=False, status__gt=-1).order_by('-id')
dictionary = {
'licenses': licenses,
}
return render(request, template_name, dictionary)
@login_required
def paid_list(request):
"""Оплаченные лицензии
"""
raise_if_no_profile(request)
template_name = 'customer/profile/paid_list.html'
licenses = License.objects.filter(company=request.user.profile,
status__in=[-1, 1, 2, 3, 4], deleted=False).order_by('-id')
dictionary = {
'licenses': licenses,
}
return render(request, template_name, dictionary)
@login_required
def delete_license(request, pk):
if not request.is_ajax():
return HttpResponseBadRequest()
raise_if_no_profile(request)
try:
license = License.objects.get(pk=pk, status=0, company=request.user.profile)
if request.method == 'POST':
license.deleted = True
license.save()
dictionary = {'res': 'Ok', 'id': pk}
except:
dictionary = {'res': 'fail'}
data = json.dumps(dictionary)
return HttpResponse(data, mimetype='application/json')
@csrf_exempt
def payment_result(request):
if request.method == 'POST':
try:
nInvId = request.POST.get('InvId')
nOutSum = request.POST.get('OutSum')
sSignatureValue = request.POST.get('SignatureValue')
SignatureValue = hashlib.md5('%s:%s:%s' % (nOutSum, nInvId, settings.ROBOKASSA_PASSWORD2)).hexdigest()
if sSignatureValue.upper() == SignatureValue.upper():
license = License.objects.get(pk=nInvId)
license.status = 1
license.save()
return HttpResponse('OK%s' % nInvId)
else:
return HttpResponse('bad sign')
except License.DoesNotExist:
return HttpResponseForbidden()
else:
return HttpResponseForbidden()
@csrf_exempt
def payment_success(request):
nInvId = request.GET.get('orderNumber')
order = License.objects.get(pk=nInvId)
context = {'success': True, 'order_num': nInvId}
return TemplateResponse(request, 'customer/profile/end_order.html', context)
@csrf_exempt
def payment_fail(request):
try:
nInvId = request.GET.get('')
message = u"Возникла проблема. Ваш Заказ не оплачен. Попробуйте оформить заявку снова, или позвоните по номеру."
return TemplateResponse(request, 'customer/profile/end_order.html', {'message': message, 'success': False})
except Order.DoesNotExist:
return HttpResponseForbidden()

@ -0,0 +1,280 @@
# -*- coding: utf-8 -*-
import os
import json
import tempfile
from email.header import Header
from django.shortcuts import render, redirect, get_object_or_404
from django.core.files import File
from django.views.decorators.csrf import csrf_protect
from django.contrib.auth.decorators import login_required
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 django.http import Http404, HttpResponse
# from sorl.thumbnail import get_thumbnail
from easy_thumbnails.files import get_thumbnailer
from project.commons.pdf_tools import render_pdf_to_string, pdf_to_response
from .. import models, forms
from ..decorators import license_required
from ..utils import raise_if_no_profile
PDF_PROFILE_NAME = u'Реквизиты.pdf'
SUPPORT_EMAIL = getattr(settings, 'SUPPORT_EMAIL', '')
# -----------------------------------------------------------------------------
@login_required
@csrf_protect
def profile_view(request):
"""Просмотр профиля пользователя, фильтрация реквизитов, скачать/отправить реквизиты по почте."""
raise_if_no_profile(request)
template_name = 'customer/profile/view.html'
profile = request.user.profile
accounts = models.BankAccount.objects.get_all(profile)
filters_form_class = forms.get_profile_filters_form_class(profile.profile_type)
filters = models.UserProfileFilters.objects.get_or_create_filters(user=request.user)
if request.method == 'POST':
filters_form = filters_form_class(data=request.POST, instance=filters, profile=profile, accounts=accounts)
if filters_form.is_valid():
filters = filters_form.save()
if 'download-pdf' in request.POST:
#return _profile_get_pdf(request, profile, filters.bank_account, filters) # для отладки
return profile_as_pdf(request, profile, filters.bank_account, filters)
elif 'email-pdf' in request.POST:
return redirect('customer_profile_email')
return redirect('customer_profile_view') # редирект на себя, чтобы не сабмитили форму по F5
else:
filters_form = filters_form_class(instance=filters, label_suffix='', profile=profile, accounts=accounts)
dictionary = {
'profile': profile,
'accounts': accounts,
'filters_form': filters_form,
'email_profile_form': forms.EmailProfileForm(),
}
return render(request, template_name, dictionary)
@login_required
@csrf_protect
def profile_edit(request):
"""Редактировать профиль пользователя."""
raise_if_no_profile(request)
template_name = 'customer/profile/edit.html'
success_url = 'customer_profile_view'
if request.method == 'POST' and '_cancel' in request.POST:
return redirect(success_url)
profile = request.user.profile
form_class = forms.get_profile_form_class(profile.profile_type)
accounts = models.BankAccount.objects.get_all(profile)
bank_account_form = forms.BankAccountForm(initial={'company': profile})
if request.method == 'POST':
form = form_class(data=request.POST, files=request.FILES, instance=profile)
if form.is_valid():
item = form.save(commit=False)
for img_url in ('tmb_logo', 'tmb_boss_sign', 'tmb_glavbuh_sign', 'tmb_stamp'):
if form.cleaned_data[img_url]:
chg_file = open(settings.MEDIA_ROOT + '/cache/imgs/' + \
form.cleaned_data[img_url])
item_attr = img_url[4:]
getattr(item, item_attr).save('%s.%s' % \
(item_attr, form.cleaned_data[img_url].split('.')[-1]),
File(chg_file))
chg_file.close()
elif form.cleaned_data:
pass
item.save()
return redirect(success_url)
else:
form = form_class(instance=profile)
dictionary = {
'form': form,
'profile': profile,
'accounts': accounts,
'bank_account_form': bank_account_form,
}
return render(request, template_name, dictionary)
def tmp_upload(request):
SIZES = {'id_boss_sign': (170, 65), 'id_glavbuh_sign': (170, 65), 'id_stamp': (170, 170), 'id_logo': (170, 170)}
elm_id = request.REQUEST['elm_id']
file_ = request.FILES.values()[0]
if not file_.content_type.startswith('image'):
return {'res': 'bad'}
if not os.path.exists(settings.MEDIA_ROOT +
'/cache/imgs/'):
os.makedirs(settings.MEDIA_ROOT +
'/cache/imgs/')
tmp_dir = tempfile.mkdtemp('img_tmp', settings.MEDIA_ROOT +
'/cache/imgs/')
os.chmod(tmp_dir, 0755)
open(tmp_dir + '/' + file_.name, "w").write(file_.read())
tmp_url_partial = os.path.basename(tmp_dir) + '/' + file_.name
thumbnailer = get_thumbnailer(tmp_dir + '/' + file_.name)
# im = get_thumbnail(tmp_dir + '/' + file_.name, SIZES[elm_id], quality=75)
thumbnail_options = {'size': SIZES[elm_id]}
im = thumbnailer.get_thumbnail(thumbnail_options) # Возвращает в url полный путь, поэтому придётся резать
im_url = os.path.join(settings.MEDIA_URL, os.path.relpath(im.url, settings.MEDIA_ROOT))
data = {'res': 'ok', 'pic': im_url, 'full_pic': tmp_url_partial}
data.update(request.REQUEST)
return HttpResponse(json.dumps(data), mimetype='application/json')
def del_tmp_photo(request, article_pk):
# wedding, wedding_guests = get_wedding_n_guests(request)
try:
pass
# article = WedPage.objects.get(pk=article_pk)
# article.main_photo.delete()
except:
return {'res': 'bad'}
return {'res': 'ok'}
@login_required
def _profile_get_pdf(request, profile=None, account=None, filters=None):
"""Создать профиль пользователя в PDF и вернуть как строку."""
template_name = 'customer/profile/as_pdf.html'
dictionary = {
'profile': profile,
'account': account,
'filters': filters,
}
return render_pdf_to_string(request, template_name, dictionary)
@login_required
def profile_as_pdf(request, profile=None, account=None, filters=None):
"""Вывести профиль пользователя в формате PDF в HttpResponse."""
pdf = _profile_get_pdf(request, profile, account, filters)
return pdf_to_response(pdf, PDF_PROFILE_NAME)
def _send_profile_email(subject, to, body, pdf_content):
"""Отправка письма."""
template_name = 'customer/profile/profile_email.txt'
dict_context = {'body': body, 'support_email': SUPPORT_EMAIL}
email_body = render_to_string(template_name, dict_context)
email = EmailMessage(
subject=subject,
to=(to,),
body=email_body,
attachments = [(smart_str(Header(PDF_PROFILE_NAME, 'cp1251')), pdf_content, 'application/pdf'),]
)
return email.send()
@login_required
@login_required
@csrf_protect
def profile_email(request):
"""Форма отправки профиля пользователя на email аттачем в PDF."""
raise_if_no_profile(request)
template_name = 'customer/profile/email.html'
success_url = 'customer_profile_view'
form_class = forms.EmailProfileForm
if request.method == 'POST' and '_cancel' in request.POST:
return redirect('customer_profile_view')
profile = request.user.profile
filters = models.UserProfileFilters.objects.get_or_create_filters(user=request.user)
if request.method == 'POST':
form = form_class(data=request.POST)
if form.is_valid():
_send_profile_email(
subject = u'Реквизиты %s' % profile.get_company_name(),
to = form.cleaned_data['to'],
body = form.cleaned_data['body'],
pdf_content = _profile_get_pdf(request, profile, filters.bank_account, filters)
)
return redirect(success_url)
else:
form = form_class()
return render(request, template_name, {'form': form, 'profile': profile,})
#@login_required
#@csrf_protect
#def profile_settings(request):
# """Редактировать настройки пользователя."""
# template_name='customer/profile/settings.html'
#
# profile = get_object_or_404(models.UserProfile, user=request.user)
# form_class = forms.UserProfileSettingsForm #TODO remove this view
#
# # пути к уже загруженным подписям/штампу
# curr_files = {'boss_sign': None, 'glavbuh_sign': None, 'stamp': None,}
# if profile.boss_sign:
# curr_files['boss_sign'] = profile.boss_sign.path
# if profile.glavbuh_sign:
# curr_files['glavbuh_sign'] = profile.glavbuh_sign.path
# if profile.stamp:
# curr_files['stamp'] = profile.stamp.path
#
# if request.method == "POST" and '_cancel' not in request.POST:
# post = request.POST
# files = request.FILES
# form = form_class(user=request.user, data=post, files=files, instance=profile)
#
# if form.is_valid():
# def delete_file(path):
# """Удалить файл. Если ошибка - сообщить в консоль."""
# try:
# os.remove(path)
# except:
# print "Can't delete file:", path
# # --- удалить старые файлы
# for field, path in curr_files.iteritems():
# if not path:
# continue
# # если стоит галочка 'очистить'
# if '%s-clear' % field in post:
# delete_file(path)
# continue
# # если загружен новый файл
# if field in files:
# delete_file(path)
# continue
# # --- изменить пароль
# if 'new_password1' in post:
# request.user.set_password(post.get('new_password1'))
# request.user.save()
# messages.add_message(request, messages.INFO, u'Пароль успешно изменен.')
#
# form.save()
# return redirect('customer_profile_settings')
# else:
# form = form_class(user=request.user, instance=profile)
#
# return render(request, template_name, {'profile': profile, 'form': form,})

@ -1,15 +1,17 @@
# -*- coding: utf-8 -*-
import simplejson as json
from django.http import HttpResponseBadRequest, HttpResponse
from django.shortcuts import get_object_or_404
from django.http import HttpResponseBadRequest, HttpResponse, Http404
from django.views.decorators.http import require_POST
from django.views.decorators.csrf import csrf_protect
from django.contrib.auth.decorators import login_required
from customer import models, forms
from .. import models, forms
from customer.views.profile import (_send_profile_email, _profile_get_pdf)
from customer.utils import raise_if_no_profile
from ..decorators import license_required
from .profile import _send_profile_email, _profile_get_pdf
from ..utils import raise_if_no_profile
@login_required
@ -29,12 +31,7 @@ def profile_filters_edit_ajax(request):
filters_form_class = forms.get_profile_filters_form_class(profile.profile_type)
filters = models.UserProfileFilters.objects.get_or_create_filters(user=request.user)
form = filters_form_class(
data=request.POST,
instance=filters,
profile=profile,
accounts=accounts
)
form = filters_form_class(data=request.POST, instance=filters, profile=profile, accounts=accounts)
if form.is_valid():
form.save()
@ -44,10 +41,10 @@ def profile_filters_edit_ajax(request):
data = {
'success': form.is_valid(),
'field_errors': form.errors, # ошибки полей
'form_errors': non_field_errors, # ошибки формы
'field_errors': form.errors, # ошибки полей
'form_errors': non_field_errors, # ошибки формы
}
return HttpResponse(json.dumps(data), content_type='application/json')
return HttpResponse(json.dumps(data), mimetype='application/json')
@login_required
@ -69,10 +66,10 @@ def profile_email_ajax(request):
form = form_class(data=request.POST)
if form.is_valid():
_send_profile_email(
subject=u'Реквизиты %s' % profile.get_company_name(),
to=form.cleaned_data['to'],
body=form.cleaned_data['body'],
pdf_content=_profile_get_pdf(request, profile, filters.bank_account, filters)
subject = u'Реквизиты %s' % profile.get_company_name(),
to = form.cleaned_data['to'],
body = form.cleaned_data['body'],
pdf_content = _profile_get_pdf(request, profile, filters.bank_account, filters)
)
non_field_errors = form.non_field_errors()
@ -81,7 +78,7 @@ def profile_email_ajax(request):
data = {
'success': form.is_valid(),
'field_errors': form.errors, # ошибки полей
'form_errors': non_field_errors, # ошибки формы
'field_errors': form.errors, # ошибки полей
'form_errors': non_field_errors, # ошибки формы
}
return HttpResponse(json.dumps(data), content_type='application/json')
return HttpResponse(json.dumps(data), mimetype='application/json')

@ -1,42 +1,60 @@
# -*- coding: utf-8 -*-
from django.contrib import admin
from docs import models
import models
#class InvoiceItemInline(admin.TabularInline):
# model = models.InvoiceItem
# extra = 0
class InvoiceAdmin(admin.ModelAdmin):
list_display = ('doc_num', 'doc_date', 'company', 'client',)
list_filter = ('doc_date',)
search_fields = ('company__name', 'company__inn', 'company__email',
'client__name', 'client__inn', 'client__contact_email')
'client__name', 'client__inn', 'client__contact_email')
#inlines = (InvoiceItemInline,)
#class NakladnItemInline(admin.TabularInline):
# model = models.NakladnItem
# extra = 0
class NakladnAdmin(admin.ModelAdmin):
list_display = ('doc_num', 'doc_date', 'company', 'client',)
list_filter = ('doc_date',)
search_fields = ('company__name', 'company__inn', 'company__email',
'client__name', 'client__inn', 'client__contact_email')
'client__name', 'client__inn', 'client__contact_email')
#inlines = (NakladnItemInline,)
#class AktRabotItemInline(admin.TabularInline):
# model = models.AktRabotItem
# extra = 0
class AktRabotAdmin(admin.ModelAdmin):
list_display = ('doc_num', 'doc_date', 'company', 'client',)
list_filter = ('doc_date',)
search_fields = ('company__name', 'company__inn', 'company__email',
'client__name', 'client__inn', 'client__contact_email')
'client__name', 'client__inn', 'client__contact_email')
#inlines = (AktRabotItemInline,)
class FakturaAdmin(admin.ModelAdmin):
list_display = ('doc_num', 'doc_date', 'company', 'client',)
list_filter = ('doc_date',)
search_fields = ('company__name', 'company__inn', 'company__email',
'client__name', 'client__inn', 'client__contact_email')
'client__name', 'client__inn', 'client__contact_email')
class PlatejkaAdmin(admin.ModelAdmin):
list_display = ('doc_num', 'doc_date', 'company', 'client',)
list_filter = ('doc_date',)
search_fields = ('doc_info', 'company__name', 'company__inn', 'company__email',
'client__name', 'client__inn', 'client__contact_email')
'client__name', 'client__inn', 'client__contact_email')
class MeasureAdmin(admin.ModelAdmin):

@ -0,0 +1 @@
from .render_to_xls import render_xls_to_string

@ -2,20 +2,18 @@
import os
import re
import math
from io import BytesIO
from StringIO import StringIO
import xlrd
import xlwt
import os.path
from PIL import Image
from django.conf import settings
from django.template import Template, RequestContext
from django.template.base import BLOCK_TAG_START, BLOCK_TAG_END, VARIABLE_TAG_START, \
VARIABLE_TAG_END
from django.template import (Template, RequestContext, BLOCK_TAG_START, BLOCK_TAG_END,
VARIABLE_TAG_START, VARIABLE_TAG_END)
from project.commons.xls import (get_xlwt_style_list, copy_cells, width_cols, horz_page_break, mm_to_twips,
sum_src_heights, sum_dst_heights)
from commons.xls import (get_xlwt_style_list, copy_cells, width_cols,
horz_page_break, mm_to_twips, sum_src_heights, sum_dst_heights)
TAG_RE = re.compile('(%s.*?%s|%s.*?%s)' % (
re.escape(BLOCK_TAG_START), re.escape(BLOCK_TAG_END),
@ -33,12 +31,12 @@ def render_xls_to_string(request, xls_template, dictionary=None):
"""Создает по шаблону новую книгу Excel.
Возвращает строку, в которой сожержится сгенерированный Excel.
"""
src_xls = os.path.join(XLS_ROOT, xls_template) # файл шаблона
src_xls = os.path.join(XLS_ROOT, xls_template) # файл шаблона
src_book = None
try:
# откуда
src_book = xlrd.open_workbook(src_xls, encoding_override='cp1251',
on_demand=True, formatting_info=True)
on_demand=True, formatting_info=True)
src_sheet = src_book.sheet_by_index(0)
# достать список стилей
@ -51,12 +49,14 @@ def render_xls_to_string(request, xls_template, dictionary=None):
# настройки
xls_settings = get_settings(src_book)
apply_page_settings(dst_sheet, xls_settings)
# заполнить данными
fill_xls(request, dictionary, src_sheet, dst_sheet, style_list, xls_settings)
fill_xls(request, dictionary, src_sheet, dst_sheet, style_list,
xls_settings)
# закрыть исходную книгу и сохранить созданную
src_book.release_resources()
f = BytesIO()
f=StringIO()
dst_book.save(f)
xls_content = f.getvalue()
f.close()
@ -74,6 +74,8 @@ def fill_xls(request, dictionary, src_sheet, dst_sheet, style_list, xls_settings
context = RequestContext(request, dictionary)
# -------------------------------------------------------------------------
def write(row, col, val, src_row=None, src_col=None, commands=None):
"""Записывает данные в ячейку с сохранением стилей."""
src_row = src_row or row
@ -90,35 +92,19 @@ def fill_xls(request, dictionary, src_sheet, dst_sheet, style_list, xls_settings
context.update(kwargs)
return template.render(context)
def set_new_value(file):
filename, file_extension = os.path.splitext(file)
if file_extension == '.png':
if os.path.isfile(f'{filename}.bmp'):
return f'{filename}.bmp'
else:
img = Image.open(file)
background = Image.new("RGB", img.size, (255, 255, 255))
background.paste(img, mask=img.split()[3]) # 3 is the alpha channel
background.save(f'{filename}.bmp')
return f'{filename}.bmp'
def parse_cells(row_from=0, row_to=None, col_from=0, col_to=None,
dst_row_shift=0, dst_col_shift=0, **kwargs):
"""
Ищет шаблонные теги и переменные в ячейках заданного диапазона.
Если находит, то передает содержимое ячейки
целиком на обработку в process_template. После чего записывает
полученный результат обратно в ячейку.
dst_row_shift=0, dst_col_shift=0, **kwargs):
"""Ищет шаблонные теги и переменные в ячейках заданного диапазона. Если находит, то передает содержимое ячейки
целиком на обработку в process_template. После чего записывает полученный результат обратно в ячейку.
Также ищет спец. токены и выполняет соответствующие действия.
"""
row_to = row_to or src_sheet.nrows - 1
col_to = col_to or src_sheet.ncols - 1
row_to = row_to or src_sheet.nrows-1
col_to = col_to or src_sheet.ncols-1
for row in range(row_from, row_to + 1):
for row in xrange(row_from, row_to+1):
cmd_fix_height = []
for col in range(col_from, col_to + 1):
for col in xrange(col_from, col_to+1):
cell = src_sheet.cell(row, col)
cell_value = new_value = cell.value
@ -127,7 +113,7 @@ def fill_xls(request, dictionary, src_sheet, dst_sheet, style_list, xls_settings
cmd_draw_thin_bottom_border = False
# если в ячейке не строка - пропускаем
if not isinstance(new_value, str):
if not isinstance(new_value, unicode):
continue
# поискать шаблонные теги и переменные в ячейке
@ -136,6 +122,7 @@ def fill_xls(request, dictionary, src_sheet, dst_sheet, style_list, xls_settings
new_value = process_template(new_value, **kwargs)
# пофиксить переводы строки
#new_value = new_value.strip().replace('\r\n', '\n')
new_value = new_value.strip().replace('\r\n', ' ')
# команда 'конвертировать во float'
@ -159,7 +146,7 @@ def fill_xls(request, dictionary, src_sheet, dst_sheet, style_list, xls_settings
new_value = new_value.replace(u'@@FIX_HEIGHT@@', u'')
cmd_fix_height.append({
'col': col,
'value': new_value,
'value': unicode(new_value),
})
if new_value != cell_value:
@ -174,12 +161,10 @@ def fill_xls(request, dictionary, src_sheet, dst_sheet, style_list, xls_settings
if new_value != cell_value:
if cmd_show_bmp and new_value:
try:
new_value = set_new_value(new_value)
dst_sheet.insert_bitmap(
new_value,
row=row + dst_row_shift,
col=col + dst_col_shift,
row = row + dst_row_shift,
col = col + dst_col_shift,
)
new_value = ''
except:
@ -189,29 +174,31 @@ def fill_xls(request, dictionary, src_sheet, dst_sheet, style_list, xls_settings
# print "Error inserting image from file '%s'" % new_value
raise
write(
row=row + dst_row_shift,
col=col + dst_col_shift,
val=new_value,
src_row=row,
src_col=col,
commands={'draw_thin_bottom_border': cmd_draw_thin_bottom_border}
row = row + dst_row_shift,
col = col + dst_col_shift,
val = new_value,
src_row = row,
src_col = col,
commands = {'draw_thin_bottom_border': cmd_draw_thin_bottom_border,
}
)
# --- конец цикла по ячейкам в строке
# подобрать высоту строки в ячейках
dst_row = row + dst_row_shift # строка назначения
row_height = dst_sheet.row(dst_row).height # текущая высота
dst_row = row + dst_row_shift # строка назначения
row_height = dst_sheet.row(dst_row).height # текущая высота
max_height = 0
for fh in cmd_fix_height:
# print '---FIX_HEIGHT:', 'dst_row=', dst_row, 'col=', fh['col']
#print '---FIX_HEIGHT:', 'dst_row=', dst_row, 'col=', fh['col']
# взять ширину ячейки
width = 0
# учитываем только объединенные ячейки
for r1, r2, c1, c2 in src_sheet.merged_cells:
for r1,r2,c1,c2 in src_sheet.merged_cells:
if r1 != row or c1 != fh['col']:
continue
for colx in range(c1, c2):
for colx in xrange(c1, c2):
width += src_sheet.computed_column_width(colx)
else:
break
@ -222,8 +209,7 @@ def fill_xls(request, dictionary, src_sheet, dst_sheet, style_list, xls_settings
width_in_pixels = width / 36.5
width_in_chars = width_in_pixels / 5.8
# может быть 0, если команда @@FIX_HEIGHT@@ задана в простой
# (не объединенной) ячейке
# может быть 0, если команда @@FIX_HEIGHT@@ задана в простой (не объединенной) ячейке
if width_in_chars == 0:
# print ('WARNING. xls generation, cmd @@FIX_HEIGHT@@. '
# 'variable `width_in_chars` = %s. skip this command.' % width_in_chars)
@ -234,15 +220,14 @@ def fill_xls(request, dictionary, src_sheet, dst_sheet, style_list, xls_settings
min_rows = 1
need_rows = math.ceil(len(value) / width_in_chars)
need_rows = int(max(min_rows, need_rows))
# print 'need_rows=', need_rows
#print 'need_rows=', need_rows
new_height = row_height * need_rows
# не фиксить высоту, если новая высота данной ячейки меньше либо
# равна текущей высоте
# не фиксить высоту, если новая высота данной ячейки меньше либо равна текущей высоте
if new_height > max_height:
max_height = new_height
else:
# print 'SKIP,', new_height, '<=', max_height
#print 'SKIP,', new_height, '<=', max_height
continue
dst_sheet.row(dst_row).height = new_height
@ -269,12 +254,12 @@ def fill_xls(request, dictionary, src_sheet, dst_sheet, style_list, xls_settings
# --- !!! ------ вывести начало документа включительно по шапку табл. части
copy_cells(src_sheet, dst_sheet, style_list, row_from=0, row_to=p.TBL_BODY_ROW - 1)
copy_cells(src_sheet, dst_sheet, style_list, row_from=0, row_to=p.TBL_BODY_ROW-1)
parse_cells(row_to=p.TBL_BODY_ROW - 1)
parse_cells(row_to=p.TBL_BODY_ROW-1)
# для отладки - выйти здесь
# return
#return
# --- !!! ------------ вывести таблицу с учетом переходов на новую страницу
@ -289,24 +274,24 @@ def fill_xls(request, dictionary, src_sheet, dst_sheet, style_list, xls_settings
row = 0
row_shift = 0
# print '---table:'
#print '---table:'
def write_tbl_body_row():
"""Хелпер для отрисовки строки таблицы.
Зависит от внешних переменных row_shift, row и item!
"""
# print '---table body row, dst_row_shift =', row_shift
#print '---table body row, dst_row_shift =', row_shift
copy_cells(
src_sheet, dst_sheet, style_list,
row_from=p.TBL_BODY_ROW, row_to=p.TBL_BODY_ROW,
dst_row_shift=row_shift
row_from = p.TBL_BODY_ROW, row_to = p.TBL_BODY_ROW,
dst_row_shift = row_shift
)
parse_cells(
row_from=p.TBL_BODY_ROW,
row_to=p.TBL_BODY_ROW,
dst_row_shift=row_shift,
item=item,
item_npp=row + 1
row_from = p.TBL_BODY_ROW,
row_to = p.TBL_BODY_ROW,
dst_row_shift = row_shift,
item = item,
item_npp = row+1
)
def write_tbl_page_footer():
@ -314,19 +299,19 @@ def fill_xls(request, dictionary, src_sheet, dst_sheet, style_list, xls_settings
Зависит от внешних переменных row_shift, last_page_item_idx и row!
"""
dst_row_shift = row_shift - (p.TBL_PAGE_FOOTER_FROM - p.TBL_BODY_ROW)
# print '---table page footer, dst_row_shift =', dst_row_shift, \
#print '---table page footer, dst_row_shift =', dst_row_shift, \
# 'items_start =', last_page_item_idx, 'items_stop =', row
copy_cells(
src_sheet, dst_sheet, style_list,
row_from=p.TBL_PAGE_FOOTER_FROM, row_to=p.TBL_PAGE_FOOTER_TO,
dst_row_shift=dst_row_shift
row_from = p.TBL_PAGE_FOOTER_FROM, row_to = p.TBL_PAGE_FOOTER_TO,
dst_row_shift = dst_row_shift
)
parse_cells(
row_from=p.TBL_PAGE_FOOTER_FROM,
row_to=p.TBL_PAGE_FOOTER_TO,
dst_row_shift=dst_row_shift,
items_start=last_page_item_idx,
items_stop=row
row_from = p.TBL_PAGE_FOOTER_FROM,
row_to = p.TBL_PAGE_FOOTER_TO,
dst_row_shift = dst_row_shift,
items_start = last_page_item_idx,
items_stop = row
)
def write_tbl_header():
@ -334,17 +319,17 @@ def fill_xls(request, dictionary, src_sheet, dst_sheet, style_list, xls_settings
Зависит от внешних переменных row и add_offset!
"""
dst_row_shift = p.TBL_HEADER_ROWS + row + add_offset
# print '---table header, dst_row_shift =', dst_row_shift
#print '---table header, dst_row_shift =', dst_row_shift
copy_cells(
src_sheet, dst_sheet, style_list,
row_from=p.TBL_HEADER_FROM, row_to=p.TBL_HEADER_TO,
dst_row_shift=dst_row_shift
row_from = p.TBL_HEADER_FROM, row_to = p.TBL_HEADER_TO,
dst_row_shift = dst_row_shift
)
# цикл по табличной части документа
for row, item in enumerate(obj_items):
row_shift = row + add_offset
# print 'row = %s, add_offset = %s' % (row, add_offset)
#print 'row = %s, add_offset = %s' % (row, add_offset)
write_tbl_body_row()
row_height = dst_sheet.row(p.TBL_BODY_ROW + row_shift).height
@ -356,13 +341,13 @@ def fill_xls(request, dictionary, src_sheet, dst_sheet, style_list, xls_settings
if curr_height + p.TBL_PAGE_FOOTER_HEIGHT + p.TBL_FOOTER_HEIGHT > p.WORK_HEIGHT:
# если это первая строка, то:
if row == 0:
# print '---table new page, row =', row
#print '---table new page, row =', row
# 1. добавить разрыв страницы перед первой шапкой
horz_page_break(dst_sheet, p.TBL_HEADER_FROM)
curr_height = p.TBL_HEADER_HEIGHT + row_height
# если это не последняя строка, то:
elif row < len(obj_items) - 1:
# print '---table new page, row =', row
elif row < len(obj_items)-1:
#print '---table new page, row =', row
# 1. вместо строки вывести подитог
if p.TBL_PAGE_FOOTER_ROWS > 0:
write_tbl_page_footer()
@ -371,8 +356,7 @@ def fill_xls(request, dictionary, src_sheet, dst_sheet, style_list, xls_settings
add_offset += p.TBL_PAGE_FOOTER_ROWS
row_shift += add_offset
# 2. добавить разрыв страницы
horz_page_break(dst_sheet,
(p.TBL_HEADER_FROM + p.TBL_HEADER_ROWS + row + add_offset))
horz_page_break(dst_sheet, (p.TBL_HEADER_FROM + p.TBL_HEADER_ROWS + row + add_offset))
# 3. вывести шапку
write_tbl_header()
add_offset += p.TBL_HEADER_ROWS
@ -382,28 +366,28 @@ def fill_xls(request, dictionary, src_sheet, dst_sheet, style_list, xls_settings
write_tbl_body_row()
curr_height += row_height
else: # for ... else
else: # for ... else
# вывести подитог, если только что не выводили его в цикле
if p.TBL_PAGE_FOOTER_ROWS > 0 and not just_wrote_page_footer:
# print '---tbl last page, row =', row
row += 1 # чтоб захватить в подитог и последнюю запись тоже
#print '---tbl last page, row =', row
row += 1 # чтоб захватить в подитог и последнюю запись тоже
row_shift = row + add_offset
write_tbl_page_footer()
curr_height += p.TBL_PAGE_FOOTER_HEIGHT
add_offset += row - p.TBL_PAGE_FOOTER_ROWS
# print '---end table'
#print '---end table'
# для отладки - выйти здесь
# return
#return
# --- !!! --------------------------------------- вывести остаток документа
copy_cells(
src_sheet, dst_sheet, style_list,
row_from=p.TBL_FOOTER_FROM,
dst_row_shift=add_offset
row_from = p.TBL_FOOTER_FROM,
dst_row_shift = add_offset
)
# добавить разрыв страницы, если остаток документа не уместится целиком
@ -413,8 +397,8 @@ def fill_xls(request, dictionary, src_sheet, dst_sheet, style_list, xls_settings
horz_page_break(dst_sheet, p.TBL_FOOTER_TO + add_offset + 1)
parse_cells(
row_from=p.TBL_FOOTER_FROM,
dst_row_shift=add_offset
row_from = p.TBL_FOOTER_FROM,
dst_row_shift = add_offset
)
return
@ -423,33 +407,36 @@ def fill_xls(request, dictionary, src_sheet, dst_sheet, style_list, xls_settings
def get_settings(src_book, sheet_name=u'settings'):
"""Загрузить настройки с листа settings."""
settings = {}
# import ipdb;ipdb.set_trace()
try:
src_sheet = src_book.sheet_by_name(sheet_name)
except xlrd.XLRDError:
return settings
for row in range(src_sheet.nrows):
for row in xrange(src_sheet.nrows):
key_cell = src_sheet.cell(row, 0).value
if key_cell:
key_name = key_cell.strip()
key_name = unicode(key_cell).strip()
if not key_name.startswith(u'#'):
val_cell = src_sheet.cell(row, 1).value
if isinstance(val_cell, float) or isinstance(val_cell, int):
if (isinstance(val_cell, unicode) or
isinstance(val_cell, str)):
settings[key_name] = unicode(val_cell)
else:
settings[key_name] = val_cell
return settings
def apply_page_settings(dst_sheet, settings):
"""Применить параметры страницы."""
def setparam(attr, key):
if key in settings:
setattr(dst_sheet, attr, settings[key])
def setparam_as_inch(attr, key):
if key in settings:
setattr(dst_sheet, attr, settings[key] / 25.4)
setattr(dst_sheet, attr, settings[key]/25.4)
setparam('portrait', 'PAGE_PORTRAIT')
setparam('header_str', 'PAGE_HEADER_STR')
@ -465,16 +452,12 @@ def apply_page_settings(dst_sheet, settings):
def get_all_these_boring_params(src_sheet, xls_settings):
"""Достает нужные настройки из словаря и проверят, некоторые вычисляет -
и всё это складывает в класс, который потом и возвращает.
Если какие-то обязательные настройки не заданы, сообщает об этом в консоль и возвращает None.
"""
Достает нужные настройки из словаря и проверят, некоторые вычисляет -
и всё это складывает в класс, который потом и возвращает.
Если какие-то обязательные настройки не заданы, сообщает
об этом в консоль и возвращает None.
"""
class Params(object):
pass
p = Params()
# строка контента таблицы - обязательно
@ -528,8 +511,7 @@ def get_all_these_boring_params(src_sheet, xls_settings):
# высота в строках
p.TBL_PAGE_FOOTER_ROWS = int(p.TBL_PAGE_FOOTER_TO - p.TBL_PAGE_FOOTER_FROM + 1)
# высота в twips
p.TBL_PAGE_FOOTER_HEIGHT = sum_src_heights(src_sheet, p.TBL_PAGE_FOOTER_FROM,
p.TBL_PAGE_FOOTER_TO)
p.TBL_PAGE_FOOTER_HEIGHT = sum_src_heights(src_sheet, p.TBL_PAGE_FOOTER_FROM, p.TBL_PAGE_FOOTER_TO)
else:
p.TBL_PAGE_FOOTER_ROWS = 0
p.TBL_PAGE_FOOTER_HEIGHT = 0

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save