django-discounts-cart

Discounts for online store and management cart


Keywords
django, discounts, cart, commerce, e-ecommerce
License
MIT
Install
pip install django-discounts-cart==1.0.2

Documentation

django-discounts-cart

ΠŸΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅ для Django - Π£ΠΏΡ€Π°Π²Π»Π΅Π½ΠΈΠ΅ скидками ΠΈ ΠΊΠΎΡ€Π·ΠΈΠ½ΠΎΠΉ Π² ΠΈΠ½Ρ‚Π΅Ρ€Π½Π΅Ρ‚ ΠΌΠ°Π³Π°Π·ΠΈΠ½Π΅.

version 1.1.7

Django >= 1.7 ΠΈ Python 2.7

Π‘ΠΊΠΈΠ΄ΠΊΠΈ Π΄ΠΎΠ±Π°Π²Π»ΡΡŽΡ‚ΡΡ Ρ‡Π΅Ρ€Π΅Π· Π°Π΄ΠΌΠΈΠ½-панСль.

Π‘ΠΊΠΈΠ΄ΠΊΠΈ ΠΌΠΎΠΆΠ½ΠΎ Π±ΡƒΠ΄Π΅Ρ‚ Π΄ΠΎΠ±Π°Π²ΠΈΡ‚ΡŒ Π½Π° ΠΊΠ°ΠΆΠ΄ΡƒΡŽ ΠΊΠ°Ρ‚Π΅Π³ΠΎΡ€ΠΈΡŽ ΠΈ ΠΏΡ€ΠΎΠ΄ΡƒΠΊΡ‚.

Π’Π°ΠΊΠΆΠ΅ ΠΌΠΎΠΆΠ½ΠΎ Π½Π°ΡΡ‚Ρ€ΠΎΠΈΡ‚ΡŒ ΠΏΠΎΠ²Π΅Π΄Π΅Π½ΠΈΠ΅ скидок.

Установка:

1) pip install django-discounts-cart

2) Π”ΠΎΠ±Π°Π²ΡŒΡ‚Π΅ Π² INSTALLED_APPS
    'discounts_cart',

3) Π”ΠΎΠ±Π°Π²ΡŒΡ‚Π΅ Π² context_processors
    'discounts_cart.context_processors.vars_cart',

4) Π”ΠΎΠ±Π°Π²ΡŒΡ‚Π΅ discounts-cart Π² urls.py вашСго ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Π°
    url(r'^cart/', include('discounts_cart.urls', namespace='discounts_cart')),

5) Π”ΠΎΠ±Π°Π²ΡŒΡ‚Π΅ Π² ΠΏΡ€ΠΎΠ΅ΠΊΡ‚
    jquery.cookie.js --> http://plugins.jquery.com/cookie/
    cart.js          --> https://github.com/genkosta/django-discounts-cart/blob/master/cart.js
    templates/discounts_cart/in_cart_link.html --> https://github.com/genkosta/django-discounts-cart/tree/master/templates/discounts_cart

6) ΠžΠ±Π½ΠΎΠ²ΠΈΡ‚ΡŒ ΠΌΠΈΠ³Ρ€Π°Ρ†ΠΈΠΈ
    python manage.py migrate discounts_cart

Π’ ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠΈ для ΠΎΠΏΡ€Π΅Π΄Π΅Π»Π΅Π½Π½ΠΎΠ³ΠΎ Ρ‚ΠΈΠΏΠ° Ρ‚ΠΎΠ²Π°Ρ€Π°, создаСм ΠΌΠΎΠ΄Π΅Π»ΠΈ

Π’ прилоТСниях Π½Π΅ Π·Π°Π±Ρ‹Π²Π°Π΅ΠΌ Π½Π°ΡΡ‚Ρ€Π°ΠΈΠ²Π°Ρ‚ΡŒ __init__.py ΠΈ apps.py
ΠŸΡ€ΠΈΠΌΠ΅Ρ€:
    __init__.py
        default_app_config = 'products.apps.ProductsConfig'

    apps.py
        from django.apps import AppConfig

        class ProductsConfig(AppConfig):
            name = 'products'
            verbose_name = 'Products'


