Skip to content

Commit

Permalink
Order confirmation backend (#6498)
Browse files Browse the repository at this point in the history
* Introduce new order status - unconfirmed

* New site setting - automatically_confirm_all_new_orders

* Order confirm mutation

* Add todo note about site object in complete checkout

* Order settings mutation and query

* Include unconfirmed orders in populatedb script

* Order webhook - order confirmed

* Fix confirm mutation order status

* CR changes

* Fix mutation logic

* Add event for order confirmed

* Fix mutation logic

* Don't create fulfillment for unconfirmed orders

* Add orders query sort and filter by unconfirmed status (#6418)

* Randomize unconfirmed generation

* Fix new setting description

* Refactor settings mutation and query to shop module

* Move error codes and schema to proper places

* Missing migration

* Send email on order confirmation (#6443)

* Ensure order_confirmed triggers when setting is enabled and checkout completes

* Move order confirmed related things to order actions

* Move order_confirmed to order_created, ensure all actions triggers properly

* Update changelog

* Adyen modifications for order confirmation

* Capture payment on order confirm mutation

* Ensure resposne kind of capture in case of adyen_auto_capture

* Fix migrations

* Make sure order captured event is triggered properly on orderconfirm

* Don't call capture for inactive payment on order confirm

* change is_active payment check to can_capture()

Co-authored-by: Tomasz Szymański <tomasz.szymanski@stxnext.pl>
  • Loading branch information
tomaszszymanski129 and Tomasz Szymański committed Nov 30, 2020
1 parent 12007c0 commit b51ac23
Show file tree
Hide file tree
Showing 40 changed files with 1,530 additions and 44 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -13,6 +13,7 @@ All notable, unreleased changes to this project will be documented in this file.
- Drop deprecated service accounts and webhooks API - #6431 by @maarcingebala
- Add editorjs sanitizer - #6456 by @IKarbowiak
- Add generic FileUpload mutation - #6470 by @IKarbowiak
- Order confirmation backend - #6498 by @tomaszszymanski129
- Multichannel MVP: Multicurrency - #6242 by @fowczarek @d-wysocki
- Fix password reset request - #6351 by @Manfred-Madelaine-pro, Ambroise and Pierre
- Add possibility to exclude products from shipping method - #6506 by @korycins
Expand Down
100 changes: 99 additions & 1 deletion locale/en/LC_MESSAGES/django.po
Expand Up @@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: master\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-10-23 07:04+0000\n"
"POT-Creation-Date: 2020-11-10 12:31+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
Expand Down Expand Up @@ -374,6 +374,88 @@ msgctxt "Order confirmation e-mail text"
msgid "No shipping required"
msgstr ""

#: templates/templated_email/order/confirmed_order.email:6
#, python-format
msgctxt "Order confirmed e-mail subject"
msgid "Order %(order)s details"
msgstr ""

#: templates/templated_email/order/confirmed_order.email:13
msgctxt "Order confirmed e-mail text"
msgid ""
"\n"
"Your order has been confirmed by staff. Below is the list of ordered "
"products.\n"
msgstr ""

#: templates/templated_email/order/confirmed_order.email:17
#, python-format
msgctxt "Order confirmed e-mail payment details"
msgid ""
"\n"
"To see your order details please visit:\n"
"%(order_details_url)s\n"
msgstr ""

#: templates/templated_email/order/confirmed_order.email:25
msgctxt "Order confirmed e-mail table header"
msgid "Order summary"
msgstr ""

#: templates/templated_email/order/confirmed_order.email:26
msgctxt "Order confirmed e-mail table header"
msgid "Subtotal"
msgstr ""

#: templates/templated_email/order/confirmed_order.email:27
msgctxt "Order confirmed e-mail table header"
msgid "Shipping"
msgstr ""

#: templates/templated_email/order/confirmed_order.email:28
msgctxt "Order confirmed e-mail table header"
msgid "Taxes (included)"
msgstr ""

#: templates/templated_email/order/confirmed_order.email:28
msgctxt "Order confirmed e-mail table header"
msgid "Taxes"
msgstr ""

#: templates/templated_email/order/confirmed_order.email:29
msgctxt "Order confirmed e-mail table header"
msgid "Discount:"
msgstr ""

#: templates/templated_email/order/confirmed_order.email:30
msgctxt "Order confirmed e-mail table header"
msgid "Total"
msgstr ""

#: templates/templated_email/order/confirmed_order.email:32
#: templates/templated_email/source/confirmed_order.mjml:35
msgctxt "Order confirmed e-mail billing address"
msgid "Billing address"
msgstr ""

#: templates/templated_email/order/confirmed_order.email:33
#: templates/templated_email/source/confirmed_order.mjml:45
msgctxt "Order confirmed e-mail text"
msgid "No billing address"
msgstr ""

#: templates/templated_email/order/confirmed_order.email:35
#: templates/templated_email/source/confirmed_order.mjml:36
msgctxt "Order confirmed e-mail shipping address"
msgid "Shipping address"
msgstr ""

#: templates/templated_email/order/confirmed_order.email:36
#: templates/templated_email/source/confirmed_order.mjml:52
msgctxt "Order confirmed e-mail text"
msgid "No shipping required"
msgstr ""

#: templates/templated_email/order/order_cancel.email:4
#, python-format
msgctxt "Order cancel e-mail subject"
Expand Down Expand Up @@ -475,6 +557,7 @@ msgstr ""
#: templates/templated_email/source/confirm_fulfillment.mjml:13
#: templates/templated_email/source/confirm_order.mjml:14
#: templates/templated_email/source/confirm_payment.mjml:13
#: templates/templated_email/source/confirmed_order.mjml:14
#: templates/templated_email/source/email_changed_notification.mjml:13
#: templates/templated_email/source/export_failed.mjml:13
#: templates/templated_email/source/export_products_file.mjml:13
Expand Down Expand Up @@ -547,6 +630,21 @@ msgctxt "Payment confirmation e-mail text"
msgid "Thank you for your payment. Your payment was successfully processed."
msgstr ""

#: templates/templated_email/source/confirmed_order.mjml:18
#, python-format
msgctxt "Order confirmed e-mail text with order details"
msgid ""
"Your order has been confirmed by staff. To see your order details please "
"visit: <a href=\"%(order_details_url)s\">%(order_details_url)s</a>"
msgstr ""

#: templates/templated_email/source/confirmed_order.mjml:22
msgctxt "Order confirmed e-mail text"
msgid ""
"Your order has been confirmed by staff. Below is the list of ordered "
"products."
msgstr ""

#: templates/templated_email/source/email_changed_notification.mjml:16
#, python-format
msgctxt "Email change e-mail text"
Expand Down
14 changes: 13 additions & 1 deletion saleor/checkout/complete_checkout.py
Expand Up @@ -2,6 +2,7 @@
from decimal import Decimal
from typing import Iterable, List, Optional, Tuple

from django.contrib.sites.models import Site
from django.core.exceptions import ValidationError
from django.db import transaction
from django.utils.encoding import smart_text
Expand All @@ -26,6 +27,7 @@
increase_voucher_usage,
remove_voucher_usage_by_customer,
)
from ..order import OrderStatus
from ..order.actions import order_created
from ..order.emails import send_order_confirmation, send_staff_order_confirmation
from ..order.models import Order, OrderLine
Expand Down Expand Up @@ -254,8 +256,18 @@ def _create_order(*, checkout: Checkout, order_data: dict, user: User) -> Order:
total_price_left = order_data.pop("total_price_left")
order_lines = order_data.pop("lines")

# TODO: refactor to use request.site / info.context site
site_settings = Site.objects.get_current().settings
status = (
OrderStatus.UNFULFILLED
if site_settings.automatically_confirm_all_new_orders
else OrderStatus.UNCONFIRMED
)
order = Order.objects.create(
**order_data, checkout_token=checkout.token, channel=checkout.channel
**order_data,
checkout_token=checkout.token,
status=status,
channel=checkout.channel,
)
for line in order_lines:
line.order_id = order.pk
Expand Down
72 changes: 56 additions & 16 deletions saleor/checkout/tests/test_checkout_complete.py
Expand Up @@ -43,16 +43,14 @@ def test_create_order_captured_payment_creates_expected_events(
)
flush_post_commit_hooks()

# Ensure only two events were created, and retrieve them
order_events = order.events.all()

(
order_placed_event,
payment_captured_event,
order_fully_paid_event,
payment_email_sent_event,
order_confirmed_event,
order_placed_email_sent_event,
) = order_events # type: OrderEvent
) = order.events.all() # type: OrderEvent

# Ensure the correct order event was created
# is the event the expected type
Expand Down Expand Up @@ -107,6 +105,18 @@ def test_create_order_captured_payment_creates_expected_events(
"email_type": OrderEventsEmails.PAYMENT,
}

# Ensure the correct order confirmed event was created
# should be order confirmed event
assert order_confirmed_event.type == OrderEvents.CONFIRMED
# ensure the user is checkout user
assert order_confirmed_event.user == checkout_user
# ensure the order confirmed event is related to order
assert order_confirmed_event.order is order
# ensure a date was set
assert order_confirmed_event.date
# ensure the event parameters are empty
assert order_confirmed_event.parameters == {}

# Ensure the correct email sent event was created
# should be email sent event
assert order_placed_email_sent_event.type == OrderEvents.EMAIL_SENT
Expand Down Expand Up @@ -164,16 +174,14 @@ def test_create_order_captured_payment_creates_expected_events_anonymous_user(
)
flush_post_commit_hooks()

# Ensure only two events were created, and retrieve them
order_events = order.events.all()

(
order_placed_event,
payment_captured_event,
order_fully_paid_event,
payment_email_sent_event,
order_confirmed_event,
order_placed_email_sent_event,
) = order_events # type: OrderEvent
) = order.events.all() # type: OrderEvent

