Skip to content

Commit

Permalink
5830 add basic discount models and apis (#164)
Browse files Browse the repository at this point in the history
* Create discount model

* Add discount models and methods

* Add API endpoint

* Add some more helper methods

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* more updates

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Add discount form to cart template

* remove console log

* Can add discount to basket

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* fix clear button

* Remove unused discounts from basket

* format

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Clean up migrations

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Add some enhancements to UI, fix migration

* ruff

* Try fixing openapi

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* fix

* fix

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* generate open API

* Fix tests

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* fix more tests

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Fix tests

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Auto apply

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Remove prints

* Code review comments

* code review comments

* Add api tests

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* tests

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Add negative tests

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Add more tests

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Add more tests

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* ruff

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* lint

* lint

* Fix tests

* Add comments

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* lint

* lint

* fix

* update open API spec

* prevent default

* Fix exception

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
  • Loading branch information
cp-at-mit and pre-commit-ci[bot] authored Nov 4, 2024
1 parent b2137b9 commit 36b8a32
Show file tree
Hide file tree
Showing 16 changed files with 1,175 additions and 24 deletions.
50 changes: 47 additions & 3 deletions cart/templates/cart.html
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
<th scope="col">Product</th>
<th scope="col">Price</th>
<th scope="col">Quantity</th>
<th scope="col">Discount code</th>
<th scope="col">Total</th>
</tr>
</thead>
Expand All @@ -35,7 +36,8 @@
<td>{{ item.product }}</td>
<td>{{ item.product.price }}</td>
<td>{{ item.quantity }}</td>
<td>{{ item.product.price }}</td>
<td>{{ item.best_discount_for_item_from_basket.discount_code }}</td>
<td>{{ item.price }}</td>
</tr>
{% endfor %}
</tbody>
Expand Down Expand Up @@ -73,6 +75,31 @@
</div>
</form>
</div>
<div class="col-12">
<p>Add a discount code to the basket:</p>

<form method="post" id="discountform">
{% csrf_token %}
<input name="discount_code" id="discount_code"/>
<div class="form-group d-flex align-items-end mt-2">
<button type="submit" class="wl-auto btn btn-primary me-2">Add discount</button>
</div>
</form>
<table class="table">
<thead>
<tr>
<th scope="col">Discount codes</th>
</tr>
</thead>
<tbody>
{% for discount in basket.discounts.all %}
<tr>
<td>{{ discount }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>

<script type="text/javascript">
Expand Down Expand Up @@ -100,12 +127,28 @@
return false;
}

function add_discount(event) {
event.preventDefault();
var discount_code = encodeURIComponent(document.getElementById("discount_code").value);
var csrfmiddlewaretoken = encodeURIComponent(document.querySelector("input[name='csrfmiddlewaretoken']").value);
var system_slug = "{{ basket.integrated_system.slug }}";
axios.post(`/api/v0/payments/baskets/add_discount/${system_slug}/`, {discount_code: discount_code}, { headers: { "X-CSRFToken": csrfmiddlewaretoken }})
.then(function (response) {
window.location.reload();
})
.catch(function (error) {
alert(error);
});

return false;
}

function clear_cart(event) {
event.preventDefault();

var csrfmiddlewaretoken = encodeURIComponent(document.querySelector("input[name='csrfmiddlewaretoken']").value);

axios.delete(`/api/v0/payments/baskets/clear/`, { headers: { "X-CSRFToken": csrfmiddlewaretoken } })
var system_slug = "{{ basket.integrated_system.slug }}";
axios.delete(`/api/v0/payments/baskets/clear/${system_slug}/`, { headers: { "X-CSRFToken": csrfmiddlewaretoken } })
.then(function (response) {
window.location.reload();
})
Expand All @@ -117,6 +160,7 @@
}

document.getElementById("cartform").addEventListener("submit", add_to_cart);
document.getElementById("discountform").addEventListener("submit", add_discount);
document.getElementById("clear-button").addEventListener("click", clear_cart);
</script>
{% endblock body %}
41 changes: 41 additions & 0 deletions openapi/specs/v0.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,26 @@ paths:
schema:
$ref: '#/components/schemas/BasketWithProduct'
description: ''
/api/v0/payments/baskets/add_discount/{system_slug}/:
post:
operationId: payments_baskets_add_discount_create
description: Creates or updates a basket for the current user, adding the discount
if valid.
parameters:
- in: path
name: system_slug
schema:
type: string
required: true
tags:
- payments
responses:
'200':
content:
application/json:
schema:
$ref: '#/components/schemas/BasketWithProduct'
description: ''
/api/v0/payments/baskets/clear/{system_slug}/:
delete:
operationId: payments_baskets_clear_destroy
Expand Down Expand Up @@ -449,9 +469,25 @@ components:
id:
type: integer
readOnly: true
price:
type: number
format: double
description: Return the total price of the basket item with discounts.
readOnly: true
discounted_price:
type: number
format: double
description: |-
Get the price of the basket item with applicable discounts.
Returns:
Decimal: The price of the basket item reduced by an applicable discount.
readOnly: true
required:
- basket
- discounted_price
- id
- price
- product
BasketWithProduct:
type: object
Expand Down Expand Up @@ -561,8 +597,13 @@ components:
type: integer
integrated_system:
type: integer
discounts:
type: array
items:
type: integer
required:
- created_on
- discounts
- id
- integrated_system
- updated_on
Expand Down
28 changes: 28 additions & 0 deletions payments/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,3 +140,31 @@ class RefundedOrderAdmin(BaseOrderAdmin):
def get_queryset(self, request):
"""Filter only to refunded orders"""
return super().get_queryset(request).filter(state=models.Order.STATE.REFUNDED)


@admin.register(models.Discount)
class DiscountAdmin(admin.ModelAdmin):
model = models.Discount
search_fields = ["discount_type", "redemption_type", "discount_code"]
list_display = [
"id",
"discount_code",
"discount_type",
"amount",
"redemption_type",
"payment_type",
]
list_filter = ["discount_type", "redemption_type", "payment_type"]


@admin.register(models.RedeemedDiscount)
class RedeemedDiscountAdmin(admin.ModelAdmin):
model = models.RedeemedDiscount
search_fields = ["discount", "order", "user"]
list_display = [
"id",
"discount",
"order",
"user",
]
list_filter = ["discount", "order", "user"]
26 changes: 26 additions & 0 deletions payments/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from django.conf import settings
from django.core.exceptions import ObjectDoesNotExist, PermissionDenied, ValidationError
from django.db import transaction
from django.db.models import Q, QuerySet
from django.urls import reverse
from ipware import get_client_ip
from mitol.payment_gateway.api import CartItem as GatewayCartItem
Expand All @@ -14,6 +15,7 @@
from payments.exceptions import PaymentGatewayError, PaypalRefundError
from payments.models import (
Basket,
Discount,
FulfilledOrder,
Order,
PendingOrder,
Expand Down Expand Up @@ -436,3 +438,27 @@ def process_post_sale_webhooks(order_id, source):
continue

send_post_sale_webhook.delay(system.id, order.id, source)


def get_auto_apply_discounts_for_basket(basket_id: int) -> QuerySet[Discount]:
"""
Get the auto-apply discounts that can be applied to a basket.
Args:
basket_id (int): The ID of the basket to get the auto-apply discounts for.
Returns:
QuerySet: The auto-apply discounts that can be applied to the basket.
"""
basket = Basket.objects.get(pk=basket_id)
return (
Discount.objects.filter(
Q(product__in=basket.get_products()) | Q(product__isnull=True)
)
.filter(
Q(integrated_system=basket.integrated_system)
| Q(integrated_system__isnull=True)
)
.filter(Q(assigned_users=basket.user) | Q(assigned_users__isnull=True))
.filter(automatic=True)
)
Loading

0 comments on commit 36b8a32

Please sign in to comment.