ΠŸΡ€ΠΈΠΌΠ΅Ρ€ настройки ΠΌΠΎΠ΄Π΅Π»Π΅ΠΉ:
( для ΠΏΡ€ΠΈΠΌΠ΅Ρ€Π°, ΠΌΠΎΠΆΠ½ΠΎ ΠΏΠΎΡΠΌΠΎΡ‚Ρ€Π΅Ρ‚ΡŒ здСсь --> https://github.com/genkosta/django-discounts-cart/tree/master/examples )

from django.db import models
from discounts_cart.models import DiscountsInCategories, DiscountsInProducts

class Brand(DiscountsInCategories):
    pass

class Category(DiscountsInCategories):
    pass

class Parameter(DiscountsInCategories):
    pass

# Product - ΠžΠ±ΡΠ·Π°Ρ‚Π΅Π»ΡŒΠ½ΠΎΠ΅ Π½Π°Π·Π²Π°Π½ΠΈΠ΅ ΠΌΠΎΠ΄Π΅Π»ΠΈ, Π²ΠΎ всСх прилоТСниях Ρ‚ΠΎΠ²Π°Ρ€ΠΎΠ²
class Product(DiscountsInProducts):
    brand = models.ForeignKey(Brand)
    categories = models.ManyToManyField(Category)
    parameters = models.ManyToManyField(Parameter)

# ΠžΠ±ΡΠ·Π°Ρ‚Π΅Π»ΡŒΠ½Ρ‹ΠΉ ΠΌΠ΅Ρ‚ΠΎΠ΄
def view_price(self):
    pattern = re.compile(r'\.[0]+$')
    return '{0} {1}'.format(pattern.sub('', str(self.price)), self.currency)
view_price.short_description = _('Price')

# ΠžΠ±ΡΠ·Π°Ρ‚Π΅Π»ΡŒΠ½Ρ‹ΠΉ ΠΌΠ΅Ρ‚ΠΎΠ΄
@property
def get_correct_price(self):
    return self.price  # Only Decimal type

# Add names of fields to calculate discounts
# ΠžΠ±ΡΠ·Π°Ρ‚Π΅Π»ΡŒΠ½Ρ‹ΠΉ ΠΌΠ΅Ρ‚ΠΎΠ΄
@staticmethod
def get_field_names():
    field_names_dect = {
        'foreign_key': ('brand',),
        'many_to_many': ('categories', 'parameters')
    }
    return field_names_dect

ДобавляСм Π² ΡˆΠ°Π±Π»ΠΎΠ½Ρ‹

base.html

<!-- Cart -->
<div id="id_cart">
  <div><strong>Cart</strong></div>
    <span>count:</span>&ensp;<span class="cart_count">{{ cart_count }}</span><br>
    <span>amount:</span>&ensp;<span class="cart_amount">{{ cart_amount }}</span><br>
    {% url 'discounts_cart:view_cart' as cart_url %}
    <a href="{% if cart_count %}{{ cart_url }}{% else %}javascript:void(0);{% endif %}"
       class="cart_view_product_list" data-cart_url="{{ cart_url }}">
        <img src="{% static 'img/cart.png' %}"><br><span>Open</span></a>
</div>

<a href="{% url 'home' %}">Home</a>&emsp;
<a href="{% url 'home' %}?sort_by_optimal_discount=1">Sort by optimal discount</a>

home.html

{% extends 'base.html' %}
{% load staticfiles discounts_cart %}


{% block content %}
  <!-- View products -->
  {% for product in products %}
    <div>
      <p>
          Product: {{ product.name }}<br>
          Discount {{ product.view_optimal_discount }}<br>
          Price: {{ product.view_price }} < > {{ product.view_optimal_price }} {{ product.currency }}
      </p>

      {% cart_add_select_product 'products' 'product' product.id 'In cart' 'From cart' flag_img=True %}
    </div>
    <br>
  {% endfor %}

  <!-- Π’ΠΎΠ·ΠΌΠΎΠΆΠ½Ρ‹Π΅ Π²Π°Ρ€ΠΈΠ°Π½Ρ‚Ρ‹ использования шаблонного Ρ‚Π΅Π³Π° cart_add_select_prod -->
  {% comment %}
      {% cart_add_select_product 'app' 'model' prod_id 'In cart' 'From cart' %}
          or
      {% cart_add_select_product 'app' 'model' prod_id 'In cart' 'From cart' flag_img=True %}
          or
      {% cart_add_select_product 'app' 'model' prod_id flag_img=True %}  <!-- only images -->
  {% endcomment %}

  <!-- README
    АргумСнты для шаблонного Ρ‚Π΅Π³Π° - cart_add_select_prod_*:
        1) app  - ( имя прилоТСния )
        2) model  - ( класс модСли )
        3) prod_id  - ( ID Ρ‚ΠΎΠ²Π°Ρ€Π° - ΠŸΡ€ΠΈΠΌΠ΅Ρ€: product.id)
        4) name_in_cart default=''  - ( НазваниС ссылок ΠΈΠ»ΠΈ ΠΊΠ½ΠΎΠΏΠΎΠΊ - ΠŸΡ€ΠΈΠΌΠ΅Ρ€: 'In cart' )
        5) name_from_cart default=''  - ( НазваниС ссылок ΠΈΠ»ΠΈ ΠΊΠ½ΠΎΠΏΠΎΠΊ - ΠŸΡ€ΠΈΠΌΠ΅Ρ€: 'From cart' )
        6) add_more_name default='Add more'  - ( НазваниС ссылок для увСличСния количСства Ρ‚ΠΎΠ³ΠΎ ΠΆΠ΅ ΠΏΡ€ΠΎΠ΄ΡƒΠΊΡ‚Π° )
        7) flag_img - default=False - ( логичСский Ρ„Π»Π°Π³, позволяСт ΠΎΡ‚ΠΎΠ±Ρ€Π°ΠΆΠ°Ρ‚ΡŒ ΠΈΠΊΠΎΠ½ΠΊΠΈ Π½Π° ссылках ΠΈΠ»ΠΈ ΠΊΠ½ΠΎΠΏΠΊΠ°Ρ…,
                                        настройка ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠΉ Ρ‡Π΅Ρ€Π΅Π· классы - cart_img_in, cart_img_from )

    Classes ( для настройки внСшнСго Π²ΠΈΠ΄Π°, ссылок ΠΈ ΠΊΠ½ΠΎΠΏΠΎΠΊ - 'In cart' ΠΈ 'From cart' ):
        1) cart_controls  - ( ΠΎΠ±Π΅Ρ€Ρ‚ΠΊΠ° <div> для ссылок ΠΈ ΠΊΠ½ΠΎΠΏΠΎΠΊ )
        2) cart_item_select_product  - ( для настройки ссылок ΠΈ ΠΊΠ½ΠΎΠΏΠΎΠΊ - 'In cart' ΠΈ 'From cart' )
        3) cart_img_in, cart_img_from  - ( для настройки ΠΈΠΊΠΎΠ½ΠΎΠΊ Π½Π° ссылках ΠΈ ΠΊΠ½ΠΎΠΏΠΊΠ°Ρ…, Ссли Π΄ΠΎΠ±Π°Π²Π»Π΅Π½ flag_img=True )

    CSS:
    <style type="text/css">
        .cart_add_more_product {  // ΠΎΠ±ΡΠ·Π°Ρ‚Π΅Π»ΡŒΠ½ΠΎ Π΄ΠΎΠ±Π°Π²ΠΈΡ‚ΡŒ Π² стили <<<
          display: none;
        }

        .cart_add_more_product_active {  // ΠΎΠ±ΡΠ·Π°Ρ‚Π΅Π»ΡŒΠ½ΠΎ Π΄ΠΎΠ±Π°Π²ΠΈΡ‚ΡŒ Π² стили <<<
          display: block;
        }

        // Π”ΠΎΠ±Π°Π²ΠΈΡ‚ΡŒ Ссли ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ΡΡ flag_img=True
        .cart_img_in, .cart_img_from {
          display: block;
          width: 40px;
          height: 40px;
          background-size: cover;
        }

        .cart_img_in {
          background: url("../img/in_cart.png") no-repeat center;  // Π΄ΠΎΠ±Π°Π²ΡŒΡ‚Π΅ свою ΠΈΠΊΠΎΠ½ΠΊΡƒ
        }

        .cart_img_from  {
          background: url("../img/from_cart.png") no-repeat center;  // Π΄ΠΎΠ±Π°Π²ΡŒΡ‚Π΅ свою ΠΈΠΊΠΎΠ½ΠΊΡƒ
        }
    </style>
  -->
{% endblock %}

