Compare commits

..

28 Commits

Author SHA1 Message Date
FUNNYDMAN 9c3df89cb6 fix problem with register confirmation 7 years ago
FUNNYDMAN 20f706acaa add checking for captcha 7 years ago
FUNNYDMAN a19a96eac6 trying to fix problem with captcha 7 years ago
FUNNYDMAN af65dbc7ba trying to fix problem with captcha 7 years ago
FUNNYDMAN ce305afae2 add check for captcha 7 years ago
FUNNYDMAN 7321a632f1 fix problem with captcha binding 7 years ago
FUNNYDMAN 08e9ca1762 configure webpack and remove shitty stuff 7 years ago
FUNNYDMAN 7904795a30 make client images inactive, i.m. without any links 7 years ago
FUNNYDMAN b073155efc test the search by products 7 years ago
FUNNYDMAN cbedba8c3f fix problem with contact us form 7 years ago
FUNNYDMAN d22034f02e trying to fix problem with contact us form 7 years ago
FUNNYDMAN 289eecb57e update development.py.template according to the actual settings 7 years ago
FUNNYDMAN a117ba8bea add simple nginx config 7 years ago
FUNNYDMAN 19f9cc95e9 use only postgres as database for testing 7 years ago
FUNNYDMAN 75106ef662 make django toolbar available 7 years ago
FUNNYDMAN 788ee4f88b restructure project 7 years ago
FUNNYDMAN a0b1a36fc1 remove some ugly stuff from models 7 years ago
FUNNYDMAN 06d39b615b add test for the Manufacturer model 7 years ago
FUNNYDMAN 7cd4a3fbce remove commented stuff from admin file 7 years ago
FUNNYDMAN 1a9e008ed0 fix problem with displaying products 7 years ago
FUNNYDMAN c56134f01e remove temp 7 years ago
FUNNYDMAN 37a855ad88 remove unused product_search template 7 years ago
FUNNYDMAN 1d192b8075 delete unused categorieslist 7 years ago
FUNNYDMAN 3624239a4b delete unused template 7 years ago
FUNNYDMAN b315ef3fe8 add simple mock images for testing front-end part 7 years ago
FUNNYDMAN de0ff868be optimize imports and reformat code according to pep8 7 years ago
FUNNYDMAN 8d2d52ae45 fix typos in tests 7 years ago
FUNNYDMAN ecd92b31d5 add folders for migrations 7 years ago
  1. 11
      .gitignore
  2. 54
      README.md
  3. 1
      accounts_ext/fixtures/accounts.json
  4. 0
      client/asserts/css/autocomplete.css
  5. 8197
      client/asserts/css/bootstrap.css
  6. 93
      client/asserts/css/jquery.rating.css
  7. 0
      client/asserts/css/landing.css
  8. 1956
      client/asserts/css/main.css
  9. 0
      client/asserts/css/main.fix.css
  10. 0
      client/asserts/css/profile.css
  11. 0
      client/asserts/css/reset.css
  12. 0
      client/asserts/img/Payment_foot.jpg
  13. 0
      client/asserts/img/arb.svg
  14. 0
      client/asserts/img/arrow-down.svg
  15. 0
      client/asserts/img/bg_image.jpg
  16. 0
      client/asserts/img/close.svg
  17. 0
      client/asserts/img/coins.png
  18. 0
      client/asserts/img/favicon/apple-touch-icon-180x180.png
  19. 0
      client/asserts/img/favicon/favicon.ico
  20. 0
      client/asserts/img/header/cart-icon.svg
  21. 0
      client/asserts/img/header/logo.png
  22. 0
      client/asserts/img/license/1.jpg
  23. 0
      client/asserts/img/license/2.jpg
  24. 0
      client/asserts/img/menu/1c.svg
  25. 0
      client/asserts/img/menu/antivirus.svg
  26. 0
      client/asserts/img/menu/graphic.svg
  27. 0
      client/asserts/img/menu/menu-mob.svg
  28. 0
      client/asserts/img/menu/office.svg
  29. 0
      client/asserts/img/menu/planning.svg
  30. 0
      client/asserts/img/menu/save.svg
  31. 0
      client/asserts/img/menu/virtualization.svg
  32. 0
      client/asserts/img/menu/windows.svg
  33. 0
      client/asserts/img/no-image.jpg
  34. 0
      client/asserts/img/pay/maestro.svg
  35. 0
      client/asserts/img/pay/mastercard.svg
  36. 0
      client/asserts/img/pay/visa.svg
  37. 0
      client/asserts/img/payment_header.jpg
  38. 0
      client/asserts/img/po_create/1c.png
  39. 0
      client/asserts/img/po_create/avast.png
  40. 0
      client/asserts/img/po_create/corel.png
  41. 0
      client/asserts/img/po_create/eset.png
  42. 0
      client/asserts/img/po_create/kerio.png
  43. 0
      client/asserts/img/po_create/mcAfee.png
  44. 0
      client/asserts/img/po_create/roadmin.png
  45. 0
      client/asserts/img/po_create/veeam.png
  46. 0
      client/asserts/img/search.svg
  47. 0
      client/asserts/img/star.svg
  48. 0
      client/asserts/img/test-b.png
  49. 0
      client/asserts/js/ajax.js
  50. 0
      client/asserts/js/basket.tools.js
  51. 7
      client/asserts/js/common.js
  52. 0
      client/asserts/js/our_search_code.js
  53. 16
      client/index.js
  54. 29
      client/package.json
  55. 48
      client/webpack.config.js
  56. 11
      deployment/eshop.conf
  57. 0
      eshop/.env.sample
  58. 0
      eshop/accounts_ext/__init__.py
  59. 7
      eshop/accounts_ext/admin.py
  60. 0
      eshop/accounts_ext/apps.py
  61. 20
      eshop/accounts_ext/fixtures/accounts.json
  62. 15
      eshop/accounts_ext/forms.py
  63. 110
      eshop/accounts_ext/migrations/0001_initial.py
  64. 0
      eshop/accounts_ext/migrations/__init__.py
  65. 5
      eshop/accounts_ext/models.py
  66. 0
      eshop/accounts_ext/templatetags/__init__.py
  67. 1
      eshop/accounts_ext/templatetags/accounts_ext_filters.py
  68. 0
      eshop/accounts_ext/tests.py
  69. 5
      eshop/accounts_ext/urls.py
  70. 0
      eshop/accounts_ext/utils.py
  71. 19
      eshop/accounts_ext/views.py
  72. 0
      eshop/blog_ext/__init__.py
  73. 6
      eshop/blog_ext/admin.py
  74. 0
      eshop/blog_ext/apps.py
  75. 1
      eshop/blog_ext/forms.py
  76. 93
      eshop/blog_ext/migrations/0001_initial.py
  77. 0
      eshop/blog_ext/migrations/__init__.py
  78. 7
      eshop/blog_ext/models.py
  79. 0
      eshop/blog_ext/tests.py
  80. 2
      eshop/blog_ext/urls.py
  81. 1
      eshop/blog_ext/views.py
  82. 0
      eshop/cabinet/__init__.py
  83. 0
      eshop/cabinet/admin.py
  84. 0
      eshop/cabinet/apps.py
  85. 0
      eshop/cabinet/migrations/__init__.py
  86. 0
      eshop/cabinet/models.py
  87. 0
      eshop/cabinet/tests.py
  88. 0
      eshop/cabinet/urls.py
  89. 41
      eshop/cabinet/views.py
  90. 0
      eshop/cart/__init__.py
  91. 16
      eshop/cart/admin.py
  92. 0
      eshop/cart/apps.py
  93. 1
      eshop/cart/context_processors.py
  94. 0
      eshop/cart/fixtures/supply_targets.json
  95. 0
      eshop/cart/fixtures/supply_types.json
  96. 9
      eshop/cart/forms.py
  97. 0
      eshop/cart/middleware.py
  98. 195
      eshop/cart/migrations/0001_initial.py
  99. 25
      eshop/cart/migrations/0002_auto_20181027_1653.py
  100. 0
      eshop/cart/migrations/__init__.py
  101. Some files were not shown because too many files have changed in this diff Show More

