parent
0547ed23ba
commit
4f26a4b7c3
20 changed files with 1142 additions and 351 deletions
@ -1,3 +1,143 @@ |
|||||||
|
import csv |
||||||
|
import datetime |
||||||
|
import pytils |
||||||
|
import weasyprint |
||||||
|
from decimal import Decimal |
||||||
|
|
||||||
|
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.template.loader import render_to_string |
||||||
|
|
||||||
|
from jet.admin import CompactInline |
||||||
|
from jet.filters import DateRangeFilter |
||||||
|
from rangefilter.filter import DateTimeRangeFilter |
||||||
|
|
||||||
|
from core.admin import SafeModelAdmin |
||||||
|
from core.models import Certificate |
||||||
|
from eshop_project.settings.base import PAY_REQUISITES |
||||||
|
from .models import ( |
||||||
|
Offer, SupplyType, |
||||||
|
Currency, Buying, |
||||||
|
SupplyTarget, |
||||||
|
Order, Discount, |
||||||
|
Client) |
||||||
|
|
||||||
|
|
||||||
|
class ProductOfferInlineAdmin(CompactInline): |
||||||
|
model = Offer |
||||||
|
exclude = ('status',) |
||||||
|
extra = 1 |
||||||
|
show_change_link = 1 |
||||||
|
max_num = 1 |
||||||
|
|
||||||
|
# Supply admins |
||||||
|
|
||||||
|
@admin.register(SupplyType) |
||||||
|
class SupplyTypeAdmin(admin.ModelAdmin): |
||||||
|
list_display = ('name', 'min_term', 'max_term') |
||||||
|
search_fields = ('name', 'min_term', 'max_term') |
||||||
|
|
||||||
|
|
||||||
|
@admin.register(SupplyTarget) |
||||||
|
class SupplyTargetAdmin(admin.ModelAdmin): |
||||||
|
list_display = ('name', 'slug') |
||||||
|
search_fields = ('name', 'slug',) |
||||||
|
|
||||||
|
|
||||||
|
@admin.register(Discount) |
||||||
|
class DiscountAdmin(admin.ModelAdmin): |
||||||
|
list_display = ['code', 'valid_from', 'valid_to', 'value', 'active'] |
||||||
|
list_filter = ['valid_from', 'valid_to', 'active'] |
||||||
|
search_field = ['code'] |
||||||
|
|
||||||
|
|
||||||
|
# Offer admins |
||||||
|
@admin.register(Offer) |
||||||
|
class ProductOfferAdmin(SafeModelAdmin): |
||||||
|
list_display = ('product', 'price', 'amount', 'currency') |
||||||
|
search_fields = ('product__name',) |
||||||
|
list_filter = ('currency',) |
||||||
|
|
||||||
|
|
||||||
|
@admin.register(Buying) |
||||||
|
class BuyingAdmin(SafeModelAdmin): |
||||||
|
def export_buyings_to_csv(self, buying, queryset): |
||||||
|
opts = buying._meta |
||||||
|
response = HttpResponse(content_type='text/csv') |
||||||
|
response['Content-Disposition'] = 'attachment; filename=Orders-{}.csv'.format( |
||||||
|
datetime.datetime.now().strftime("%d/%m/%Y")) |
||||||
|
writer = csv.writer(response) |
||||||
|
|
||||||
|
fields = [field for field in opts.get_fields() if not field.many_to_many and not field.one_to_many] |
||||||
|
|
||||||
|
writer.writerow([field.verbose_name for field in fields]) |
||||||
|
|
||||||
|
for obj in queryset: |
||||||
|
data_row = [] |
||||||
|
for field in fields: |
||||||
|
value = getattr(obj, field.name) |
||||||
|
if isinstance(value, datetime.datetime): |
||||||
|
value = value.strftime('%d/%m/%Y') |
||||||
|
|
||||||
|
data_row.append(value) |
||||||
|
writer.writerow(data_row) |
||||||
|
return response |
||||||
|
export_buyings_to_csv.short_description = _('экспортировать CSV') |
||||||
|
|
||||||
|
def print_order_in_pdf(self,buyings): |
||||||
|
verb_price = pytils.numeral.in_words(round(buyings.total_price)) |
||||||
|
verb_cur = pytils.numeral.choose_plural(round(buyings.total_price), ("рубль", "рубля", "рублей")) |
||||||
|
html = render_to_string('bootstrap/pdf/buyings.html', { |
||||||
|
**PAY_REQUISITES, 'order': buyings, 'verb_cur': verb_cur, 'verb_price': verb_price |
||||||
|
}) |
||||||
|
rendered_html = html.encode(encoding="UTF-8") |
||||||
|
response = HttpResponse(content_type='application/pdf') |
||||||
|
response['Content-Disposition'] = 'filename=order_{}.pdf'.format(buyings.id) |
||||||
|
|
||||||
|
|
||||||
|
weasyprint.HTML( |
||||||
|
string=rendered_html, |
||||||
|
base_url=self.request.build_absolute_uri() |
||||||
|
).write_pdf( |
||||||
|
response, |
||||||
|
stylesheets = [ |
||||||
|
weasyprint.CSS(settings.STATIC_ROOT + '/css/bootstrap.min.css') |
||||||
|
] |
||||||
|
) |
||||||
|
return response |
||||||
|
print_order_in_pdf.short_description = _('Распечатать заказ в pdf') |
||||||
|
|
||||||
|
def mark_buyings_as_paid(self, request, queryset): |
||||||
|
for buying in queryset: |
||||||
|
user_profile = buying.user.profile |
||||||
|
if user_profile.parent: |
||||||
|
parent_profile = user_profile.parent.profile |
||||||
|
parent_profile.user_points += round(buying.total_price * Decimal(0.01)) |
||||||
|
parent_profile.save() |
||||||
|
buying.status = BUYING_STATUS_PAID |
||||||
|
buying.save() |
||||||
|
mark_buyings_as_paid.short_description = _('Отметить как оплаченные') |
||||||
|
|
||||||
|
|
||||||
|
inlines = () |
||||||
|
list_display = ('user', 'offer', 'status', 'amount', 'total_price') |
||||||
|
search_fields = ('user', 'offer',) |
||||||
|
list_filter = ( |
||||||
|
('create_at', DateRangeFilter), ('updated_at', DateTimeRangeFilter) |
||||||
|
) |
||||||
|
|
||||||
|
actions = (export_buyings_to_csv, print_order_in_pdf, mark_buyings_as_paid) |
||||||
|
|
||||||
|
|
||||||
|
@admin.register(Order) |
||||||
|
class OrderAdmin(SafeModelAdmin): |
||||||
|
list_display = ('order_code', 'customer_user', 'customer_name', 'customer_email','phone') |
||||||
|
|
||||||
|
|
||||||
# Register your models here. |
@admin.register(Client) |
||||||
|
class ClientAdmin(SafeModelAdmin): |
||||||
|
list_display = ('name','image','status',) |
||||||
|
search_fields = ('name',) |
||||||
|
list_filter = ('status',) |
||||||
|
|||||||
@ -1,87 +0,0 @@ |
|||||||
from decimal import Decimal |
|
||||||
from django.conf import settings |
|
||||||
from django.contrib import auth |
|
||||||
from products.models import Product |
|
||||||
# from discount.models import Discount |
|
||||||
|
|
||||||
class Cart(object): |
|
||||||
def __init__(self, request): |
|
||||||
self.session = request.session |
|
||||||
# self.discount_id = self.session.get('discount_id') |
|
||||||
if request.user.is_authenticated(): |
|
||||||
# self.points = self.session.get('points') |
|
||||||
self.points_quant = auth.get_user(request).profile.user_points |
|
||||||
cart = self.session.get(settings.CART_SESSION_ID) |
|
||||||
if not cart: |
|
||||||
request.session['points'] = False |
|
||||||
cart = self.session[settings.CART_SESSION_ID] = {} |
|
||||||
self.cart = cart |
|
||||||
|
|
||||||
def add(self, offer, price_per_itom, quantity=1, update_quantity=False): |
|
||||||
offer_slug = offer.slug |
|
||||||
if offer_slug not in self.cart: |
|
||||||
self.cart[offer_slug] = {'quantity': 0, |
|
||||||
'price': str(price_per_itom)} |
|
||||||
if update_quantity: |
|
||||||
self.cart[offer_slug]['quantity'] = int(quantity) |
|
||||||
else: |
|
||||||
self.cart[offer_slug]['quantity'] += int(quantity) |
|
||||||
self.save() |
|
||||||
|
|
||||||
def save(self): |
|
||||||
self.session[settings.CART_SESSION_ID] = self.cart |
|
||||||
self.session.modified = True |
|
||||||
|
|
||||||
def remove(self, offer_slug): |
|
||||||
# product_id = str(products.id) |
|
||||||
if offer_slug in self.cart: |
|
||||||
del self.cart[offer_slug] |
|
||||||
self.save() |
|
||||||
|
|
||||||
def __iter__(self): |
|
||||||
offers_ids = self.cart.keys() |
|
||||||
offers = Offer.objects.filter(slug__in=offers_ids) |
|
||||||
|
|
||||||
for offer in offers: |
|
||||||
self.cart[str(offer.slug)]['offer'] = offer |
|
||||||
|
|
||||||
for item in self.cart.values(): |
|
||||||
item['price'] = Decimal(item['price']) |
|
||||||
item['total_price'] = item['price'] * item['quantity'] |
|
||||||
yield item |
|
||||||
|
|
||||||
def __len__(self): |
|
||||||
return sum(item['quantity'] for item in self.cart.values()) |
|
||||||
|
|
||||||
def get_total_price(self): |
|
||||||
return sum(Decimal(item['price']) * item['quantity'] for item in self.cart.values()) |
|
||||||
|
|
||||||
def get_max(self): |
|
||||||
return min(self.points_quant, self.get_total_price() - 1) |
|
||||||
|
|
||||||
def clear(self): |
|
||||||
del self.session[settings.CART_SESSION_ID] |
|
||||||
self.session.modified = True |
|
||||||
|
|
||||||
# @property |
|
||||||
# def discount(self): |
|
||||||
# if self.discount_id: |
|
||||||
# return Discount.objects.get(id=self.discount_id) |
|
||||||
# return None |
|
||||||
|
|
||||||
# def get_discount(self): |
|
||||||
# if self.discount: |
|
||||||
# return (self.discount.discount / Decimal('100')) * self.get_total_price() |
|
||||||
# return Decimal('0') |
|
||||||
|
|
||||||
# def get_total_price_after_discount(self): |
|
||||||
# return self.get_total_price() - self.get_discount() |
|
||||||
|
|
||||||
def get_total_deduct_points(self): |
|
||||||
total_price = self.get_total_price() |
|
||||||
if total_price <= self.points_quant: |
|
||||||
# self.points_quant = self.points_quant - total_price + 1 |
|
||||||
# self.save() |
|
||||||
return 1 |
|
||||||
return total_price - self.points_quant |
|
||||||
|
|
||||||
@ -1,6 +1,6 @@ |
|||||||
from .cart import Cart |
from cart.utils import Cart |
||||||
|
|
||||||
|
|
||||||
def cart(request): |
def cart_basket(request): |
||||||
return {'cart': Cart(request)} |
return {'cart': Cart(request) } |
||||||
|
|
||||||
|
|||||||
@ -0,0 +1 @@ |
|||||||
|
[{"model": "cart.supplytarget", "pk": 1, "fields": {"create_at": "2018-08-12T20:48:30.165Z", "updated_at": "2018-08-12T20:48:30.165Z", "name": "\u0414\u043e\u043c\u0430\u0448\u043d\u044f\u044f", "slug": "domashnyaya", "status": 25}}, {"model": "cart.supplytarget", "pk": 2, "fields": {"create_at": "2018-08-12T20:48:38.954Z", "updated_at": "2018-08-12T20:49:01.074Z", "name": "\u0413\u043e\u0441\u0443\u0434\u0430\u0440\u0441\u0442\u0432\u0435\u043d\u043d\u0430\u044f", "slug": "gosudarstvennaya", "status": 75}}, {"model": "cart.supplytarget", "pk": 3, "fields": {"create_at": "2018-08-12T20:48:49.758Z", "updated_at": "2018-08-12T20:48:49.758Z", "name": "\u041a\u043e\u0440\u043f\u043e\u0440\u0430\u0442\u0438\u0432\u043d\u0430\u044f", "slug": "korporativnaya", "status": 50}}, {"model": "cart.supplytarget", "pk": 4, "fields": {"create_at": "2018-08-12T20:49:11.950Z", "updated_at": "2018-08-12T20:49:11.950Z", "name": "\u0410\u043a\u0430\u0434\u0435\u043c\u0438\u0447\u0435\u0441\u043a\u0430\u044f", "slug": "akademicheskaya", "status": 100}}] |
||||||
@ -0,0 +1 @@ |
|||||||
|
[] |
||||||
@ -1,21 +1,257 @@ |
|||||||
|
from crispy_forms.helper import FormHelper |
||||||
|
from crispy_forms.layout import Layout, Field, Div, HTML, Hidden, Fieldset, Submit |
||||||
from django import forms |
from django import forms |
||||||
|
from django.conf import settings |
||||||
|
from django.core.exceptions import ValidationError |
||||||
|
from django.core.validators import MaxValueValidator, MinValueValidator |
||||||
|
from django.forms import ALL_FIELDS, formset_factory |
||||||
|
from django.urls import reverse_lazy |
||||||
|
|
||||||
class CartAddProductForm(forms.Form): |
from cart.models import ( |
||||||
quantity = forms.CharField(widget=forms.TextInput(attrs={ |
Buying, BUYING_STATUS_IN_CART, Offer, SupplyType, SupplyTarget, Discount, Order |
||||||
'id': 'quantity', |
) |
||||||
'name': 'quantity', |
from core.forms import QueryFormBase |
||||||
'type': 'number', |
from core.utils import parse_path |
||||||
'min': '1', |
from django.utils.translation import ugettext_lazy as _ |
||||||
'max': '1000', |
|
||||||
'value': '1', |
|
||||||
'onchange': 'calculate()'})) |
|
||||||
product_slug = forms.CharField(widget=forms.TextInput(attrs={ |
|
||||||
'id': 'product_slug', |
|
||||||
'name': 'product_slug', |
|
||||||
'type': 'hidden'})) |
|
||||||
price_per_itom = forms.IntegerField(widget=forms.TextInput(attrs={ |
|
||||||
'id': 'price_per_itom', |
|
||||||
'name': 'price_per_itom', |
|
||||||
'type': 'hidden'})) |
|
||||||
update = forms.BooleanField(required=False, initial=False, widget=forms.HiddenInput) |
|
||||||
|
|
||||||
|
from products.models import Product |
||||||
|
|
||||||
|
|
||||||
|
class CartAddInlineForm(forms.ModelForm): |
||||||
|
form_action = {'viewname': 'cart:add', 'kwargs': {}} |
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs): |
||||||
|
self.helper = FormHelper() |
||||||
|
self.helper.form_method = 'post' |
||||||
|
self.helper.form_action = reverse_lazy(**self.form_action) |
||||||
|
self.helper.layout = Layout( |
||||||
|
Field('offer'), |
||||||
|
Field('amount'), |
||||||
|
Div( |
||||||
|
Submit('Купить',value='submit'), |
||||||
|
css_class='catalog__btn' |
||||||
|
) |
||||||
|
) |
||||||
|
|
||||||
|
super().__init__(*args, **kwargs) |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def clean_amount(self): |
||||||
|
amount = self.cleaned_data['amount'] |
||||||
|
offer = self.cleaned_data['offer'] |
||||||
|
if amount > offer.amount: |
||||||
|
raise ValidationError('Колличество товара указано больше доступного') |
||||||
|
elif amount <= 0: |
||||||
|
raise ValidationError('Укажите колличество товара больше 0') |
||||||
|
return amount |
||||||
|
|
||||||
|
def save(self, cart, user, commit=True): |
||||||
|
offer = Offer.active.get(self.offer) |
||||||
|
self.instance.user = user |
||||||
|
self.instance.offer = offer |
||||||
|
self.instance.amount = self.cart[offer.product.id]['quantity'] |
||||||
|
self.instance.total_price = offer.product * self.cart[offer.product.id]['quantity'] |
||||||
|
return super().save(commit) |
||||||
|
|
||||||
|
class Meta: |
||||||
|
model = Buying |
||||||
|
fields = ('offer', 'amount',) |
||||||
|
widgets = { |
||||||
|
'offer': forms.HiddenInput(), |
||||||
|
'amount': forms.HiddenInput() |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
class CartRemoveBuyingForm(forms.ModelForm): |
||||||
|
form_action = {'viewname': 'cart:remove', 'kwargs': {}} |
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs): |
||||||
|
self.helper = FormHelper() |
||||||
|
self.helper.form_method = 'post' |
||||||
|
self.helper.form_action = reverse_lazy(**self.form_action) |
||||||
|
self.helper.layout = Layout( |
||||||
|
Field('offer'), |
||||||
|
Div( |
||||||
|
Submit('Убрать'), |
||||||
|
css_class='catalog__btn' |
||||||
|
) |
||||||
|
) |
||||||
|
|
||||||
|
super().__init__(*args, **kwargs) |
||||||
|
|
||||||
|
class Meta: |
||||||
|
model = Buying |
||||||
|
fields = ('offer',) |
||||||
|
widgets = { |
||||||
|
'offer': forms.HiddenInput() |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
CartRemoveBuyingFormset = formset_factory(CartRemoveBuyingForm) |
||||||
|
|
||||||
|
|
||||||
|
class CartCheckoutForm(forms.ModelForm): |
||||||
|
def __init__(self, *args, **kwargs): |
||||||
|
self.helper = FormHelper() |
||||||
|
self.helper.form_method = 'post' |
||||||
|
self.helper.form_action = reverse_lazy(**self.form_action) |
||||||
|
self.helper.layout = Layout( |
||||||
|
Field('customer_name'), |
||||||
|
Field('customer_email'), |
||||||
|
Field('customer_user'), |
||||||
|
Field('phone'), |
||||||
|
Field('customer_address'), |
||||||
|
Field('city'), |
||||||
|
Field('buyings'), |
||||||
|
Field('comment'), |
||||||
|
Div( |
||||||
|
Submit('Подвердить'), |
||||||
|
css_class='catalog__btn' |
||||||
|
) |
||||||
|
) |
||||||
|
|
||||||
|
super().__init__(*args, **kwargs) |
||||||
|
|
||||||
|
class Model: |
||||||
|
model = Order |
||||||
|
fields = ( |
||||||
|
'customer_name', 'customer_email', 'customer_user', |
||||||
|
'phone', 'customer_address', 'city', 'buyings', |
||||||
|
'comment' |
||||||
|
) |
||||||
|
|
||||||
|
class ProductOfferPriceFilterForm(QueryFormBase): |
||||||
|
min_price = 0 |
||||||
|
max_price = 9999 |
||||||
|
price = forms.IntegerField(min_value=0, max_value=0) |
||||||
|
field_template = 'bootstrap/forms/product_filter.html' |
||||||
|
title = _('Цена') |
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs): |
||||||
|
|
||||||
|
self.helper = FormHelper() |
||||||
|
self.helper.form_method = 'get' |
||||||
|
self.helper.layout = Layout( |
||||||
|
Div(HTML(self.title), css_class='category__title left-menu__price-item'), |
||||||
|
Field('price', template=self.field_template) |
||||||
|
) |
||||||
|
|
||||||
|
super().__init__(*args, **kwargs) |
||||||
|
|
||||||
|
self.helper.form_action = reverse_lazy(**self.form_action) |
||||||
|
|
||||||
|
self.init_price_bounders() |
||||||
|
self.init_field_params() |
||||||
|
|
||||||
|
def init_price_bounders(self): |
||||||
|
if Offer.active.exists(): |
||||||
|
off_qs = Offer.active |
||||||
|
category_instance = '' |
||||||
|
if self.form_action.get('kwargs', None): |
||||||
|
category_instance = parse_path(self.form_action.get('kwargs').get('path', '')) |
||||||
|
|
||||||
|
off_qs = Offer.active.filter(product__parent__name=category_instance, |
||||||
|
product__name__icontains=self.query_params.get('name', '')) |
||||||
|
if off_qs.exists(): |
||||||
|
self.min_price = round(off_qs.order_by('price').only('price').first().price, 0) |
||||||
|
self.max_price = round(off_qs.order_by('-price').only('price').first().price, 0) |
||||||
|
|
||||||
|
def init_field_params(self): |
||||||
|
for field in self.fields: |
||||||
|
if field == 'price': |
||||||
|
self.fields[field].validators = [ |
||||||
|
MaxValueValidator(self.max_price), |
||||||
|
MinValueValidator(self.min_price) |
||||||
|
] |
||||||
|
|
||||||
|
def get_initial_for_field(self, field, field_name): |
||||||
|
return super().get_initial_for_field(field, field_name) |
||||||
|
|
||||||
|
|
||||||
|
class ProductOfferSupplyTypeFilterForm(QueryFormBase): |
||||||
|
supply_type = forms.ChoiceField() |
||||||
|
|
||||||
|
field_template = 'bootstrap/forms/product_filter.html' |
||||||
|
title = _('Тип поставки') |
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs): |
||||||
|
self.helper = FormHelper() |
||||||
|
self.helper.form_method = 'get' |
||||||
|
self.helper.layout = Layout( |
||||||
|
Div(HTML(self.title), css_class='category__title'), |
||||||
|
Field('supply_type', template=self.field_template) |
||||||
|
) |
||||||
|
super().__init__(*args, **kwargs) |
||||||
|
|
||||||
|
self.helper.form_action = reverse_lazy(**self.form_action) |
||||||
|
|
||||||
|
def get_initial_for_field(self, field, field_name): |
||||||
|
if field_name == 'supply_type': |
||||||
|
sup_typ_qs = SupplyType.objects |
||||||
|
category_instance = '' |
||||||
|
if self.form_action.get('kwargs', None): |
||||||
|
category_instance = parse_path(self.form_action.get('kwargs').get('path', '')) |
||||||
|
|
||||||
|
off_qs = Offer.active.filter(product__parent__name=category_instance, |
||||||
|
product__name__icontains=self.query_params.get('name', '')) |
||||||
|
if off_qs.count(): |
||||||
|
sup_typ_qs = sup_typ_qs.filter(offer__pk__in=off_qs.all()) |
||||||
|
|
||||||
|
return sup_typ_qs.distinct('name').only('name', 'slug') |
||||||
|
return super().get_initial_for_field(field, field_name) |
||||||
|
|
||||||
|
|
||||||
|
class ProductOfferSupplyTargetFilterForm(QueryFormBase): |
||||||
|
supply_target = forms.ChoiceField() |
||||||
|
|
||||||
|
field_template = 'bootstrap/forms/product_filter.html' |
||||||
|
title = _('Назначение') |
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs): |
||||||
|
self.helper = FormHelper() |
||||||
|
self.helper.form_method = 'get' |
||||||
|
self.helper.layout = Layout( |
||||||
|
Div(HTML(self.title), css_class='category__title'), |
||||||
|
Field('supply_target', template=self.field_template) |
||||||
|
) |
||||||
|
super().__init__(*args, **kwargs) |
||||||
|
|
||||||
|
self.helper.form_action = reverse_lazy(**self.form_action) |
||||||
|
|
||||||
|
def get_initial_for_field(self, field, field_name): |
||||||
|
if field_name == 'supply_target': |
||||||
|
sup_tar_qs = SupplyTarget.objects |
||||||
|
category_instance = '' |
||||||
|
if self.form_action.get('kwargs', None): |
||||||
|
category_instance = parse_path(self.form_action.get('kwargs').get('path', '')) |
||||||
|
|
||||||
|
off_qs = Offer.active.filter(product__parent__name=category_instance, |
||||||
|
product__name__icontains=self.query_params.get('name', '')) |
||||||
|
|
||||||
|
if off_qs.count(): |
||||||
|
sup_tar_qs = sup_tar_qs.filter(product__pk__in=off_qs.all()) |
||||||
|
|
||||||
|
return sup_tar_qs.distinct('name').only('name', 'slug') |
||||||
|
return super().get_initial_for_field(field, field_name) |
||||||
|
|
||||||
|
|
||||||
|
# @TODO: NOT IMPLEMENTED ON THE FRONT END. TEST BEFORE PRODUCTION |
||||||
|
class DiscountForm(forms.ModelForm): |
||||||
|
class Meta: |
||||||
|
model = Discount |
||||||
|
fields = ('code',) |
||||||
|
|
||||||
|
|
||||||
|
class OrderCreateForm(forms.ModelForm): |
||||||
|
customer_name = forms.CharField(max_length=100, required=True, label='Customer_name', |
||||||
|
widget=forms.TextInput(attrs={'placeholder': 'Ф.И.О.'})) |
||||||
|
customer_phone = forms.CharField(required=True, label='Customer_phone', |
||||||
|
widget=forms.TextInput(attrs={'placeholder': 'номер телефона'})) |
||||||
|
customer_email = forms.EmailField(required=True, label='Customer_email', |
||||||
|
widget=forms.TextInput(attrs={'placeholder': 'e-mail'})) |
||||||
|
city = forms.CharField(max_length=100, label='City', widget=forms.TextInput(attrs={'placeholder': 'город'})) |
||||||
|
|
||||||
|
class Meta: |
||||||
|
model = Order |
||||||
|
exclude = ('status',) |
||||||
|
|||||||
@ -0,0 +1,13 @@ |
|||||||
|
from cart.utils import Cart |
||||||
|
|
||||||
|
|
||||||
|
class CartMonkeyPatchingMiddleware: |
||||||
|
def __init__(self, get_response): |
||||||
|
self.get_response = get_response |
||||||
|
|
||||||
|
def __call__(self, request): |
||||||
|
request.cart = Cart(request) |
||||||
|
|
||||||
|
response = self.get_response(request) |
||||||
|
|
||||||
|
return response |
||||||
@ -0,0 +1,45 @@ |
|||||||
|
import celery |
||||||
|
from django.conf import settings |
||||||
|
from celery import task |
||||||
|
from django.template.loader import render_to_string |
||||||
|
from django.core.mail import send_mail, EmailMessage |
||||||
|
from io import BytesIO |
||||||
|
import weasyprint |
||||||
|
import pytils |
||||||
|
|
||||||
|
from cart.models import Order |
||||||
|
|
||||||
|
SUPPLIER_INFO = '''ООО "Русские Программы", ИНН 7713409230, КПП 771301001, |
||||||
|
127411, Москва г, Дмитровское ш., дом № 157, корпус 7, тел.: +74957258950''' |
||||||
|
|
||||||
|
requisites = {'name': 'ООО "Русские Программы"', 'bank': 'АО "СМП БАНК" Г. МОСКВА', 'INN': '7713409230', |
||||||
|
'KPP': '771301001', 'BIK': '44525503', 'bank_acc': '30101810545250000503', 'acc': '40702810300750000177', |
||||||
|
'sup_info': SUPPLIER_INFO} |
||||||
|
|
||||||
|
@celery.task |
||||||
|
def send_user_order_notification(order_id): |
||||||
|
""" |
||||||
|
Sending Email of order creating |
||||||
|
""" |
||||||
|
order = Order.objects.get(id=order_id) |
||||||
|
verb_price = pytils.numeral.in_words(round(order.total_price)) |
||||||
|
verb_cur = pytils.numeral.choose_plural(round(order.total_price), ("рубль", "рубля", "рублей")) |
||||||
|
subject = 'Заказ № {}'.format(order.id) |
||||||
|
message = 'Уважаемый, {}, номер Вашего заказа {}. \ |
||||||
|
Пожалуйста, совершите платеж по поручению в приложении к этому письму в течение 14 дней.'.format(order.customer_name, order.id) |
||||||
|
mail_send = EmailMessage(subject, message, 'admin@myshop.ru', [order.customer_email]) |
||||||
|
|
||||||
|
# html = render_to_string('orders:AdminOrderPDF', args=[order_id]) |
||||||
|
|
||||||
|
html = render_to_string('orders/pdf.html', {**requisites, 'order': order, |
||||||
|
'verb_cur': verb_cur, 'verb_price': verb_price}) |
||||||
|
rendered_html = html.encode(encoding="UTF-8") |
||||||
|
out = BytesIO() |
||||||
|
weasyprint.HTML(string=rendered_html).write_pdf(out, |
||||||
|
stylesheets=[weasyprint.CSS(settings.STATIC_ROOT + 'css/bootstrap.min.css')]) |
||||||
|
|
||||||
|
# weasyprint.HTML(string=rendered_html, base_url=request.build_absolute_uri()).write_pdf(response, |
||||||
|
# stylesheets=[weasyprint.CSS(settings.STATIC_ROOT + '/css/bootstrap.min.css')]) |
||||||
|
mail_send.attach('order_{}.pdf'.format(order_id), out.getvalue(), 'application/pdf') |
||||||
|
mail_send.send() |
||||||
|
return mail_send |
||||||
@ -0,0 +1,18 @@ |
|||||||
|
from django.template import Library |
||||||
|
|
||||||
|
register = Library() |
||||||
|
|
||||||
|
|
||||||
|
@register.filter |
||||||
|
def calculate_price(cart, offer): |
||||||
|
# @TODO: BUG!!! MAKE TYPE CASTING OF CART KEYS |
||||||
|
if offer.product_id in cart: |
||||||
|
return offer.price * cart[offer.product_id]['quantity'] |
||||||
|
return offer.price |
||||||
|
|
||||||
|
|
||||||
|
@register.filter |
||||||
|
def get_cart_offer_amount(cart, offer): |
||||||
|
if offer.product_id in cart: |
||||||
|
return cart[offer.product_id]['quantity'] if offer.product_id in cart else 0 |
||||||
|
return 0 |
||||||
@ -0,0 +1,124 @@ |
|||||||
|
from django import template |
||||||
|
from django.template import loader, Node, Variable |
||||||
|
from django.utils.encoding import smart_str, smart_bytes |
||||||
|
from django.template.defaulttags import url |
||||||
|
from django.template import VariableDoesNotExist |
||||||
|
from mptt.templatetags.mptt_tags import recursetree |
||||||
|
|
||||||
|
register = template.Library() |
||||||
|
|
||||||
|
@register.tag |
||||||
|
def breadcrumb(parser, token): |
||||||
|
""" |
||||||
|
Renders the breadcrumb. |
||||||
|
Examples: |
||||||
|
{% breadcrumb "Title of breadcrumb" url_var %} |
||||||
|
{% breadcrumb context_var url_var %} |
||||||
|
{% breadcrumb "Just the title" %} |
||||||
|
{% breadcrumb just_context_var %} |
||||||
|
|
||||||
|
Parameters: |
||||||
|
-First parameter is the title of the crumb, |
||||||
|
-Second (optional) parameter is the url variable to link to, produced by url tag, i.e.: |
||||||
|
{% url person_detail object.id as person_url %} |
||||||
|
then: |
||||||
|
{% breadcrumb person.name person_url %} |
||||||
|
|
||||||
|
@author Andriy Drozdyuk |
||||||
|
""" |
||||||
|
return BreadcrumbNode(token.split_contents()[1:]) |
||||||
|
|
||||||
|
|
||||||
|
@register.tag |
||||||
|
def breadcrumb_url(parser, token): |
||||||
|
""" |
||||||
|
Same as breadcrumb |
||||||
|
but instead of url context variable takes in all the |
||||||
|
arguments URL tag takes. |
||||||
|
{% breadcrumb "Title of breadcrumb" person_detail person.id %} |
||||||
|
{% breadcrumb person.name person_detail person.id %} |
||||||
|
""" |
||||||
|
|
||||||
|
bits = token.split_contents() |
||||||
|
if len(bits)==2: |
||||||
|
return breadcrumb(parser, token) |
||||||
|
|
||||||
|
# Extract our extra title parameter |
||||||
|
title = bits.pop(1) |
||||||
|
token.contents = ' '.join(bits) |
||||||
|
|
||||||
|
url_node = url(parser, token) |
||||||
|
|
||||||
|
return UrlBreadcrumbNode(title, url_node) |
||||||
|
|
||||||
|
@register.tag |
||||||
|
def breadcrumb_mptt_url(parser, token): |
||||||
|
return recursetree(parser, token) |
||||||
|
|
||||||
|
class BreadcrumbNode(Node): |
||||||
|
def __init__(self, vars): |
||||||
|
""" |
||||||
|
First var is title, second var is url context variable |
||||||
|
""" |
||||||
|
self.vars = map(Variable,vars) |
||||||
|
|
||||||
|
def render(self, context): |
||||||
|
title = self.vars[0].var |
||||||
|
|
||||||
|
if title.find("'")==-1 and title.find('"')==-1: |
||||||
|
try: |
||||||
|
val = self.vars[0] |
||||||
|
title = val.resolve(context) |
||||||
|
except: |
||||||
|
title = '' |
||||||
|
|
||||||
|
else: |
||||||
|
title=title.strip("'").strip('"') |
||||||
|
title=smart_bytes(title) |
||||||
|
|
||||||
|
url = None |
||||||
|
|
||||||
|
if len(self.vars)>1: |
||||||
|
val = self.vars[1] |
||||||
|
try: |
||||||
|
url = val.resolve(context) |
||||||
|
except VariableDoesNotExist: |
||||||
|
print('URL does not exist', val) |
||||||
|
url = None |
||||||
|
|
||||||
|
return create_crumb(title, url) |
||||||
|
|
||||||
|
|
||||||
|
class UrlBreadcrumbNode(Node): |
||||||
|
def __init__(self, title, url_node): |
||||||
|
self.title = Variable(title) |
||||||
|
self.url_node = url_node |
||||||
|
|
||||||
|
def render(self, context): |
||||||
|
title = self.title.var |
||||||
|
|
||||||
|
if title.find("'")==-1 and title.find('"')==-1: |
||||||
|
try: |
||||||
|
val = self.title |
||||||
|
title = val.resolve(context) |
||||||
|
except: |
||||||
|
title = '' |
||||||
|
else: |
||||||
|
title=title.strip("'").strip('"') |
||||||
|
title=smart_bytes(title) |
||||||
|
|
||||||
|
url = self.url_node.render(context) |
||||||
|
return create_crumb(title, url) |
||||||
|
|
||||||
|
|
||||||
|
def create_crumb(title, url=None): |
||||||
|
""" |
||||||
|
Helper function |
||||||
|
""" |
||||||
|
crumb = """<li><a href='%s'>%s</a></span>""" |
||||||
|
if url: |
||||||
|
crumb = crumb.format(url, title) |
||||||
|
else: |
||||||
|
crumb = crumb.format('#', title) |
||||||
|
|
||||||
|
return crumb |
||||||
@ -0,0 +1,65 @@ |
|||||||
|
from decimal import Decimal |
||||||
|
from django.conf import settings |
||||||
|
from django.contrib import auth |
||||||
|
|
||||||
|
from cart.models import Offer |
||||||
|
from products.models import Product |
||||||
|
|
||||||
|
|
||||||
|
# from discount.models import Discount |
||||||
|
|
||||||
|
class Cart(object): |
||||||
|
def __init__(self, request): |
||||||
|
self.store = request.session |
||||||
|
cart = self.store.get(settings.CART_SESSION_ID) |
||||||
|
if not cart: |
||||||
|
cart = self.store[settings.CART_SESSION_ID] = {} |
||||||
|
self.cart = cart |
||||||
|
|
||||||
|
def add(self, offer_id, quantity=1): |
||||||
|
offer_id = str(offer_id) |
||||||
|
if offer_id in self.cart: |
||||||
|
self.cart[offer_id]['quantity'] += int(quantity) |
||||||
|
else: |
||||||
|
self.cart[offer_id] = {'quantity': quantity} |
||||||
|
self.save() |
||||||
|
|
||||||
|
def save(self): |
||||||
|
self.store[settings.CART_SESSION_ID] = self.cart |
||||||
|
self.store.modified = True |
||||||
|
|
||||||
|
def remove(self, offer_id): |
||||||
|
if offer_id in self.cart: |
||||||
|
del self.cart[offer_id] |
||||||
|
self.save() |
||||||
|
|
||||||
|
def clear(self): |
||||||
|
del self.store[settings.CART_SESSION_ID] |
||||||
|
self.store.modified = True |
||||||
|
|
||||||
|
def keys(self): |
||||||
|
return self.cart.keys() |
||||||
|
|
||||||
|
def __iter__(self): |
||||||
|
return iter(self.cart.keys()) |
||||||
|
|
||||||
|
def __setitem__(self, key, value): |
||||||
|
try: |
||||||
|
self.cart[str(key)] = value |
||||||
|
except KeyError: |
||||||
|
return setattr(self, key, value) |
||||||
|
|
||||||
|
def __getitem__(self, key): |
||||||
|
try: |
||||||
|
return self.cart[str(key)] |
||||||
|
except KeyError: |
||||||
|
return getattr(self, key) |
||||||
|
|
||||||
|
def __contains__(self, item): |
||||||
|
return str(item) in self.cart |
||||||
|
|
||||||
|
def __next__(self): |
||||||
|
return self.cart.__next__() |
||||||
|
|
||||||
|
def __len__(self): |
||||||
|
return self.cart.__len__() |
||||||
@ -1,4 +0,0 @@ |
|||||||
{% extends 'base.html' %} |
|
||||||
{% block content %} |
|
||||||
Missing buying history |
|
||||||
{% endblock %} |
|
||||||
@ -0,0 +1 @@ |
|||||||
|
{% extends 'base.html' %} |
||||||
@ -0,0 +1 @@ |
|||||||
|
{% extends 'base.html' %} |
||||||
Loading…
Reference in new issue