# Ensure the correct order event was created
# is the event the expected type
Expand Down Expand Up @@ -228,6 +236,18 @@ def test_create_order_captured_payment_creates_expected_events_anonymous_user(
"email_type": OrderEventsEmails.PAYMENT,
}

# Ensure the correct order confirmed event was created
# should be order confirmed event
assert order_confirmed_event.type == OrderEvents.CONFIRMED
# ensure the user is checkout user
assert order_confirmed_event.user == checkout_user
# ensure the order confirmed event is related to order
assert order_confirmed_event.order is order
# ensure a date was set
assert order_confirmed_event.date
# ensure the event parameters are empty
assert order_confirmed_event.parameters == {}

# Ensure the correct email sent event was created
# should be email sent event
assert order_placed_email_sent_event.type == OrderEvents.EMAIL_SENT
Expand Down Expand Up @@ -277,14 +297,12 @@ def test_create_order_preauth_payment_creates_expected_events(
)
flush_post_commit_hooks()

# Ensure only two events were created, and retrieve them
order_events = order.events.all()

(
order_placed_event,
payment_authorized_event,
order_confirmed_event,
order_placed_email_sent_event,
) = order_events # type: OrderEvent
) = order.events.all() # type: OrderEvent

# Ensure the correct order event was created
# is the event the expected type
Expand Down Expand Up @@ -312,6 +330,18 @@ def test_create_order_preauth_payment_creates_expected_events(
assert "payment_id" in payment_authorized_event.parameters.keys()
assert "payment_gateway" in payment_authorized_event.parameters.keys()

# Ensure the correct order confirmed event was created
# should be order confirmed event
assert order_confirmed_event.type == OrderEvents.CONFIRMED
# ensure the user is checkout user
assert order_confirmed_event.user == checkout_user
# ensure the order confirmed event is related to order
assert order_confirmed_event.order is order
# ensure a date was set
assert order_confirmed_event.date
# ensure the event parameters are empty
assert order_confirmed_event.parameters == {}

# Ensure the correct email sent event was created
# should be email sent event
assert order_placed_email_sent_event.type == OrderEvents.EMAIL_SENT
Expand Down Expand Up @@ -369,14 +399,12 @@ def test_create_order_preauth_payment_creates_expected_events_anonymous_user(
)
flush_post_commit_hooks()

# Ensure only two events were created, and retrieve them
order_events = order.events.all()

(
order_placed_event,
payment_captured_event,
order_confirmed_event,
order_placed_email_sent_event,
) = order_events # type: OrderEvent
) = order.events.all() # type: OrderEvent