cart.html

{% extends 'base.html' %}
{% load staticfiles %}


{% block content %}
  <!-- Products list -->
  {% for product in products %}
    <p>
        Product: {{ product.name }}<br>
        Count: {{ product.count }}<br>
        Price: {{ product.view_optimal_price }} {{ product.currency }}
    </p>
  {% endfor %}

  <p>_ _ _ _ _ _ _ _ _ _</p>

  <!-- Total amount -->
  <p>Π˜Ρ‚ΠΎΠ³ΠΎ</p>
  <p>Total count:&ensp;<span class="cart_count">{{ cart_count }}</span></p>
  <p>Total amount:&ensp;<span class="cart_amount">{{ cart_amount }}</span></p>
  {% if cart_count %}

  <p>_ _ _ _ _ _ _ _ _ _</p>

  <!-- Recalculate with a promo-code -->
  <P>ΠŸΠ΅Ρ€Π΅ΡΡ‡ΠΈΡ‚Π°Ρ‚ΡŒ с ΠΏΡ€ΠΎΠΌΠΎ-ΠΊΠΎΠ΄ΠΎΠΌ</P>
  <p>Total count:&ensp;<span class="cart_count_recalculation">0</span></p>
  <p>Total amount:&ensp;<span class="cart_amount_recalculation">0</span></p>
  <form id="id_cart_check_promo_code_form" action="{% url 'discounts_cart:recalculation_cart_with_promo_code' %}" method="post">
    <input id="id_cart_promo_code_text" type="text" name="promo_code" value="" placeholder="Enter promo-code">
    <button>Recalculation</button>
  </form>

  <p>_ _ _ _ _ _ _ _ _ _</p>

  <!-- Payment -->
  <p>ΠŸΠ»Π°Ρ‚Π΅ΠΆ</p>
  <form id="id_cart_payment_form" action="{% url 'payments' %}" method="post">{% csrf_token %}
    <input id="id_cart_promo_code_text" type="text" name="promo_code" value="" placeholder="Enter promo-code, if present">
    <button>Payment</button><br>
    <label for="id_cart_promo_code_text">( Если ΠΏΡ€ΠΎΠΌΠΎ-ΠΊΠΎΠ΄ Π½Π΅ ΠΏΠΎΠ΄Ρ…ΠΎΠ΄ΠΈΡ‚, ΠΎΠ½ Π±ΡƒΠ΄Π΅Ρ‚ Π½Π΅ ΡƒΡ‡Ρ‚Π΅Π½. )</label>
  </form>
  {% endif %}
{% endblock %}

