parent
66b134174a
commit
1927e0047b
31 changed files with 810 additions and 100 deletions
@ -1,3 +1,69 @@ |
|||||||
from django.contrib import admin |
from django.contrib import admin |
||||||
|
from django import forms |
||||||
|
from django.contrib import admin |
||||||
|
from django.contrib.auth.models import Group |
||||||
|
from django.contrib.auth.admin import UserAdmin |
||||||
|
from django.contrib.auth.forms import ReadOnlyPasswordHashField |
||||||
|
|
||||||
|
from .models import Profile |
||||||
|
|
||||||
|
|
||||||
|
class ProfileCreationForm(forms.ModelForm): |
||||||
|
password1 = forms.CharField(label='Пароль', widget=forms.PasswordInput) |
||||||
|
password2 = forms.CharField(label='Подтверждение', widget=forms.PasswordInput) |
||||||
|
|
||||||
|
class Meta: |
||||||
|
model = Profile |
||||||
|
fields = ('phone', 'email') |
||||||
|
|
||||||
|
def clean_password2(self): |
||||||
|
password1 = self.cleaned_data.get("password1") |
||||||
|
password2 = self.cleaned_data.get("password2") |
||||||
|
if password1 and password2 and password1 != password2: |
||||||
|
raise forms.ValidationError("Пароли не совпадают") |
||||||
|
return password2 |
||||||
|
|
||||||
|
def save(self, commit=True): |
||||||
|
user = super(ProfileCreationForm, self).save(commit=False) |
||||||
|
user.set_password(self.cleaned_data["password1"]) |
||||||
|
if commit: |
||||||
|
user.save() |
||||||
|
return user |
||||||
|
|
||||||
|
|
||||||
|
class ProfileChangeForm(forms.ModelForm): |
||||||
|
password = ReadOnlyPasswordHashField() |
||||||
|
|
||||||
|
class Meta: |
||||||
|
model = Profile |
||||||
|
fields = ('phone', 'email', 'password', 'is_active', 'is_superuser') |
||||||
|
|
||||||
|
def clean_password(self): |
||||||
|
return self.initial["password"] |
||||||
|
|
||||||
|
|
||||||
|
class ProfileAdmin(UserAdmin): |
||||||
|
form = ProfileChangeForm |
||||||
|
add_form = ProfileCreationForm |
||||||
|
|
||||||
|
list_display = ('phone', 'first_name', 'last_name', 'email', 'is_superuser', 'date_joined') |
||||||
|
list_filter = ('is_superuser',) |
||||||
|
fieldsets = ( |
||||||
|
(None, {'fields': ( |
||||||
|
'phone', 'email', 'password', 'first_name', 'last_name' |
||||||
|
)}), |
||||||
|
('Инфо', {'fields': ('date_joined',)}), |
||||||
|
('Доступ', {'fields': ('is_superuser', 'is_active')}), |
||||||
|
) |
||||||
|
add_fieldsets = ( |
||||||
|
(None, { |
||||||
|
'classes': ('wide',), |
||||||
|
'fields': ('phone', 'email', 'first_name', 'last_name', 'password1', 'password2')} |
||||||
|
), |
||||||
|
) |
||||||
|
search_fields = ('phone', 'email',) |
||||||
|
ordering = ('phone', 'email',) |
||||||
|
filter_horizontal = () |
||||||
|
|
||||||
# Register your models here. |
admin.site.register(Profile, ProfileAdmin) |
||||||
|
admin.site.unregister(Group) |
||||||
|
|||||||
@ -0,0 +1,4 @@ |
|||||||
|
from django import forms |
||||||
|
|
||||||
|
class LoginForm(forms.Form): |
||||||
|
phone = forms.CharField(label='Телефон', max_length=15) |
||||||
@ -0,0 +1,33 @@ |
|||||||
|
# -*- coding: utf-8 -*- |
||||||
|
from __future__ import unicode_literals |
||||||
|
|
||||||
|
from django.db import models, migrations |
||||||
|
import datetime |
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration): |
||||||
|
|
||||||
|
dependencies = [ |
||||||
|
] |
||||||
|
|
||||||
|
operations = [ |
||||||
|
migrations.CreateModel( |
||||||
|
name='Profile', |
||||||
|
fields=[ |
||||||
|
('id', models.AutoField(verbose_name='ID', serialize=False, primary_key=True, auto_created=True)), |
||||||
|
('password', models.CharField(verbose_name='password', max_length=128)), |
||||||
|
('last_login', models.DateTimeField(verbose_name='last login', blank=True, null=True)), |
||||||
|
('phone', models.CharField(verbose_name='Телефон', db_index=True, max_length=15, unique=True)), |
||||||
|
('email', models.EmailField(verbose_name='Email', db_index=True, default=None, max_length=254, unique=True)), |
||||||
|
('first_name', models.CharField(verbose_name='first name', blank=True, max_length=30)), |
||||||
|
('last_name', models.CharField(verbose_name='last name', blank=True, max_length=30)), |
||||||
|
('date_joined', models.DateTimeField(verbose_name='Регистрация', default=datetime.datetime.now)), |
||||||
|
('is_superuser', models.BooleanField(verbose_name='Админ', default=False)), |
||||||
|
('is_active', models.BooleanField(verbose_name='Активный', db_index=True, default=True)), |
||||||
|
], |
||||||
|
options={ |
||||||
|
'verbose_name': 'пользователь', |
||||||
|
'verbose_name_plural': 'пользователи', |
||||||
|
}, |
||||||
|
), |
||||||
|
] |
||||||
@ -0,0 +1,24 @@ |
|||||||
|
# -*- coding: utf-8 -*- |
||||||
|
from __future__ import unicode_literals |
||||||
|
|
||||||
|
from django.db import models, migrations |
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration): |
||||||
|
|
||||||
|
dependencies = [ |
||||||
|
('accounts', '0001_initial'), |
||||||
|
] |
||||||
|
|
||||||
|
operations = [ |
||||||
|
migrations.AlterField( |
||||||
|
model_name='profile', |
||||||
|
name='first_name', |
||||||
|
field=models.CharField(max_length=30, blank=True, verbose_name='Имя'), |
||||||
|
), |
||||||
|
migrations.AlterField( |
||||||
|
model_name='profile', |
||||||
|
name='last_name', |
||||||
|
field=models.CharField(max_length=30, blank=True, verbose_name='Фамилия'), |
||||||
|
), |
||||||
|
] |
||||||
@ -1,3 +1,68 @@ |
|||||||
|
from django.contrib.auth.models import BaseUserManager, AbstractBaseUser, AbstractUser, User |
||||||
|
from django.core.mail import send_mail |
||||||
from django.db import models |
from django.db import models |
||||||
|
from datetime import datetime |
||||||
|
|
||||||
# Create your models here. |
class ProfileManager(BaseUserManager): |
||||||
|
def _create_user(self, phone, email, password, |
||||||
|
is_superuser, **extra_fields): |
||||||
|
now = datetime.now() |
||||||
|
if not phone: |
||||||
|
raise ValueError('Телефон не может быть пустым') |
||||||
|
email = self.normalize_email(email) |
||||||
|
user = self.model(phone=phone, email=email, |
||||||
|
is_active=True, |
||||||
|
is_superuser=is_superuser, last_login=now, |
||||||
|
date_joined=now, **extra_fields) |
||||||
|
user.set_password(password) |
||||||
|
user.save() |
||||||
|
return user |
||||||
|
|
||||||
|
def create_user(self, phone, email=None, password=None, **extra_fields): |
||||||
|
return self._create_user(phone, email, password, False, |
||||||
|
**extra_fields) |
||||||
|
|
||||||
|
def create_superuser(self, phone, email, password, **extra_fields): |
||||||
|
return self._create_user(phone, email, password, True, |
||||||
|
**extra_fields) |
||||||
|
|
||||||
|
|
||||||
|
class Profile(AbstractBaseUser): |
||||||
|
phone = models.CharField('Телефон', max_length=15, unique=True, db_index=True) |
||||||
|
email = models.EmailField('Email', blank=False, null=False, default=None, unique=True, db_index=True) |
||||||
|
first_name = models.CharField('Имя', max_length=30, blank=True) |
||||||
|
last_name = models.CharField('Фамилия', max_length=30, blank=True) |
||||||
|
date_joined = models.DateTimeField('Регистрация', default=datetime.now) |
||||||
|
is_superuser = models.BooleanField('Админ', default=False) |
||||||
|
is_active = models.BooleanField('Активный', default=True, db_index=True) |
||||||
|
|
||||||
|
USERNAME_FIELD = 'phone' |
||||||
|
REQUIRED_FIELDS = ['email'] |
||||||
|
|
||||||
|
objects = ProfileManager() |
||||||
|
|
||||||
|
class Meta: |
||||||
|
verbose_name = 'пользователь' |
||||||
|
verbose_name_plural = 'пользователи' |
||||||
|
|
||||||
|
def __str__(self): |
||||||
|
return self.phone |
||||||
|
|
||||||
|
def get_full_name(self): |
||||||
|
return '{} {}'.format(self.first_name, self.last_name) |
||||||
|
|
||||||
|
def get_short_name(self): |
||||||
|
return self.first_name |
||||||
|
|
||||||
|
def has_perm(self, perm, obj=None): |
||||||
|
return True |
||||||
|
|
||||||
|
def has_module_perms(self, app_label): |
||||||
|
return True |
||||||
|
|
||||||
|
@property |
||||||
|
def is_staff(self): |
||||||
|
return self.is_superuser |
||||||
|
|
||||||
|
def email_user(self, subject, message, from_email=None, **kwargs): |
||||||
|
send_mail(subject, message, from_email, [self.email], **kwargs) |
||||||
@ -0,0 +1,10 @@ |
|||||||
|
from django.conf.urls import patterns, url |
||||||
|
from django.views.generic import RedirectView |
||||||
|
from .views import * |
||||||
|
|
||||||
|
urlpatterns = patterns('', |
||||||
|
url(r'^$', RedirectView.as_view( |
||||||
|
url='/', permanent=True), name='store_index'), |
||||||
|
url(r'^login/$', LoginView.as_view(), |
||||||
|
name='store_cart_detail'), |
||||||
|
) |
||||||
@ -1,3 +1,7 @@ |
|||||||
from django.shortcuts import render |
from django.shortcuts import render |
||||||
|
from django.views.generic import FormView |
||||||
|
from .forms import * |
||||||
|
|
||||||
# Create your views here. |
class LoginView(FormView): |
||||||
|
form_class = LoginForm |
||||||
|
template_name = 'accounts/login.jinja' |
||||||
@ -0,0 +1,4 @@ |
|||||||
|
{% extends 'base.jinja' %} |
||||||
|
{% block content %} |
||||||
|
{{ form }} |
||||||
|
{% endblock %} |
||||||
@ -0,0 +1,82 @@ |
|||||||
|
{% macro render_field_checkbox(form, field, classes) -%} |
||||||
|
<div class="{{ classes.single_value }}"> |
||||||
|
<div class="checkbox"> |
||||||
|
{% if field.auto_id %} |
||||||
|
<label {% if field.field.required and form.required_css_class %}class="{{ form.required_css_class }}"{% endif %}> |
||||||
|
{{ field|safe }} <span>{{ field.label }}</span> |
||||||
|
</label> |
||||||
|
{% endif %} |
||||||
|
{% for error in field.errors %} |
||||||
|
<span class="help-block {{ form.error_css_class }}">{{ error }}</span> |
||||||
|
{% endfor %} |
||||||
|
|
||||||
|
{% if field.help_text %} |
||||||
|
<p class="help-block"> |
||||||
|
{{ field.help_text|safe }} |
||||||
|
</p> |
||||||
|
{% endif %} |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
{%- endmacro %} |
||||||
|
|
||||||
|
{% macro render_field_radio(form, field, classes) -%} |
||||||
|
{% if field.auto_id %} |
||||||
|
<label class="control-label {{ classes.label }} {% if field.field.required %}{{ form.required_css_class }}{% endif %}">{{ field.label }}</label> |
||||||
|
{% endif %} |
||||||
|
<div class="{{ classes.value }}"> |
||||||
|
{% for choice in field %} |
||||||
|
<div class="radio"> |
||||||
|
<label> |
||||||
|
{{ choice.tag()|safe }} |
||||||
|
{{ choice.choice_label }} |
||||||
|
</label> |
||||||
|
</div> |
||||||
|
{% endfor %} |
||||||
|
|
||||||
|
{% for error in field.errors %} |
||||||
|
<span class="help-block {{ form.error_css_class }}">{{ error }}</span> |
||||||
|
{% endfor %} |
||||||
|
|
||||||
|
{% if field.help_text %} |
||||||
|
<p class="help-block"> |
||||||
|
{{ field.help_text|safe }} |
||||||
|
</p> |
||||||
|
{% endif %} |
||||||
|
</div> |
||||||
|
{%- endmacro %} |
||||||
|
|
||||||
|
{% macro render_field_standard(form, field, classes) -%} |
||||||
|
{% if field.auto_id %} |
||||||
|
<label class="control-label {{ classes.label }} {% if field.field.required %}{{ form.required_css_class }}{% endif %}" for="{{ field.auto_id }}">{{ field.label }}</label> |
||||||
|
{% endif %} |
||||||
|
|
||||||
|
<div class="{{ classes.value }} {% if field|is_multiple_checkbox %}multiple-checkbox{% endif %}"> |
||||||
|
{{ field|safe }} |
||||||
|
|
||||||
|
{% for error in field.errors %} |
||||||
|
<span class="help-block {{ form.error_css_class }}">{{ error }}</span> |
||||||
|
{% endfor %} |
||||||
|
|
||||||
|
{% if field.help_text %} |
||||||
|
<p class="help-block"> |
||||||
|
{{ field.help_text|safe }} |
||||||
|
</p> |
||||||
|
{% endif %} |
||||||
|
</div> |
||||||
|
{%- endmacro %} |
||||||
|
|
||||||
|
<div class="form-group{% if field.errors %} has-error{% endif %}"> |
||||||
|
{% if field|is_checkbox %} |
||||||
|
|
||||||
|
{{ render_field_checkbox(form, field, classes) }} |
||||||
|
|
||||||
|
{% elif field|is_radio %} |
||||||
|
|
||||||
|
{{ render_field_radio(form, field, classes) }} |
||||||
|
|
||||||
|
{% else %} |
||||||
|
|
||||||
|
{{ render_field_standard(form, field, classes) }} |
||||||
|
|
||||||
|
{% endif %} |
||||||
|
</div> |
||||||
@ -0,0 +1,16 @@ |
|||||||
|
{% if form.non_field_errors() %} |
||||||
|
<div class="alert alert-danger"> |
||||||
|
<a class="close" data-dismiss="alert">×</a> |
||||||
|
{% for non_field_error in form.non_field_errors() %} |
||||||
|
{{ non_field_error }} |
||||||
|
{% endfor %} |
||||||
|
</div> |
||||||
|
{% endif %} |
||||||
|
|
||||||
|
{% for field in form.hidden_fields() %} |
||||||
|
{{ field|safe }} |
||||||
|
{% endfor %} |
||||||
|
|
||||||
|
{% for field in form.visible_fields() %} |
||||||
|
{% include 'bootstrapform/field.jinja' %} |
||||||
|
{% endfor %} |
||||||
@ -0,0 +1,13 @@ |
|||||||
|
{{ formset.management_form }} |
||||||
|
|
||||||
|
{% for form in formset %} |
||||||
|
|
||||||
|
{% if classes.label == 'sr-only' %} |
||||||
|
<div class="form-inline"> |
||||||
|
{% endif %} |
||||||
|
{% include 'bootstrapform/form.jinja' %} |
||||||
|
{% if classes.label == 'sr-only' %} |
||||||
|
</div> |
||||||
|
{% endif %} |
||||||
|
|
||||||
|
{% endfor %} |
||||||
@ -0,0 +1,12 @@ |
|||||||
|
{% extends 'base.jinja' %} |
||||||
|
{% block content %} |
||||||
|
|
||||||
|
<div class="alert alert-success alert-dismissable"> |
||||||
|
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button> |
||||||
|
<h4>Спасибо!</h4> |
||||||
|
<p>Ваш заказ успешно принят и поступит в обработку сразу после оплаты.</p> |
||||||
|
<p><a class="btn btn-success" href="/order/{{ order.pk }}/print/">Распечатать квитанцию для оплаты</a></p> |
||||||
|
</div> |
||||||
|
|
||||||
|
{% endblock %} |
||||||
|
|
||||||
@ -0,0 +1,36 @@ |
|||||||
|
<table> |
||||||
|
<tr> |
||||||
|
<td colspan="2" style="border-bottom: 1px black solid">{{ date.strftime('%d.%m.%Y') }}</td> |
||||||
|
<td colspan="2" style="border-bottom: 1px black solid"> </td> |
||||||
|
<td></td> |
||||||
|
<td></td> |
||||||
|
<td></td> |
||||||
|
<td style="border: 1px black solid">{{ order.pk }}</td> |
||||||
|
</tr> |
||||||
|
<tr> |
||||||
|
<td>Поступ. в банк плат.</td> |
||||||
|
<td>Списано со сч. плат.</td> |
||||||
|
<td></td> |
||||||
|
<td></td> |
||||||
|
<td></td> |
||||||
|
<td></td> |
||||||
|
<td></td> |
||||||
|
<td></td> |
||||||
|
</tr> |
||||||
|
<tr> |
||||||
|
<td colspan="3">ПЛАТЕЖНОЕ ПОРУЧЕНИЕ №2015</td> |
||||||
|
<td colspan="2" style="border-bottom: 1px black solid"></td> |
||||||
|
<td colspan="2" style="border-bottom: 1px black solid"></td> |
||||||
|
<td style="border: 1px black solid">08</td> |
||||||
|
</tr> |
||||||
|
<tr> |
||||||
|
<td style="border-right: 1px black solid; border-bottom: 1px black solid; ">Сумма <br/>прописью</td> |
||||||
|
<td colspan="3" style="border-bottom: 1px black solid">Двести тысяч рублей 00 копеек</td> |
||||||
|
<td colspan="4" style="border-bottom: 1px black solid"></td> |
||||||
|
</tr> |
||||||
|
<tr> |
||||||
|
<td colspan="2" style="border-right: 1px black solid; border-bottom: 1px black solid; ">ИИН 7708654321</td> |
||||||
|
<td colspan="2" style="border-right: 1px black solid; border-bottom: 1px black solid; ">КПП 770801001</td> |
||||||
|
<td colspan="4" style="border-bottom: 1px black solid"></td> |
||||||
|
</tr> |
||||||
|
</table> |
||||||
@ -0,0 +1,69 @@ |
|||||||
|
import requests |
||||||
|
|
||||||
|
|
||||||
|
class AlemTat(object): |
||||||
|
COUNTRY_CODE = '0001' |
||||||
|
CITY_CODE = '000003' |
||||||
|
API_KEY = '677a8773-c647-4b8f-8968-32a67d55e0d3' |
||||||
|
CONTRACT = '05828/ИМ' |
||||||
|
|
||||||
|
def _build_url(self, url): |
||||||
|
# url = url.replace('\{ext\}', API_KEY) |
||||||
|
retval = url |
||||||
|
if '?' in url: |
||||||
|
retval = url + '&ApiKey={}'.format(self.API_KEY) |
||||||
|
else: |
||||||
|
retval = url + '?ApiKey={}'.format(self.API_KEY) |
||||||
|
return retval |
||||||
|
|
||||||
|
def get_cities(self): |
||||||
|
url = self._build_url( |
||||||
|
'http://api.alemtat.kz/web/{ext}/Catalog/getCitiesByCountry?CountryLocalCode={}'.format(self.COUNTRY_CODE, ext=self.API_KEY)) |
||||||
|
r = requests.get(url) |
||||||
|
return r.json() |
||||||
|
|
||||||
|
def get_cities_tuple(self): |
||||||
|
retval = map(lambda cities: (cities['LocalCode'], '{} - {}'.format(cities['LocalityName'].title(), cities[ |
||||||
|
'Region'].capitalize())), self.get_cities()) |
||||||
|
return tuple(retval) |
||||||
|
|
||||||
|
def get_services(self): |
||||||
|
url = self._build_url( |
||||||
|
'http://api.alemtat.kz/web/{ext}/Catalog/getServices'.format(ext=self.API_KEY)) |
||||||
|
r = requests.get(url) |
||||||
|
return r.json() |
||||||
|
|
||||||
|
def get_services_tuple(self): |
||||||
|
retval = map(lambda services: (services['LocalCode'], services['Name'],), self.get_services()) |
||||||
|
return tuple(retval) |
||||||
|
|
||||||
|
# def get_services_tuple(self): |
||||||
|
# retval = map(lambda cities: (cities['LocalCode'], '{} - {}'.format(cities['LocalityName'].title(), cities[ |
||||||
|
# 'Region'].capitalize())), self.get_cities()) |
||||||
|
# return tuple(retval) |
||||||
|
|
||||||
|
def get_amount(self, to, places, weight, service): |
||||||
|
url = self._build_url( |
||||||
|
'http://api.alemtat.kz/web/{ext}/Calc/getAmount'.format(ext=self.API_KEY)) |
||||||
|
post_data = dict( |
||||||
|
FromCountryCode=self.COUNTRY_CODE, |
||||||
|
FromLocalCode=self.CITY_CODE, |
||||||
|
ToCountryCode=self.COUNTRY_CODE, |
||||||
|
ToLocalCode=to, |
||||||
|
ServiceLocalCode=service, |
||||||
|
Places=places, |
||||||
|
Weight=weight, |
||||||
|
Contract=self.CONTRACT, |
||||||
|
) |
||||||
|
r = requests.post(url, data=post_data) |
||||||
|
return r.json() |
||||||
|
|
||||||
|
|
||||||
|
def alemtat_get_cities_tuple(): |
||||||
|
a = AlemTat() |
||||||
|
return a.get_cities_tuple() |
||||||
|
|
||||||
|
def alemtat_get_services_tuple(): |
||||||
|
a = AlemTat() |
||||||
|
return a.get_services_tuple() |
||||||
|
|
||||||
@ -0,0 +1,9 @@ |
|||||||
|
from django import forms |
||||||
|
from store.alemtat import alemtat_get_services_tuple |
||||||
|
from store.models import OrderData |
||||||
|
|
||||||
|
class OrderForm(forms.ModelForm): |
||||||
|
class Meta: |
||||||
|
model = OrderData |
||||||
|
fields = ['first_name', 'last_name', 'phone', 'email', 'city', 'address', 'deliv_type'] |
||||||
|
|
||||||
File diff suppressed because one or more lines are too long
@ -0,0 +1,24 @@ |
|||||||
|
# -*- coding: utf-8 -*- |
||||||
|
from __future__ import unicode_literals |
||||||
|
|
||||||
|
from django.db import models, migrations |
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration): |
||||||
|
|
||||||
|
dependencies = [ |
||||||
|
('store', '0013_auto_20150611_2306'), |
||||||
|
] |
||||||
|
|
||||||
|
operations = [ |
||||||
|
migrations.AddField( |
||||||
|
model_name='orderdata', |
||||||
|
name='deliv_type', |
||||||
|
field=models.CharField(verbose_name='Способ доставки', choices=[('E', 'Экспресс-отправления по РК'), ('T', 'Не срочные отправления по РК'), ('A', 'Автоконсолидация по РК'), ('C', 'Доставка по городу')], max_length=2, default=''), |
||||||
|
), |
||||||
|
migrations.AlterField( |
||||||
|
model_name='orderdata', |
||||||
|
name='address', |
||||||
|
field=models.CharField(verbose_name='Адрес', max_length=100), |
||||||
|
), |
||||||
|
] |
||||||
@ -0,0 +1,19 @@ |
|||||||
|
# -*- coding: utf-8 -*- |
||||||
|
from __future__ import unicode_literals |
||||||
|
|
||||||
|
from django.db import models, migrations |
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration): |
||||||
|
|
||||||
|
dependencies = [ |
||||||
|
('store', '0014_auto_20150612_0050'), |
||||||
|
] |
||||||
|
|
||||||
|
operations = [ |
||||||
|
migrations.AddField( |
||||||
|
model_name='orderdata', |
||||||
|
name='items', |
||||||
|
field=models.CharField(default='', verbose_name='Товары', max_length=256), |
||||||
|
), |
||||||
|
] |
||||||
Loading…
Reference in new issue