Skip to content

Commit

Permalink
Core: add signal and notify event when order status changes
Browse files Browse the repository at this point in the history
Bump mock to latest to be able to use mock call test correctly

Refs HEAL-34
  • Loading branch information
chessbr committed Jun 12, 2019
1 parent e4bd2df commit 2dda66c
Show file tree
Hide file tree
Showing 6 changed files with 113 additions and 4 deletions.
11 changes: 10 additions & 1 deletion shuup/core/models/_orders.py
Expand Up @@ -40,7 +40,7 @@
from shuup.core.pricing import TaxfulPrice, TaxlessPrice
from shuup.core.settings_provider import ShuupSettings
from shuup.core.signals import (
payment_created, refund_created, shipment_created,
order_status_changed, payment_created, refund_created, shipment_created,
shipment_created_and_processed
)
from shuup.utils.analog import define_log_model, LogEntryKind
Expand Down Expand Up @@ -527,14 +527,23 @@ def save(self, *args, **kwargs):
"when SHUUP_ALLOW_ANONYMOUS_ORDERS is not enabled.")
self._cache_values()
first_save = (not self.pk)
old_status = self.status

if not first_save:
old_status = Order.objects.only("status").get(pk=self.pk).status

super(Order, self).save(*args, **kwargs)

if first_save: # Have to do a double save the first time around to be able to save identifiers
self._save_identifiers()
self._cache_contact_values_post_create()

for line in self.lines.exclude(product_id=None):
line.supplier.module.update_stock(line.product_id)

if self.status != old_status:
order_status_changed.send(type(self), order=self, old_status=old_status, new_status=self.status)

def delete(self, using=None):
if not self.deleted:
self.deleted = True
Expand Down
1 change: 1 addition & 0 deletions shuup/core/signals.py
Expand Up @@ -20,6 +20,7 @@
pre_clean = Signal(providing_args=["instance"], use_caching=True)
post_clean = Signal(providing_args=["instance"], use_caching=True)
context_cache_item_bumped = Signal(providing_args=["item"], use_caching=True)
order_status_changed = Signal(providing_args=["order", "old_status", "new_status"], use_caching=True)

#: Send from supplier module after the stocks updated have
#: been triggered after order, shipment and shop product change.
Expand Down
1 change: 1 addition & 0 deletions shuup/front/__init__.py
Expand Up @@ -30,6 +30,7 @@ class ShuupFrontAppConfig(AppConfig):
],
"notify_event": [
"shuup.front.notify_events:OrderReceived",
"shuup.front.notify_events:OrderStatusChanged",
"shuup.front.notify_events:ShipmentCreated",
"shuup.front.notify_events:ShipmentDeleted",
"shuup.front.notify_events:PaymentCreated",
Expand Down
50 changes: 48 additions & 2 deletions shuup/front/notify_events.py
Expand Up @@ -11,10 +11,11 @@
from shuup.core.models import PaymentStatus, ShipmentStatus, ShippingStatus
from shuup.core.order_creator.signals import order_creator_finished
from shuup.core.signals import (
payment_created, refund_created, shipment_created_and_processed,
shipment_deleted
order_status_changed, payment_created, refund_created,
shipment_created_and_processed, shipment_deleted
)
from shuup.notify.base import Event, Variable
from shuup.notify.models import Script
from shuup.notify.typology import Email, Enum, Language, Model, Phone


Expand All @@ -30,6 +31,20 @@ class OrderReceived(Event):
language = Variable(_("Language"), type=Language)


class OrderStatusChanged(Event):
identifier = "order_status_changed"
name = _("Order Status Changed")

order = Variable(_("Order"), type=Model("shuup.Order"))
customer_email = Variable(_("Customer Email"), type=Email)
customer_phone = Variable(_("Customer Phone"), type=Phone)
shop_email = Variable(_("Shop Email"), type=Email)
shop_phone = Variable(_("Shop Phone"), type=Phone)
old_status = Variable(_("Old Status"), type=Model("shuup.OrderStatus"))
new_status = Variable(_("New Status"), type=Model("shuup.OrderStatus"))
language = Variable(_("Language"), type=Language)


class ShipmentCreated(Event):
identifier = "shipment_created"
name = _("Shipment Created")
Expand Down Expand Up @@ -148,3 +163,34 @@ def send_refund_created_notification(order, refund_lines, **kwargs):
language=order.language,
payment_status=order.payment_status
).run(shop=order.shop)


@receiver(order_status_changed)
def send_order_status_changed_notification(order, old_status, new_status, **kwargs):
# no script for this event configured
enabled_scripts = Script.objects.filter(
shop=order.shop,
event_identifier=OrderStatusChanged.identifier,
enabled=True
)
if not enabled_scripts.exists():
return

params = dict(
order=order,
customer_email=order.email,
customer_phone=order.phone,
shop_email=None,
shop_phone=None,
language=order.language,
old_status=old_status,
new_status=new_status
)

if order.shop.contact_address:
params.update(dict(
shop_email=order.shop.contact_address.email,
shop_phone=order.shop.contact_address.phone
))

OrderStatusChanged(**params).run(shop=order.shop)
52 changes: 52 additions & 0 deletions shuup_tests/front/test_notify_events.py
@@ -0,0 +1,52 @@
# -*- coding: utf-8 -*-
# This file is part of Shuup.
#
# Copyright (c) 2012-2019, Shoop Commerce Ltd. All rights reserved.
#
# This source code is licensed under the OSL-3.0 license found in the
# LICENSE file in the root directory of this source tree.
from __future__ import unicode_literals

from datetime import datetime

import mock
import pytest
import pytz

from shuup.core.models import OrderStatus, OrderStatusManager
from shuup.notify.models import Script
from shuup.testing import factories
from shuup.utils.i18n import get_locally_formatted_datetime


@pytest.mark.django_db
def test_class_refunded():
shop = factories.get_default_shop()
supplier = factories.get_default_supplier()
customer = factories.create_random_person("en")
OrderStatusManager().ensure_default_statuses()

product = factories.create_product("p", shop, supplier, 1.0)
order = factories.create_order_with_product(product, supplier, 1, 1, shop=shop)

# make sure to have some script enabled
Script.objects.create(shop=shop, event_identifier="order_status_changed", name="Script", enabled=True)

def get_mocked_cls():
return mock.MagicMock(identifier="order_status_changed")

with mock.patch("shuup.front.notify_events.OrderStatusChanged", new_callable=get_mocked_cls) as mocked:
order.status = OrderStatus.objects.get_default_processing()
order.save()
mocked.assert_called()
order.refresh_from_db()
assert mocked.call_args.kwargs["order"] == order
assert mocked.call_args.kwargs["old_status"] == OrderStatus.objects.get_default_initial()
assert mocked.call_args.kwargs["new_status"] == OrderStatus.objects.get_default_processing()
assert order.status == OrderStatus.objects.get_default_processing()

# nothing changes
with mock.patch("shuup.front.notify_events.OrderStatusChanged", new_callable=get_mocked_cls) as mocked:
order.status = OrderStatus.objects.get_default_processing()
order.save()
mocked.assert_not_called()
2 changes: 1 addition & 1 deletion tox.ini
Expand Up @@ -33,7 +33,7 @@ deps =
# BEGIN testing deps
beautifulsoup4==4.5.3
html5lib==0.999999999
mock==2.0.0
mock==3.0.5
pytest-cache==1.0
pytest==3.0.6
pytest-cov==2.4.0
Expand Down

0 comments on commit 2dda66c

Please sign in to comment.