Для сортировки ΠΏΠΎ ΠΎΠΏΡ‚ΠΈΠΌΠ°Π»ΡŒΠ½Ρ‹ΠΌ скидкам

ΠŸΡ€ΠΈΠΌΠ΅Ρ€:

home views.py

from products.models import Product  # ΠΈΠ»ΠΈ Phone ΠΈΠ»ΠΈ Π‘Ρ‚ΠΈΡ€Π°Π»ΡŒΠ½Ρ‹Π΅ ΠΌΠ°ΡˆΠΈΠ½Ρ‹

def home(request):

    if request.GET.get('sort_by_optimal_discount', False):
        # Π’Ρ‹Π±ΠΎΡ€ΠΊΠ° всСх Π°ΠΊΡ‚ΠΈΠ²Π½Ρ‹Ρ…, плюс сортировка ΠΏΠΎ ΠΎΠΏΡ‚ΠΈΠΌΠ°Π»ΡŒΠ½Ρ‹ΠΌ скидкам
        products = Product.products.sort_by_optimal_discount()
    else:
        # Π’Ρ‹Π±ΠΎΡ€ΠΊΠ° всСх Π°ΠΊΡ‚ΠΈΠ²Π½Ρ‹Ρ…
        products = Product.products.active()
        # ΠΈΠ»ΠΈ
        # products = Product.objects.filter(active=True)

    return render(request, 'home.html', {
        'products': products
    })

Для контроля Π½Π°Π΄ скидками, послС Π·Π°Π²Π΅Ρ€ΡˆΠ΅Π½ΠΈΡ ΠΎΠΏΠ»Π°Ρ‚Ρ‹, Π΄ΠΎΠ±Π°Π²ΠΈΡ‚ΡŒ

( Если ΠΏΡ€ΠΎΠΌΠΎ-ΠΊΠΎΠ΄Ρ‹ настроСны ΠΊΠ°ΠΊ удаляСмыС, ΠΎΠ½ΠΈ Π±ΡƒΠ΄ΡƒΡ‚ удалятся. )

ΠŸΡ€ΠΈΠΌΠ΅Ρ€:

payment views.py

from discounts_cart.utils import control_promo_codes, recalculation_payment

def payment(request):

    if request.method == 'POST':

        # Recalculation before payment ( for checking )
        result = recalculation_payment(request)
        count_products = result['count']
        amount = result['amount']

        # Control promo-codes
        promo_code = request.POST['promo_code'].strip()
        control_promo_codes(request, promo_code)

Cron

( По ТСланию, Π΄ΠΎΠ±Π°Π²ΠΈΡ‚ΡŒ ΠΊΠΎΠΌΠΌΠ°Π½Π΄Ρ‹ Π² Cron. )

# Π”Π΅Π°ΠΊΡ‚ΠΈΠ²ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Π½Π½Ρ‹Π΅ скидки
python manage.py discounts_cart deact
ΠΈΠ»ΠΈ
# Π£Π΄Π°Π»ΠΈΡ‚ΡŒ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Π½Π½Ρ‹Π΅ скидки
python manage.py discounts_cart del