11
.gitignore vendored

@ -33,6 +33,7 @@ __pycache__/
# Distribution / packaging # Distribution / packaging
.Python .Python
env/ env/
venv/
build/ build/
develop-eggs/ develop-eggs/
dist/ dist/
@ -77,17 +78,17 @@ docs/_build/
target/ target/
### Django ### ### Project ###
*.log *.log
*.pot *.pot
*.pyc *.pyc
__pycache__/ __pycache__/
assets/ **/node_modules/**
node_modules/
package-lock.json package-lock.json
development.py development.py
*/migrations/* static/
client/node_modules/*
client/dist/*
.env .env
db.sqlite3 db.sqlite3

@ -8,27 +8,58 @@
--- ---
### Server configuration ### Server configuration
1. Use systemd file with name gunicorn-eshop.service to initialize gunicorn server with DJANGO_SETTINGS_MODULE environment var 1. Install necessary packages:
2. Use systemd file with name celery-eshop.service to initialize celery following with these [instructions](http://docs.celeryproject.org/en/latest/userguide/daemonizing.html#usage-systemd "Celery | Daemonization") ```bash
2. Install virtualenv inside the project directory in run server sudo apt-get install -y libpq-dev postgresql nginx virtualenv
3. Install dependencies from inside the activated virtualenv curl -sL https://deb.nodesource.com/setup_10.x | sudo -E bash -
4. Create empty logs dir in project directory sudo apt-get install -y nodejs
5. Install nodejs with npm(nvm preferably) ```
6. install all packages
2. Create database:
```bash
CREATE DATABASE eshop_db;
CREATE USER eshop_admin WITH password '12345678';
GRANT ALL ON DATABASE eshop_db TO eshop_admin;
# Allow user to create db for running django tests
ALTER USER eshop_admin CREATEDB;
```
3. Install virtualenv inside the project directory and activate it:
```bash
virtualenv --python3=$(which python3) venv
source venv/bin/activate
```
4. Use systemd file with name gunicorn-eshop.service to initialize gunicorn server with DJANGO_SETTINGS_MODULE environment var
5. Use systemd file with name celery-eshop.service to initialize celery following with these [instructions](http://docs.celeryproject.org/en/latest/userguide/daemonizing.html#usage-systemd "Celery | Daemonization")
6. Install dependencies from inside the activated virtualenv:
```bash
pip install -r requirements.txt
```
7. Install all packages from package.json in the client/ directory and run webpack
```bash
npm i
npm run dev
```
8. Create empty logs dir in project directory
###Development ###Development
1. Activate eshop environment using virtualenvwrapper(workon) 1. Activate eshop environment using virtualenv
2. Make .env file with the structure described in .env.sample file 2. Make .env file with the structure described in .env.sample file
3. Make migrations for modules: auth, accounts_ext, etc. 3. Make migrations for modules: auth, accounts_ext, etc.
4. Start celery queue for tasks with the command: 4. Start celery queue for tasks with the command:
`celery -A eshop_project worker -l info --pool=eventlet` `celery -A eshop_project worker -l info --pool=eventlet`
5. Make alias for gulp with alias command and path gulp executable file
6. Run `gulp default`
###Deployment ###Deployment
1. Activate eshop environment using virtualenvwrapper(workon) 1. Activate eshop environment using virtualenv
2. Make pull request from bitbucket repo by ssh using passphrase 2. Make pull request from bitbucket repo by ssh using passphrase
3. Make .env file with the structure described in .env.sample file 3. Make .env file with the structure described in .env.sample file
4. Make migrations for modules 4. Make migrations for modules
@ -36,4 +67,5 @@
6. Restart gunicorn-eshop and celery-eshop daemons in systemd 6. Restart gunicorn-eshop and celery-eshop daemons in systemd
7. Load fixture core fixture sites to configure site domain (before the FIRST http request) 7. Load fixture core fixture sites to configure site domain (before the FIRST http request)
8. Add corresponding entry to django_site table

@ -1 +0,0 @@
[{"model": "accounts_ext.user", "pk": 1, "fields": {"password": "pbkdf2_sha256$100000$AAP4DpqYKrFB$mQRKz7qi/1va3Zoh2O3g9j7wYKCBaMLz5LFrbszDNLA=", "last_login": "2018-08-12T20:46:54Z", "create_at": "2018-08-12T20:46:44.961Z", "updated_at": "2018-08-12T20:47:05.733Z", "status": 25, "is_superuser": true, "username": "dimkasp", "email": "dimkasp@mail.ru", "referral_user": null, "confirmed_at": null, "groups": [], "user_permissions": []}}]

File diff suppressed because it is too large Load Diff

@ -0,0 +1,93 @@
.br-theme-css-stars .br-widget {
height: 28px;
white-space: nowrap
}
.br-theme-css-stars .br-widget a {
text-decoration: none;
height: 18px;
width: 18px;
float: left;
font-size: 23px;
margin-right: 5px
}
.br-theme-css-stars .br-widget a:after {
content: "\2605";
color: #d2d2d2
}
.br-theme-css-stars .br-widget a.br-active:after {
color: #edb867
}
.br-theme-css-stars .br-widget a.br-selected:after {
color: #edb867
}
.br-theme-css-stars .br-widget .br-current-rating {
display: none
}
.br-theme-css-stars .br-readonly a {
cursor: default
}
@media print {
.br-theme-css-stars .br-widget a:after {
content: "\2606";
color: #000
}
.br-theme-css-stars .br-widget a.br-active:after, .br-theme-css-stars .br-widget a.br-selected:after {
content: "\2605";
color: #000
}
}
.br-theme-fontawesome-stars .br-widget {
height: 28px;
white-space: nowrap
}
.br-theme-fontawesome-stars .br-widget a {
font: normal normal normal 20px/1 FontAwesome;
text-rendering: auto;
-webkit-font-smoothing: antialiased;
text-decoration: none;
margin-right: 2px
}
.br-theme-fontawesome-stars .br-widget a:after {
content: '\f005';
color: #d2d2d2
}
.br-theme-fontawesome-stars .br-widget a.br-active:after {
color: #edb867
}
.br-theme-fontawesome-stars .br-widget a.br-selected:after {
color: #edb867
}
.br-theme-fontawesome-stars .br-widget .br-current-rating {
display: none
}
.br-theme-fontawesome-stars .br-readonly a {
cursor: default
}
@media print {
.br-theme-fontawesome-stars .br-widget a:after {
content: '\f006';
color: #000
}
.br-theme-fontawesome-stars .br-widget a.br-active:after, .br-theme-fontawesome-stars .br-widget a.br-selected:after {
content: '\f005';
color: #000
}
}

File diff suppressed because it is too large Load Diff

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 21 KiB

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

Before

Width:  |  Height:  |  Size: 418 KiB

After

Width:  |  Height:  |  Size: 418 KiB

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Before

Width:  |  Height:  |  Size: 9.4 KiB

After

Width:  |  Height:  |  Size: 9.4 KiB

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Before

Width:  |  Height:  |  Size: 7.7 KiB

After

Width:  |  Height:  |  Size: 7.7 KiB

Before

Width:  |  Height:  |  Size: 266 KiB

After

Width:  |  Height:  |  Size: 266 KiB

Before

Width:  |  Height:  |  Size: 254 KiB

After

Width:  |  Height:  |  Size: 254 KiB

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Before

Width:  |  Height:  |  Size: 4.7 KiB

After

Width:  |  Height:  |  Size: 4.7 KiB

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Before

Width:  |  Height:  |  Size: 6.0 KiB

After

Width:  |  Height:  |  Size: 6.0 KiB

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Before

Width:  |  Height:  |  Size: 5.8 KiB

After

Width:  |  Height:  |  Size: 5.8 KiB

Before

Width:  |  Height:  |  Size: 7.5 KiB

After

Width:  |  Height:  |  Size: 7.5 KiB

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 28 KiB

Before

Width:  |  Height:  |  Size: 9.6 KiB

After

Width:  |  Height:  |  Size: 9.6 KiB

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 21 KiB

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 23 KiB

Before

Width:  |  Height:  |  Size: 926 B

After

Width:  |  Height:  |  Size: 926 B

Before

Width:  |  Height:  |  Size: 721 B

After

Width:  |  Height:  |  Size: 721 B

Before

Width:  |  Height:  |  Size: 148 KiB

After

Width:  |  Height:  |  Size: 148 KiB

@ -49,13 +49,6 @@
}); });
// Magnific popup
$('.certificate__item').magnificPopup({
type: 'image'
// other options
});
// звезды // звезды
var stars = $('.stars'); var stars = $('.stars');
stars.barrating({ stars.barrating({

@ -0,0 +1,16 @@
import "./asserts/css/main.css";
import './node_modules/jquery-ui-dist/jquery-ui.min.css';
import "./node_modules/bootstrap/dist/css/bootstrap.min.css";
import './node_modules/jquery/dist/jquery.js';
import './node_modules/jquery-ui-dist/jquery-ui.js';
import './node_modules/magnific-popup/dist/jquery.magnific-popup.js';
import './node_modules/jquery-bar-rating/dist/jquery.barrating.min.js';
import './node_modules/bootstrap/dist/js/bootstrap.js';
import './node_modules/jquery-autocomplete/jquery.autocomplete.js';
import "./asserts/js/ajax.js";
import "./asserts/js/basket.tools.js";
import "./asserts/js/common.js";
import "./asserts/js/our_search_code.js";

@ -0,0 +1,29 @@
{
"name": "eshop",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"dev": "webpack --mode=development --watch",
"build": "webpack --mode=production"
},
"author": "FUNNYDMAN",
"license": "ISC",
"devDependencies": {
"webpack": "^4.25.1",
"webpack-cli": "^3.1.2",
"css-loader": "^1.0.1",
"extract-text-webpack-plugin": "^4.0.0-beta.0",
"file-loader": "^2.0.0",
"style-loader": "^0.23.1"
},
"dependencies": {
"bootstrap": "3.3.7",
"jquery": "^3.3.1",
"jquery-autocomplete": "^1.2.8",
"jquery-bar-rating": "^1.2.2",
"jquery-ui-dist": "1.12.1",
"magnific-popup": "^1.1.0"
}
}

@ -0,0 +1,48 @@
const path = require('path');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const webpack = require('webpack');
module.exports = {
entry: './index.js',
output: {
filename: './js/build-min.js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [{
test: /\.(css)$/,
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: ['css-loader']
})
},
{
test: /\.(png|svg|jpg|gif|jpeg)$/,
use: [
{
loader: 'file-loader',
options: {outputPath: 'img/'}
}
]
}, {
test: /\.(woff(2)?|ttf|eot)(\?v=\d+\.\d+\.\d+)?$/,
use: [
{
loader: 'file-loader',
options: {
name: '[name].[ext]',
outputPath: 'fonts/'
}
}
]
}
]
},
plugins: [
new ExtractTextPlugin("css/build.css"),
new webpack.ProvidePlugin({
$: "jquery",
jQuery: "jquery"
})
]
};

@ -0,0 +1,11 @@
server {
server_name name_or_ip;
listen 80;
location / {
proxy_pass http://localhost:8000;
}
location /static/ {
root path_to_static_files;
}
}

@ -1,7 +1,7 @@
from django.contrib import admin from django.contrib import admin
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from django.contrib.auth.models import Group as GroupBase
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin, GroupAdmin from django.contrib.auth.admin import UserAdmin as BaseUserAdmin, GroupAdmin
from django.contrib.auth.models import Group as GroupBase
from django.urls import reverse_lazy from django.urls import reverse_lazy
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from import_export import resources from import_export import resources
@ -9,8 +9,7 @@ from jet.filters import DateRangeFilter
from rangefilter.filter import DateTimeRangeFilter from rangefilter.filter import DateTimeRangeFilter
from core.admin import SafeModelAdmin from core.admin import SafeModelAdmin
# from referral.admin import ReferralAdminInline from referral.admin import ReferralAdminInline
from .forms import UserChangeForm, UserCreationForm from .forms import UserChangeForm, UserCreationForm
from .models import Profile, Company, Group from .models import Profile, Company, Group
@ -27,7 +26,7 @@ class CustomUserResource(resources.ModelResource):
@admin.register(get_user_model()) @admin.register(get_user_model())
class UserAdmin(SafeModelAdmin, BaseUserAdmin): class UserAdmin(SafeModelAdmin, BaseUserAdmin):
inlines = ( inlines = (
# ReferralAdminInline, ReferralAdminInline,
) )
fieldsets = ( fieldsets = (

@ -0,0 +1,20 @@
[
{
"model": "accounts_ext.user",
"pk": 1,
"fields": {
"password": "pbkdf2_sha256$100000$AAP4DpqYKrFB$mQRKz7qi/1va3Zoh2O3g9j7wYKCBaMLz5LFrbszDNLA=",
"last_login": "2018-08-12T20:46:54Z",
"create_at": "2018-08-12T20:46:44.961Z",
"updated_at": "2018-08-12T20:47:05.733Z",
"status": 25,
"is_superuser": true,
"username": "dimkasp",
"email": "dimkasp@mail.ru",
"referral_user": null,
"confirmed_at": null,
"groups": [],
"user_permissions": []
}
}
]

@ -1,13 +1,9 @@
import logging import logging
from crispy_forms.helper import FormHelper from crispy_forms.helper import FormHelper
from crispy_forms.layout import Layout, Field, Div, Submit, HTML from crispy_forms.layout import Layout, Field, Div, Submit, HTML
from django import forms from django import forms
from django.conf import settings
from django.contrib.auth import get_user_model, password_validation from django.contrib.auth import get_user_model, password_validation
from django.urls import reverse, reverse_lazy
from django.utils.translation import ugettext_lazy as _
from django_email_blacklist import DisposableEmailChecker
from registration.forms import RegistrationFormUniqueEmail
from django.contrib.auth.forms import ( from django.contrib.auth.forms import (
AuthenticationForm as AuthenticationFormBase, AuthenticationForm as AuthenticationFormBase,
PasswordResetForm as PasswordResetFormBase, PasswordResetForm as PasswordResetFormBase,
@ -16,6 +12,10 @@ from django.contrib.auth.forms import (
UserCreationForm as UserCreationFormBase UserCreationForm as UserCreationFormBase
) )
from django.contrib.auth.forms import UsernameField from django.contrib.auth.forms import UsernameField
from django.urls import reverse, reverse_lazy
from django.utils.translation import ugettext_lazy as _
from django_email_blacklist import DisposableEmailChecker
from registration.forms import RegistrationFormUniqueEmail
from snowpenguin.django.recaptcha2.fields import ReCaptchaField from snowpenguin.django.recaptcha2.fields import ReCaptchaField
from snowpenguin.django.recaptcha2.widgets import ReCaptchaWidget from snowpenguin.django.recaptcha2.widgets import ReCaptchaWidget
@ -56,6 +56,11 @@ class RegistrationForm(RegistrationFormUniqueEmail):
raise forms.ValidationError(_('Введите email с валидными доменом')) raise forms.ValidationError(_('Введите email с валидными доменом'))
return email return email
def clean(self):
if not self.cleaned_data['captcha']:
raise forms.ValidationError(_("Проверьте правильности капчи"))
return self.cleaned_data
def save(self, commit=True): def save(self, commit=True):
user = super().save(commit) user = super().save(commit)
profile = Profile.objects.filter(user=user).first() profile = Profile.objects.filter(user=user).first()

@ -0,0 +1,110 @@
# Generated by Django 2.0.7 on 2018-10-25 15:27
from django.conf import settings
import django.contrib.auth.models
import django.core.validators
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
('auth', '0009_alter_user_last_name_max_length'),
('core', '0001_initial'),
]
operations = [
migrations.CreateModel(
name='User',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('password', models.CharField(max_length=128, verbose_name='password')),
('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')),
('create_at', models.DateTimeField(auto_now_add=True, verbose_name='создан в')),
('updated_at', models.DateTimeField(auto_now=True, verbose_name='обновлен')),
('status', models.SmallIntegerField(choices=[(0, 'Новый'), (25, 'Активный'), (50, 'Удаленный')], default=0, verbose_name='статус')),
('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')),
('username', models.CharField(max_length=255, verbose_name='username')),
('email', models.EmailField(error_messages={'unique': 'A user with that email already exists.'}, max_length=254, unique=True, verbose_name='email')),
('confirmed_at', models.DateTimeField(blank=True, null=True, verbose_name='подвтержден в')),
],
options={
'verbose_name': 'пользователь',
'verbose_name_plural': 'пользователи',
},
),
migrations.CreateModel(
name='Company',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('create_at', models.DateTimeField(auto_now_add=True, verbose_name='создан в')),
('updated_at', models.DateTimeField(auto_now=True, verbose_name='обновлен')),
('status', models.SmallIntegerField(choices=[(0, 'Новый'), (25, 'Активный'), (50, 'Удаленный')], default=0, verbose_name='статус')),
('company_name', models.CharField(blank=True, max_length=255, null=True, verbose_name='компания')),
('address', models.TextField(blank=True, null=True, verbose_name='адрес')),
('inn', models.CharField(blank=True, max_length=12, null=True, verbose_name='ИНН')),
('ogrn', models.CharField(blank=True, max_length=13, null=True, verbose_name='ОГРН')),
('type', models.SmallIntegerField(blank=True, choices=[(25, 'Компания'), (50, 'Физ лицо')], default=25, null=True, verbose_name='тип')),
('city', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='core.City')),
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='username')),
],
options={
'verbose_name': 'компания',
'verbose_name_plural': 'компании',
},
),
migrations.CreateModel(
name='Profile',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('create_at', models.DateTimeField(auto_now_add=True, verbose_name='создан в')),
('updated_at', models.DateTimeField(auto_now=True, verbose_name='обновлен')),
('status', models.SmallIntegerField(choices=[(0, 'Новый'), (25, 'Активный'), (50, 'Удаленный')], default=0, verbose_name='статус')),
('first_name', models.CharField(blank=True, max_length=100, null=True, verbose_name='first name')),
('last_name', models.CharField(blank=True, max_length=100, null=True, verbose_name='last name')),
('patronymic', models.CharField(blank=True, max_length=100, null=True, verbose_name='отчество')),
('birthday', models.DateField(blank=True, null=True, verbose_name='дата рождения')),
('phone', models.CharField(blank=True, max_length=12, null=True, validators=[django.core.validators.RegexValidator(message="Phone number must be entered in the format: '+99999999999'. Up to 12 digits allowed.", regex='^\\((+7)|8)?\\d{10}$')], verbose_name='телефон')),
('address', models.TextField(verbose_name='aдрес')),
('city', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='core.City', verbose_name='город')),
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='username')),
],
options={
'verbose_name': 'профиль',
'verbose_name_plural': 'профили',
},
),
migrations.CreateModel(
name='Group',
fields=[
],
options={
'verbose_name': 'группа',
'verbose_name_plural': 'группы',
'proxy': True,
'indexes': [],
},
bases=('auth.group',),
managers=[
('objects', django.contrib.auth.models.GroupManager()),
],
),
migrations.AddField(
model_name='user',
name='groups',
field=models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.Group', verbose_name='groups'),
),
migrations.AddField(
model_name='user',
name='referral_user',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL, verbose_name='Реферальный пользователь'),
),
migrations.AddField(
model_name='user',
name='user_permissions',
field=models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.Permission', verbose_name='user permissions'),
),
]

@ -6,9 +6,8 @@ from django.core.validators import RegexValidator
from django.db import models from django.db import models
from django.db.models.signals import post_save from django.db.models.signals import post_save
from django.dispatch import receiver from django.dispatch import receiver
from django.utils.translation import ugettext_lazy as _
from django.utils.timezone import now as datetime_now from django.utils.timezone import now as datetime_now
from django.utils.translation import ugettext_lazy as _
from registration.signals import user_activated from registration.signals import user_activated
from core.models import ( from core.models import (
@ -18,8 +17,6 @@ from core.models import (
) )
# Create your models here.
class UserManager(ActualOnlyManager, BaseUserManager): class UserManager(ActualOnlyManager, BaseUserManager):
def create_superuser(self, email, password): def create_superuser(self, email, password):
user = self.model( user = self.model(

@ -2,6 +2,7 @@ from django.template import Library
register = Library() register = Library()
@register.simple_tag(name='get') @register.simple_tag(name='get')
def get_by_key(dict, key): def get_by_key(dict, key):
return getattr(dict, key) return getattr(dict, key)

@ -1,6 +1,6 @@
from django.urls import re_path from django.urls import re_path
from . import views
from . import views
urlpatterns = [ urlpatterns = [
re_path(r'^login/$', views.LoginView.as_view(), name='login'), re_path(r'^login/$', views.LoginView.as_view(), name='login'),
@ -9,6 +9,7 @@ urlpatterns = [
re_path(r'^register/done/$', views.RegistrationDoneView.as_view(), name='register_done'), re_path(r'^register/done/$', views.RegistrationDoneView.as_view(), name='register_done'),
re_path(r'^password/reset/$', views.ResetPasswordView.as_view(), name='reset_password'), re_path(r'^password/reset/$', views.ResetPasswordView.as_view(), name='reset_password'),
re_path(r'^password/reset/done/$', views.ResetPasswordDoneView.as_view(), name='reset_password_done'), re_path(r'^password/reset/done/$', views.ResetPasswordDoneView.as_view(), name='reset_password_done'),
re_path(r'^password/reset/change/(?P<uidb64>[0-9A-Za-z_\-]+)/(?P<token>.+)/$', views.ResetPasswordConfirmView.as_view(), name='reset_password_change'), re_path(r'^password/reset/change/(?P<uidb64>[0-9A-Za-z_\-]+)/(?P<token>.+)/$',
views.ResetPasswordConfirmView.as_view(), name='reset_password_change'),
re_path(r'^password/reset/complete/$', views.ResetPasswordCompleteView.as_view(), name='reset_password_complete') re_path(r'^password/reset/complete/$', views.ResetPasswordCompleteView.as_view(), name='reset_password_complete')
] ]

@ -1,6 +1,6 @@
import logging import logging
from django.conf import settings from django.conf import settings
from django.contrib.auth.forms import SetPasswordForm
from django.contrib.auth.views import ( from django.contrib.auth.views import (
LoginView as LoginViewBase, LoginView as LoginViewBase,
LogoutView, LogoutView,
@ -9,29 +9,26 @@ from django.contrib.auth.views import (
PasswordResetConfirmView as PasswordResetConfirmViewBase, PasswordResetConfirmView as PasswordResetConfirmViewBase,
PasswordResetCompleteView as PasswordResetCompleteViewBase, PasswordResetCompleteView as PasswordResetCompleteViewBase,
) )
from django.shortcuts import redirect, resolve_url
from django.urls import reverse_lazy
from django.utils.http import is_safe_url, urlencode
from django.utils.translation import ugettext_lazy as _
from django.views.generic import TemplateView from django.views.generic import TemplateView
from registration.backends.default.views import ( from registration.backends.default.views import (
RegistrationView as RegistrationViewBase, RegistrationView as RegistrationViewBase,
ResendActivationView as ResendActivationViewBase ResendActivationView as ResendActivationViewBase
) )
from django.contrib.messages.views import SuccessMessageMixin
from django.shortcuts import redirect, resolve_url
from django.urls import reverse_lazy
from django.utils.http import is_safe_url, urlencode
from django.utils.translation import ugettext_lazy as _
from core.views import ProtectedTemplateView from core.views import ProtectedTemplateView
from .forms import ( from .forms import (
PasswordResetRequestForm, SetPasswordForm, RegistrationCompanyForm, PasswordResetRequestForm, SetPasswordForm, RegistrationCompanyForm,
AuthenticationForm, AuthenticationForm,
RegistrationForm) RegistrationForm
)
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
# Create your views here.
class LoginView(LoginViewBase): class LoginView(LoginViewBase):
redirect_field_name = 'next' redirect_field_name = 'next'
form_class = AuthenticationForm form_class = AuthenticationForm
@ -152,7 +149,7 @@ class RegistrationDoneView(TemplateView):
class ResendActivationView(ResendActivationViewBase): class ResendActivationView(ResendActivationViewBase):
template_name = 'registration/resend_activation.tml' template_name = 'registration/resend_activation.html'
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs) context = super().get_context_data(**kwargs)

@ -1,14 +1,11 @@
from ckeditor_uploader.widgets import CKEditorUploadingWidget
from django.contrib import admin from django.contrib import admin
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from ckeditor_uploader.widgets import CKEditorUploadingWidget
from pinax.blog.admin import ( from pinax.blog.admin import (
PostAdmin as BasePostAdmin, PostAdmin as BasePostAdmin,
SectionAdmin as BaseSectionAdmin, SectionAdmin as BaseSectionAdmin,
PostImageSet as BasePostImageSet PostImageSet as BasePostImageSet
) )
from pinax.blog.models import ( from pinax.blog.models import (
Post as PinaxPost, Post as PinaxPost,
Section as PinaxSection, Section as PinaxSection,
@ -60,7 +57,6 @@ class PostImageSet(ImageSet):
verbose_name_plural = _('Изображения') verbose_name_plural = _('Изображения')
admin.site.unregister(BasePostImageSet) admin.site.unregister(BasePostImageSet)
admin.site.register( admin.site.register(

@ -1,6 +1,5 @@
from django.forms import forms from django.forms import forms
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from pinax.blog.forms import AdminPostForm as BaseAdminPostForm from pinax.blog.forms import AdminPostForm as BaseAdminPostForm
from blog_ext.models import Post from blog_ext.models import Post

@ -0,0 +1,93 @@
# Generated by Django 2.0.7 on 2018-10-25 15:27
import blog_ext.models
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
('blog', '0001_initial'),
('pinax_images', '0001_initial'),
]
operations = [
migrations.CreateModel(
name='ImageSet',
fields=[
('imageset_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='pinax_images.ImageSet')),
('create_at', models.DateTimeField(auto_now_add=True, verbose_name='создан в')),
('updated_at', models.DateTimeField(auto_now=True, verbose_name='обновлен')),
],
options={
'verbose_name': 'Коллаж',
'verbose_name_plural': 'Коллажи',
},
bases=('pinax_images.imageset', models.Model),
),
migrations.CreateModel(
name='Post',
fields=[
('post_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='blog.Post')),
('create_at', models.DateTimeField(auto_now_add=True, verbose_name='создан в')),
('updated_at', models.DateTimeField(auto_now=True, verbose_name='обновлен')),
('status', models.SmallIntegerField(choices=[(0, 'Новый'), (25, 'Активный'), (50, 'Удаленный')], default=0, verbose_name='статус')),
('preview_image', models.FileField(blank=True, null=True, upload_to=blog_ext.models.Post.upload_file_to, verbose_name='Превью изображение')),
],
options={
'verbose_name': 'Пост',
'verbose_name_plural': 'Посты',
},
bases=('blog.post', models.Model),
),
migrations.CreateModel(
name='ReviewComment',
fields=[
('reviewcomment_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='blog.ReviewComment')),
],
options={
'verbose_name': 'Комментарий',
'verbose_name_plural': 'Комментарий',
},
bases=('blog.reviewcomment',),
),
migrations.CreateModel(
name='Revision',
fields=[
('revision_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='blog.Revision')),
],
options={
'verbose_name': 'Ревизия',
'verbose_name_plural': 'Ревизии',
},
bases=('blog.revision',),
),
migrations.CreateModel(
name='Section',
fields=[
('section_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='blog.Section')),
('create_at', models.DateTimeField(auto_now_add=True, verbose_name='создан в')),
('updated_at', models.DateTimeField(auto_now=True, verbose_name='обновлен')),
],
options={
'verbose_name': 'Секция',
'verbose_name_plural': 'Секции',
},
bases=('blog.section', models.Model),
),
migrations.CreateModel(
name='PostImageSet',
fields=[
],
options={
'verbose_name': 'Изображение',
'verbose_name_plural': 'Изображения',
'proxy': True,
'indexes': [],
},
bases=('blog_ext.imageset',),
),
]

@ -1,11 +1,8 @@
import uuid
import pytz import pytz
from django.conf import settings from django.conf import settings
from django.db import models from django.db import models
from django.urls import reverse_lazy from django.urls import reverse_lazy
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
# Create your models here. # Create your models here.
from pinax.blog.models import ( from pinax.blog.models import (
Post as BasePost, Post as BasePost,
@ -14,9 +11,7 @@ from pinax.blog.models import (
Section as BaseSection Section as BaseSection
) )
from pinax.images.models import ( from pinax.images.models import (
ImageSet as BaseImageSet, ImageSet as BaseImageSet)
Image as BaseImage,
image_upload_to)
from core.models import AbstractStatusModel, AbstractDateTimeModel from core.models import AbstractStatusModel, AbstractDateTimeModel

@ -1,6 +1,6 @@
from django.urls import re_path from django.urls import re_path
from . import views
from . import views
urlpatterns = [ urlpatterns = [
re_path(r"^$", views.BlogIndexView.as_view(), name="blog"), re_path(r"^$", views.BlogIndexView.as_view(), name="blog"),

@ -5,6 +5,7 @@ from pinax.blog.views import (
SecretKeyPostDetailView as BaseSecretKeyPostDetailView, SecretKeyPostDetailView as BaseSecretKeyPostDetailView,
SlugUniquePostDetailView as BaseSlugUniquePostDetailView) SlugUniquePostDetailView as BaseSlugUniquePostDetailView)
class SlugUniquePostDetailView(BaseSlugUniquePostDetailView): class SlugUniquePostDetailView(BaseSlugUniquePostDetailView):
pass pass

@ -1,9 +1,8 @@
from django.conf import settings
from django.core.paginator import Paginator from django.core.paginator import Paginator
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
# Create your views here. # Create your views here.
# from cart.models import BUYING_STATUS_PAID from cart.models import BUYING_STATUS_PAID
from core.views import ProtectedTemplateView from core.views import ProtectedTemplateView
@ -11,20 +10,20 @@ class IndexTemplateView(ProtectedTemplateView):
template_name = 'cabinet/index.html' template_name = 'cabinet/index.html'
title = _('Личный кабинет') title = _('Личный кабинет')
# def get_ref_link(self,user): def get_ref_link(self, user):
# return "{path}?ref={ref_link}".format(**{ return "{path}?ref={ref_link}".format(**{
# 'path': user.referral.url, 'path': user.referral.url,
# 'ref_link': user.referral.code 'ref_link': user.referral.code
# }) })
# def get_bought_item_list(self,user): def get_bought_item_list(self, user):
# bought_item_queryset = user.buying_set.filter(status=BUYING_STATUS_PAID).order_by('-create_at').all() bought_item_queryset = user.buying_set.filter(status=BUYING_STATUS_PAID).order_by('-create_at').all()
# paginator = Paginator( paginator = Paginator(
# object_list=bought_item_queryset, object_list=bought_item_queryset,
# per_page=5 per_page=5
# ) )
# the_page = 1 the_page = 1
# return paginator.page(the_page) return paginator.page(the_page)
def get_full_name(self, user): def get_full_name(self, user):
return '{last_name} {first_name} {patronymic}'.format(**{ return '{last_name} {first_name} {patronymic}'.format(**{
@ -33,18 +32,18 @@ class IndexTemplateView(ProtectedTemplateView):
'patronymic': user.profile.patronymic or "" 'patronymic': user.profile.patronymic or ""
}) })
# def get_user_points(self,user): def get_user_points(self, user):
# return user.referral.referralstats.points return user.referral.referralstats.points
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs) context = super().get_context_data(**kwargs)
context['title'] = self.title context['title'] = self.title
# context['ref_link'] = self.get_ref_link(self.request.user) context['ref_link'] = self.get_ref_link(self.request.user)
# context['bought_item_list'] = self.get_bought_item_list(self.request.user) context['bought_item_list'] = self.get_bought_item_list(self.request.user)
context['full_name'] = self.get_full_name(self.request.user) context['full_name'] = self.get_full_name(self.request.user)
context['email'] = self.request.user.email context['email'] = self.request.user.email
context['phone_number'] = self.request.user.profile.phone context['phone_number'] = self.request.user.profile.phone
context['company'] = self.request.user.company or None context['company'] = self.request.user.company or None
context['profile'] = self.request.user.profile or None context['profile'] = self.request.user.profile or None
# context['referral_points'] = self.get_user_points(user=self.request.user) context['referral_points'] = self.get_user_points(user=self.request.user)
return context return context

@ -1,25 +1,23 @@
import csv import csv
import datetime import datetime
import pytils
import weasyprint
from decimal import Decimal from decimal import Decimal
import pytils
import weasyprint
from django.conf import settings from django.conf import settings
from django.contrib import admin from django.contrib import admin
from django.utils.translation import ugettext_lazy as _
from django.http import HttpResponse from django.http import HttpResponse
from django.template.loader import render_to_string from django.template.loader import render_to_string
from django.utils.translation import ugettext_lazy as _
from jet.admin import CompactInline from jet.admin import CompactInline
from jet.filters import DateRangeFilter from jet.filters import DateRangeFilter
from rangefilter.filter import DateTimeRangeFilter from rangefilter.filter import DateTimeRangeFilter
from core.admin import SafeModelAdmin from core.admin import SafeModelAdmin
from core.models import Certificate
from eshop_project.settings.base import PAY_REQUISITES from eshop_project.settings.base import PAY_REQUISITES
from .models import ( from .models import (
Offer, SupplyType, Offer, SupplyType,
Currency, Buying, Buying,
SupplyTarget, SupplyTarget,
Order, Discount, Order, Discount,
Client) Client)
@ -32,6 +30,7 @@ class ProductOfferInlineAdmin(CompactInline):
show_change_link = 1 show_change_link = 1
max_num = 1 max_num = 1
# Supply admins # Supply admins
@admin.register(SupplyType) @admin.register(SupplyType)
@ -84,6 +83,7 @@ class BuyingAdmin(SafeModelAdmin):
data_row.append(value) data_row.append(value)
writer.writerow(data_row) writer.writerow(data_row)
return response return response
export_buyings_to_csv.short_description = _('экспортировать CSV') export_buyings_to_csv.short_description = _('экспортировать CSV')
def print_order_in_pdf(self, buyings): def print_order_in_pdf(self, buyings):
@ -96,7 +96,6 @@ class BuyingAdmin(SafeModelAdmin):
response = HttpResponse(content_type='application/pdf') response = HttpResponse(content_type='application/pdf')
response['Content-Disposition'] = 'filename=order_{}.pdf'.format(buyings.id) response['Content-Disposition'] = 'filename=order_{}.pdf'.format(buyings.id)
weasyprint.HTML( weasyprint.HTML(
string=rendered_html, string=rendered_html,
base_url=self.request.build_absolute_uri() base_url=self.request.build_absolute_uri()
@ -107,6 +106,7 @@ class BuyingAdmin(SafeModelAdmin):
] ]
) )
return response return response
print_order_in_pdf.short_description = _('Распечатать заказ в pdf') print_order_in_pdf.short_description = _('Распечатать заказ в pdf')
def mark_buyings_as_paid(self, request, queryset): def mark_buyings_as_paid(self, request, queryset):
@ -118,8 +118,8 @@ class BuyingAdmin(SafeModelAdmin):
parent_profile.save() parent_profile.save()
buying.status = BUYING_STATUS_PAID buying.status = BUYING_STATUS_PAID
buying.save() buying.save()
mark_buyings_as_paid.short_description = _('Отметить как оплаченные')
mark_buyings_as_paid.short_description = _('Отметить как оплаченные')
inlines = () inlines = ()
list_display = ('user', 'offer', 'status', 'amount', 'total_price') list_display = ('user', 'offer', 'status', 'amount', 'total_price')

@ -3,4 +3,3 @@ from cart.utils import Cart
def cart_basket(request): def cart_basket(request):
return {'cart': Cart(request)} return {'cart': Cart(request)}

@ -1,25 +1,22 @@
import uuid import uuid
from crispy_forms.helper import FormHelper from crispy_forms.helper import FormHelper
from crispy_forms.layout import Layout, Field, Div, HTML, Hidden, Fieldset, Submit from crispy_forms.layout import Layout, Field, Div, HTML, Submit
from django import forms from django import forms
from django.conf import settings from django.conf import settings
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.core.validators import MaxValueValidator, MinValueValidator from django.core.validators import MaxValueValidator, MinValueValidator
from django.forms import ALL_FIELDS, formset_factory
from django.urls import reverse_lazy from django.urls import reverse_lazy
from django.utils.translation import ugettext_lazy as _
from cart.models import ( from cart.models import (
Buying, BUYING_STATUS_IN_CART, Offer, SupplyType, SupplyTarget, Discount, Order Buying, Offer, SupplyType, SupplyTarget, Discount, Order
) )
from cart.tasks import send_user_order_notification, send_admin_order_notification from cart.tasks import send_user_order_notification, send_admin_order_notification
from contact_us.mixins import RequestNotifiable from contact_us.mixins import RequestNotifiable
from core.forms import QueryFormBase from core.forms import QueryFormBase
from core.models import City from core.models import City
from core.utils import parse_path from core.utils import parse_path
from django.utils.translation import ugettext_lazy as _
from products.models import Product
class CartAddInlineForm(forms.ModelForm): class CartAddInlineForm(forms.ModelForm):

@ -0,0 +1,195 @@
# Generated by Django 2.0.7 on 2018-10-25 15:27
import autoslug.fields
import cart.models
import datetime
from django.conf import settings
import django.core.validators
from django.db import migrations, models
import django.db.models.deletion
import django.db.models.manager
from django.utils.timezone import utc
class Migration(migrations.Migration):
initial = True
dependencies = [
('products', '0001_initial'),
('core', '0001_initial'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='Buying',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('create_at', models.DateTimeField(auto_now_add=True, verbose_name='создан в')),
('updated_at', models.DateTimeField(auto_now=True, verbose_name='обновлен')),
('bonus_points', models.IntegerField(validators=[django.core.validators.MinValueValidator(0)], verbose_name='бонусы')),
('status', models.SmallIntegerField(choices=[(25, 'В корзине'), (50, 'Обрабатываеться'), (75, 'Оплаченно')], default=25, verbose_name='статус')),
('amount', models.SmallIntegerField(default=0, verbose_name='колличество')),
('total_price', models.DecimalField(decimal_places=2, max_digits=10, verbose_name='цена')),
],
options={
'verbose_name': 'Покупка',
'verbose_name_plural': 'Покупки',
},
managers=[
('active', django.db.models.manager.Manager()),
],
),
migrations.CreateModel(
name='Cashback',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('create_at', models.DateTimeField(auto_now_add=True, verbose_name='создан в')),
('updated_at', models.DateTimeField(auto_now=True, verbose_name='обновлен')),
('cashback', models.DecimalField(decimal_places=2, default=0, max_digits=7, verbose_name='Сумма')),
('status', models.SmallIntegerField(choices=[(0, 'заработанный'), (100, 'потраченный')], default=0, verbose_name='статус')),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
options={
'verbose_name': 'cashback',
'verbose_name_plural': 'cashback',
},
),
migrations.CreateModel(
name='Client',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('create_at', models.DateTimeField(auto_now_add=True, verbose_name='создан в')),
('updated_at', models.DateTimeField(auto_now=True, verbose_name='обновлен')),
('status', models.SmallIntegerField(choices=[(0, 'Новый'), (25, 'Активный'), (50, 'Удаленный')], default=0, verbose_name='статус')),
('name', models.CharField(max_length=255, verbose_name='Название')),
('image', models.FileField(upload_to=cart.models.Client.upload_file_to, verbose_name='Изображение')),
('preview', models.FileField(upload_to=cart.models.Client.upload_file_to, verbose_name='Миниатюрка')),
],
options={
'verbose_name': 'Клиент',
'verbose_name_plural': 'Клиенты',
},
),
migrations.CreateModel(
name='Discount',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('create_at', models.DateTimeField(auto_now_add=True, verbose_name='создан в')),
('updated_at', models.DateTimeField(auto_now=True, verbose_name='обновлен')),
('name', models.CharField(max_length=255, verbose_name='Имя')),
('image', models.FileField(blank=True, null=True, upload_to=cart.models.Discount.upload_file_to, verbose_name='Изображение')),
('code', models.CharField(blank=True, default='f09d1c83-a6f8-40b5-a980-c7e69f7f585c', max_length=50, unique=True, verbose_name='Код')),
('valid_from', models.DateTimeField(auto_now_add=True, verbose_name='Начало')),
('valid_to', models.DateTimeField(blank=True, default=datetime.datetime(2018, 11, 1, 15, 27, 27, 195955, tzinfo=utc), verbose_name='Конец')),
('value', models.IntegerField(default=0, help_text='Указываем целым числом. Пример: 30 = 30%', validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(100)], verbose_name='Процент')),
('active', models.BooleanField(default=True, verbose_name='Активная')),
],
options={
'verbose_name': 'Дисконт',
'verbose_name_plural': 'Дисконт',
},
),
migrations.CreateModel(
name='Offer',
fields=[
('create_at', models.DateTimeField(auto_now_add=True, verbose_name='создан в')),
('updated_at', models.DateTimeField(auto_now=True, verbose_name='обновлен')),
('status', models.SmallIntegerField(choices=[(0, 'Новый'), (25, 'Активный'), (50, 'Удаленный')], default=0, verbose_name='статус')),
('product', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, primary_key=True, serialize=False, to='products.Product', verbose_name='Продукт')),
('vendor_code', models.CharField(help_text='Должен быть уникальным', max_length=255, unique=True, verbose_name='Артикул')),
('price', models.DecimalField(decimal_places=2, help_text='Цена за продукт', max_digits=10, validators=[django.core.validators.MinValueValidator(1.0)], verbose_name='цена')),
('amount', models.IntegerField(default=1, validators=[django.core.validators.MinValueValidator(1)], verbose_name='Колличество')),
('cashback', models.DecimalField(decimal_places=2, default=0, help_text='Указаная сумма будет отображаться в выбранной валюте позиции', max_digits=6, verbose_name='Кешбек')),
('note', models.TextField(blank=True, null=True, verbose_name='Пометка')),
('account_nds', models.BooleanField(default=False, verbose_name='с учетом НДС')),
('currency', models.ForeignKey(help_text='Цена по умолчанию в рублях', on_delete=django.db.models.deletion.PROTECT, to='core.Currency', verbose_name='Валюта')),
('discount', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='cart.Discount', verbose_name='Дисконт')),
],
options={
'verbose_name': 'Позиция',
'verbose_name_plural': 'Позиции',
},
),
migrations.CreateModel(
name='Order',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('create_at', models.DateTimeField(auto_now_add=True, verbose_name='создан в')),
('updated_at', models.DateTimeField(auto_now=True, verbose_name='обновлен')),
('order_code', models.CharField(max_length=255, verbose_name='код заказа')),
('customer_name', models.CharField(max_length=255, verbose_name='имя')),
('customer_email', models.EmailField(blank=True, default=None, max_length=254, null=True, verbose_name='email')),
('phone', models.CharField(max_length=12, validators=[django.core.validators.RegexValidator(message="Phone number must be entered in the format: '+99999999999'. Up to 12 digits allowed.", regex='^((\\+7)|8)?\\d{10}$')], verbose_name='телефон')),
('customer_address', models.TextField(verbose_name='адрес')),
('total_price', models.DecimalField(decimal_places=2, default=0, max_digits=10, verbose_name='стоимость')),
('comment', models.TextField(blank=True, default=None, null=True, verbose_name='комментарий')),
('status', models.SmallIntegerField(choices=[(0, 'Новый'), (50, 'Обрабатывается'), (100, 'Оплаченно')], default=0, verbose_name='статус')),
('city', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='core.City', verbose_name='Город')),
('customer_user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL, verbose_name='пользователь')),
],
options={
'verbose_name': 'Заказ',
'verbose_name_plural': 'Заказы',
'ordering': ('-create_at',),
},
),
migrations.CreateModel(
name='SupplyTarget',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('create_at', models.DateTimeField(auto_now_add=True, verbose_name='создан в')),
('updated_at', models.DateTimeField(auto_now=True, verbose_name='обновлен')),
('name', models.CharField(max_length=255, verbose_name='Назначение')),
('slug', autoslug.fields.AutoSlugField(editable=False, populate_from='name', unique=True)),
('status', models.PositiveSmallIntegerField(help_text='Необходимо указать числовой код статус', verbose_name='статус')),
],
options={
'verbose_name': 'Лицензия',
'verbose_name_plural': 'Лицензии',
},
),
migrations.CreateModel(
name='SupplyType',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('create_at', models.DateTimeField(auto_now_add=True, verbose_name='создан в')),
('updated_at', models.DateTimeField(auto_now=True, verbose_name='обновлен')),
('name', models.CharField(max_length=255, verbose_name='Тип')),
('slug', autoslug.fields.AutoSlugField(editable=False, populate_from='name', unique=True)),
('min_term', models.IntegerField(help_text='Минимальный срок поставки', verbose_name='от')),
('max_term', models.IntegerField(help_text='Максимальный срок поставки', verbose_name='до')),
('term_dimension', models.SmallIntegerField(choices=[(0, 'Час'), (0, 'День'), (0, 'Неделя'), (0, 'Месяц')], default=0, verbose_name='размерность')),
],
options={
'verbose_name': 'Тип поставки',
'verbose_name_plural': 'Тип поставки',
},
),
migrations.AddField(
model_name='offer',
name='supply_target',
field=models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.SET_NULL, to='cart.SupplyTarget', verbose_name='Лицензия'),
),
migrations.AddField(
model_name='offer',
name='supply_type',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='cart.SupplyType', verbose_name='Поставка'),
),
migrations.AddField(
model_name='buying',
name='offer',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='cart.Offer', verbose_name='позиция'),
),
migrations.AddField(
model_name='buying',
name='order',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='cart.Order', verbose_name='пользователь'),
),
migrations.AddField(
model_name='buying',
name='user',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='пользователь'),
),
]

@ -0,0 +1,25 @@
# Generated by Django 2.0.7 on 2018-10-27 16:53
import datetime
from django.db import migrations, models
from django.utils.timezone import utc
class Migration(migrations.Migration):
dependencies = [
('cart', '0001_initial'),
]
operations = [
migrations.AlterField(
model_name='discount',
name='code',
field=models.CharField(blank=True, default='866872fd-bf5a-48bc-8e22-c3581eb7a307', max_length=50, unique=True, verbose_name='Код'),
),
migrations.AlterField(
model_name='discount',
name='valid_to',
field=models.DateTimeField(blank=True, default=datetime.datetime(2018, 11, 3, 16, 53, 38, 906817, tzinfo=utc), verbose_name='Конец'),
),
]

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

Loading…
Cancel
Save