# Ensure the correct order event was created
# is the event the expected type
Expand Down Expand Up @@ -404,6 +432,18 @@ def test_create_order_preauth_payment_creates_expected_events_anonymous_user(
assert "payment_id" in payment_captured_event.parameters.keys()
assert "payment_gateway" in payment_captured_event.parameters.keys()

# Ensure the correct order confirmed event was created
# should be order confirmed event
assert order_confirmed_event.type == OrderEvents.CONFIRMED
# ensure the user is checkout user
assert order_confirmed_event.user == checkout_user
# ensure the order confirmed event is related to order
assert order_confirmed_event.order is order
# ensure a date was set
assert order_confirmed_event.date
# ensure the event parameters are empty
assert order_confirmed_event.parameters == {}

# Ensure the correct email sent event was created
# should be email sent event
assert order_placed_email_sent_event.type == OrderEvents.EMAIL_SENT
Expand Down
11 changes: 10 additions & 1 deletion saleor/core/utils/random_data.py
Expand Up @@ -50,6 +50,7 @@
from ...discount.utils import fetch_discounts
from ...giftcard.models import GiftCard
from ...menu.models import Menu
from ...order import OrderStatus
from ...order.models import Fulfillment, Order, OrderLine
from ...order.utils import update_order_status
from ...page.models import Page, PageType
Expand Down Expand Up @@ -633,6 +634,9 @@ def create_fake_order(discounts, max_order_lines=5):
)
customer = random.choice([None, customers.first()])

# 20% chance to be unconfirmed order.
will_be_unconfirmed = random.choice([0, 0, 0, 0, 1])

if customer:
address = customer.default_shipping_address
order_data = {
Expand Down Expand Up @@ -665,6 +669,8 @@ def create_fake_order(discounts, max_order_lines=5):
"shipping_price": shipping_price,
}
)
if will_be_unconfirmed:
order_data["status"] = OrderStatus.UNCONFIRMED

order = Order.objects.create(**order_data)
lines = create_order_lines(order, discounts, random.randrange(1, max_order_lines))
Expand All @@ -676,7 +682,10 @@ def create_fake_order(discounts, max_order_lines=5):
order.save()

create_fake_payment(order=order)
create_fulfillments(order)

if not will_be_unconfirmed:
create_fulfillments(order)

return order


Expand Down

0 comments on commit b51ac23

Please sign in to comment.