Fix #339 -- Allow to split orders (#341)

* Fix #339 -- Allow to split orders

* Add tests for split orders

* Add notificatiosn to both users

* Improve logdisplay
This commit is contained in:
Raphael Michel
2017-10-30 23:15:53 +01:00
committed by GitHub
parent 429f30fca7
commit 71a4664d1f
7 changed files with 461 additions and 29 deletions

View File

@@ -1,3 +1,4 @@
import copy
import json import json
import logging import logging
from collections import Counter, namedtuple from collections import Counter, namedtuple
@@ -23,7 +24,10 @@ from pretix.base.models import (
User, Voucher, User, Voucher,
) )
from pretix.base.models.event import SubEvent from pretix.base.models.event import SubEvent
from pretix.base.models.orders import CachedTicket, InvoiceAddress, OrderFee from pretix.base.models.orders import (
CachedTicket, InvoiceAddress, OrderFee, generate_position_secret,
generate_secret,
)
from pretix.base.models.organizer import TeamAPIToken from pretix.base.models.organizer import TeamAPIToken
from pretix.base.models.tax import TaxedPrice from pretix.base.models.tax import TaxedPrice
from pretix.base.payment import BasePaymentProvider from pretix.base.payment import BasePaymentProvider
@@ -657,10 +661,12 @@ class OrderChangeManager:
PriceOperation = namedtuple('PriceOperation', ('position', 'price')) PriceOperation = namedtuple('PriceOperation', ('position', 'price'))
CancelOperation = namedtuple('CancelOperation', ('position',)) CancelOperation = namedtuple('CancelOperation', ('position',))
AddOperation = namedtuple('AddOperation', ('item', 'variation', 'price', 'addon_to', 'subevent')) AddOperation = namedtuple('AddOperation', ('item', 'variation', 'price', 'addon_to', 'subevent'))
SplitOperation = namedtuple('SplitOperation', ('position',))
def __init__(self, order: Order, user, notify=True): def __init__(self, order: Order, user, notify=True):
self.order = order self.order = order
self.user = user self.user = user
self.split_order = None
self._totaldiff = 0 self._totaldiff = 0
self._quotadiff = Counter() self._quotadiff = Counter()
self._operations = [] self._operations = []
@@ -782,6 +788,12 @@ class OrderChangeManager:
self._quotadiff.update(new_quotas) self._quotadiff.update(new_quotas)
self._operations.append(self.AddOperation(item, variation, price, addon_to, subevent)) self._operations.append(self.AddOperation(item, variation, price, addon_to, subevent))
def split(self, position: OrderPosition):
if self.order.event.settings.invoice_include_free or position.price != Decimal('0.00'):
self._invoice_dirty = True
self._operations.append(self.SplitOperation(position))
def _check_quotas(self): def _check_quotas(self):
for quota, diff in self._quotadiff.items(): for quota, diff in self._quotadiff.items():
if diff <= 0: if diff <= 0:
@@ -801,12 +813,26 @@ class OrderChangeManager:
def _check_paid_to_free(self): def _check_paid_to_free(self):
if self.order.total == 0: if self.order.total == 0:
try: try:
mark_order_paid(self.order, 'free', send_mail=False, count_waitinglist=False) mark_order_paid(
self.order, 'free', send_mail=False, count_waitinglist=False,
user=self.user
)
except Quota.QuotaExceededException:
raise OrderError(self.error_messages['paid_to_free_exceeded'])
if self.split_order and self.split_order.total == 0:
try:
mark_order_paid(
self.split_order, 'free', send_mail=False, count_waitinglist=False,
user=self.user
)
except Quota.QuotaExceededException: except Quota.QuotaExceededException:
raise OrderError(self.error_messages['paid_to_free_exceeded']) raise OrderError(self.error_messages['paid_to_free_exceeded'])
def _perform_operations(self): def _perform_operations(self):
nextposid = self.order.positions.aggregate(m=Max('positionid'))['m'] + 1 nextposid = self.order.positions.aggregate(m=Max('positionid'))['m'] + 1
split_positions = []
for op in self._operations: for op in self._operations:
if isinstance(op, self.ItemOperation): if isinstance(op, self.ItemOperation):
self.order.log_action('pretix.event.order.changed.item', user=self.user, data={ self.order.log_action('pretix.event.order.changed.item', user=self.user, data={
@@ -891,23 +917,89 @@ class OrderChangeManager:
'positionid': pos.positionid, 'positionid': pos.positionid,
'subevent': op.subevent.pk if op.subevent else None, 'subevent': op.subevent.pk if op.subevent else None,
}) })
elif isinstance(op, self.SplitOperation):
split_positions.append(op.position)
def _recalculate_total_and_payment_fee(self): if split_positions:
payment_fee = Decimal('0.00') self.split_order = self._create_split_order(split_positions)
if self.order.total != 0:
prov = self._get_payment_provider()
if prov:
payment_fee = prov.calculate_fee(self.order.total)
if payment_fee: def _create_split_order(self, split_positions):
fee = self.order.fees.get_or_create(fee_type=OrderFee.FEE_TYPE_PAYMENT, defaults={'value': 0})[0] split_order = Order.objects.get(pk=self.order.pk)
split_order.pk = None
split_order.code = None
split_order.datetime = now()
split_order.secret = generate_secret()
split_order.save()
split_order.log_action('pretix.event.order.changed.split_from', user=self.user, data={
'original_order': self.order.code
})
for op in split_positions:
self.order.log_action('pretix.event.order.changed.split', user=self.user, data={
'position': op.pk,
'positionid': op.positionid,
'old_item': op.item.pk,
'old_variation': op.variation.pk if op.variation else None,
'old_price': op.price,
'new_order': split_order.code,
})
op.order = split_order
op.secret = generate_position_secret()
op.save()
try:
ia = copy.copy(self.order.invoice_address)
ia.pk = None
ia.order = split_order
ia.save()
except InvoiceAddress.DoesNotExist:
pass
split_order.total = sum([p.price for p in split_positions])
if split_order.total != Decimal('0.00') and self.order.status != Order.STATUS_PAID:
payment_fee = self._get_payment_provider().calculate_fee(split_order.total)
fee = split_order.fees.get_or_create(fee_type=OrderFee.FEE_TYPE_PAYMENT, defaults={'value': 0})[0]
fee.value = payment_fee fee.value = payment_fee
fee._calculate_tax() fee._calculate_tax()
fee.save() if payment_fee != 0:
else: fee.save()
self.order.fees.filter(fee_type=OrderFee.FEE_TYPE_PAYMENT).delete() elif fee.pk:
fee.delete()
split_order.total += fee.value
self.order.total = sum([p.price for p in self.order.positions.all()]) + sum([f.value for f in self.order.fees.all()]) for fee in self.order.fees.exclude(fee_type=OrderFee.FEE_TYPE_PAYMENT):
new_fee = copy.copy(fee)
new_fee.pk = None
new_fee.order = split_order
split_order.total += new_fee.value
new_fee.save()
split_order.save()
if split_order.total != Decimal('0.00') and self.order.invoices.filter(is_cancellation=False).last():
generate_invoice(split_order)
return split_order
def _recalculate_total_and_payment_fee(self):
self.order.total = sum([p.price for p in self.order.positions.all()])
if self.order.status != Order.STATUS_PAID:
# Do not change payment fees of paid orders
payment_fee = Decimal('0.00')
if self.order.total != 0:
prov = self._get_payment_provider()
if prov:
payment_fee = prov.calculate_fee(self.order.total)
if payment_fee:
fee = self.order.fees.get_or_create(fee_type=OrderFee.FEE_TYPE_PAYMENT, defaults={'value': 0})[0]
fee.value = payment_fee
fee._calculate_tax()
fee.save()
else:
self.order.fees.filter(fee_type=OrderFee.FEE_TYPE_PAYMENT).delete()
self.order.total += sum([f.value for f in self.order.fees.all()])
self.order.save() self.order.save()
def _reissue_invoice(self): def _reissue_invoice(self):
@@ -917,7 +1009,7 @@ class OrderChangeManager:
generate_invoice(self.order) generate_invoice(self.order)
def _check_complete_cancel(self): def _check_complete_cancel(self):
cancels = len([o for o in self._operations if isinstance(o, self.CancelOperation)]) cancels = len([o for o in self._operations if isinstance(o, (self.CancelOperation, self.SplitOperation))])
if cancels == self.order.positions.count(): if cancels == self.order.positions.count():
raise OrderError(self.error_messages['complete_cancel']) raise OrderError(self.error_messages['complete_cancel'])
@@ -928,27 +1020,27 @@ class OrderChangeManager:
except InvoiceAddress.DoesNotExist: except InvoiceAddress.DoesNotExist:
return None return None
def _notify_user(self): def _notify_user(self, order):
with language(self.order.locale): with language(order.locale):
try: try:
invoice_name = self.order.invoice_address.name invoice_name = order.invoice_address.name
invoice_company = self.order.invoice_address.company invoice_company = order.invoice_address.company
except InvoiceAddress.DoesNotExist: except InvoiceAddress.DoesNotExist:
invoice_name = "" invoice_name = ""
invoice_company = "" invoice_company = ""
email_template = self.order.event.settings.mail_text_order_changed email_template = order.event.settings.mail_text_order_changed
email_context = { email_context = {
'event': self.order.event.name, 'event': order.event.name,
'url': build_absolute_uri(self.order.event, 'presale:event.order', kwargs={ 'url': build_absolute_uri(self.order.event, 'presale:event.order', kwargs={
'order': self.order.code, 'order': order.code,
'secret': self.order.secret 'secret': order.secret
}), }),
'invoice_name': invoice_name, 'invoice_name': invoice_name,
'invoice_company': invoice_company, 'invoice_company': invoice_company,
} }
email_subject = _('Your order has been changed: %(code)s') % {'code': self.order.code} email_subject = _('Your order has been changed: %(code)s') % {'code': order.code}
try: try:
self.order.send_mail( order.send_mail(
email_subject, email_template, email_context, email_subject, email_template, email_context,
'pretix.event.order.email.order_changed', self.user 'pretix.event.order.email.order_changed', self.user
) )
@@ -973,7 +1065,9 @@ class OrderChangeManager:
self._clear_tickets_cache() self._clear_tickets_cache()
self._check_paid_to_free() self._check_paid_to_free()
if self.notify: if self.notify:
self._notify_user() self._notify_user(self.order)
if self.split_order:
self._notify_user(self.split_order)
def _clear_tickets_cache(self): def _clear_tickets_cache(self):
CachedTicket.objects.filter(order_position__order=self.order).delete() CachedTicket.objects.filter(order_position__order=self.order).delete()

View File

@@ -138,7 +138,9 @@ order_placed = EventPluginSignal(
) )
""" """
This signal is sent out every time an order is placed. The order object is given This signal is sent out every time an order is placed. The order object is given
as the first argument. as the first argument. This signal is *not* sent out if an order is created through
splitting an existing order, so you can not expect to see all orders by listening
to this signal.
As with all event-plugin signals, the ``sender`` keyword argument will contain the event. As with all event-plugin signals, the ``sender`` keyword argument will contain the event.
""" """
@@ -148,7 +150,8 @@ order_paid = EventPluginSignal(
) )
""" """
This signal is sent out every time an order is paid. The order object is given This signal is sent out every time an order is paid. The order object is given
as the first argument. as the first argument. This signal is *not* sent out if an order is marked as paid
because it an already-paid order has been splitted.
As with all event-plugin signals, the ``sender`` keyword argument will contain the event. As with all event-plugin signals, the ``sender`` keyword argument will contain the event.
""" """

View File

@@ -187,7 +187,8 @@ class OrderPositionChangeForm(forms.Form):
('product', 'Change product'), ('product', 'Change product'),
('price', 'Change price'), ('price', 'Change price'),
('subevent', 'Change event date'), ('subevent', 'Change event date'),
('cancel', 'Remove product') ('cancel', 'Remove product'),
('split', 'Split into new order'),
) )
) )

View File

@@ -85,6 +85,21 @@ def _display_order_changed(event: Event, logentry: LogEntry):
price=formats.localize(Decimal(data['price'])), price=formats.localize(Decimal(data['price'])),
currency=event.currency currency=event.currency
) )
elif logentry.action_type == 'pretix.event.order.changed.split':
old_item = str(event.items.get(pk=data['old_item']))
if data['old_variation']:
old_item += ' - ' + str(ItemVariation.objects.get(pk=data['old_variation']))
return text + ' ' + _('Position #{posid} ({old_item}, {old_price} {currency}) split into new order: {order}').format(
old_item=old_item,
posid=data.get('positionid', '?'),
order=data['new_order'],
old_price=formats.localize(Decimal(data['old_price'])),
currency=event.currency
)
elif logentry.action_type == 'pretix.event.order.changed.split_from':
return _('This order has been created by splitting the order {order}').format(
order=data['original_order'],
)
@receiver(signal=logentry_display, dispatch_uid="pretixcontrol_logentry_display") @receiver(signal=logentry_display, dispatch_uid="pretixcontrol_logentry_display")

View File

@@ -36,6 +36,12 @@
If an invoice is attached to the order, a cancellation will be created together with a new invoice. If an invoice is attached to the order, a cancellation will be created together with a new invoice.
{% endblocktrans %} {% endblocktrans %}
</p> </p>
<p>
{% blocktrans trimmed %}
If you chose "split into new order" for multiple positions, they will be all split in one second order
together, not multiple orders.
{% endblocktrans %}
</p>
<div class="alert alert-warning"><strong> <div class="alert alert-warning"><strong>
{% blocktrans trimmed %} {% blocktrans trimmed %}
Please use this tool carefully. Changes you make here are not reversible. Also, if you change an order Please use this tool carefully. Changes you make here are not reversible. Also, if you change an order
@@ -118,6 +124,13 @@
{% endif %} {% endif %}
</label> </label>
</div> </div>
<div class="radio">
<label>
<input name="{{ position.form.prefix }}-operation" type="radio" value="split"
{% if position.form.operation.value == "split" %}checked="checked"{% endif %}>
{% trans "Split into new order" %}
</label>
</div>
<div class="radio"> <div class="radio">
<label> <label>
<input name="{{ position.form.prefix }}-operation" type="radio" value="cancel" <input name="{{ position.form.prefix }}-operation" type="radio" value="cancel"

View File

@@ -593,6 +593,8 @@ class OrderChange(OrderView):
ocm.change_subevent(p, p.form.cleaned_data['subevent']) ocm.change_subevent(p, p.form.cleaned_data['subevent'])
elif p.form.cleaned_data['operation'] == 'cancel': elif p.form.cleaned_data['operation'] == 'cancel':
ocm.cancel(p) ocm.cancel(p)
elif p.form.cleaned_data['operation'] == 'split':
ocm.split(p)
except OrderError as e: except OrderError as e:
p.custom_error = str(e) p.custom_error = str(e)

View File

@@ -762,3 +762,307 @@ class OrderChangeManagerTests(TestCase):
assert fee.value == prov.calculate_fee(self.order.total) assert fee.value == prov.calculate_fee(self.order.total)
assert fee.tax_rate == Decimal('19.00') assert fee.tax_rate == Decimal('19.00')
assert fee.tax_value == Decimal('0.05') assert fee.tax_value == Decimal('0.05')
def test_split_simple(self):
old_secret = self.op2.secret
self.ocm.split(self.op2)
self.ocm.commit()
self.order.refresh_from_db()
self.op2.refresh_from_db()
assert self.order.total == Decimal('23.00')
assert self.order.positions.count() == 1
assert self.op2.order != self.order
o2 = self.op2.order
assert o2.total == Decimal('23.00')
assert o2.positions.count() == 1
assert o2.code != self.order.code
assert o2.secret != self.order.secret
assert o2.datetime > self.order.datetime
assert self.op2.secret != old_secret
assert not self.order.invoices.exists()
assert not o2.invoices.exists()
def test_split_pending_payment_fees(self):
# Set payment fees
self.event.settings.set('tax_rate_default', self.tr19.pk)
prov = self.ocm._get_payment_provider()
prov.settings.set('_fee_percent', Decimal('2.00'))
prov.settings.set('_fee_abs', Decimal('1.00'))
prov.settings.set('_fee_reverse_calc', False)
self.ocm.change_price(self.op1, Decimal('23.00'))
self.ocm.commit()
self.order.refresh_from_db()
assert self.order.total == Decimal('47.92')
fee = self.order.fees.get(fee_type=OrderFee.FEE_TYPE_PAYMENT)
assert fee.value == Decimal('1.92')
assert fee.tax_rate == Decimal('19.00')
# Split
self.ocm.split(self.op2)
self.ocm.commit()
self.order.refresh_from_db()
self.op2.refresh_from_db()
# First order
assert self.order.total == Decimal('24.46')
fee = self.order.fees.get(fee_type=OrderFee.FEE_TYPE_PAYMENT)
assert fee.value == Decimal('1.46')
assert fee.tax_rate == Decimal('19.00')
assert self.order.positions.count() == 1
assert self.order.fees.count() == 1
# New order
assert self.op2.order != self.order
o2 = self.op2.order
assert o2.total == Decimal('24.46')
fee = o2.fees.get(fee_type=OrderFee.FEE_TYPE_PAYMENT)
assert fee.value == Decimal('1.46')
assert fee.tax_rate == Decimal('19.00')
assert o2.positions.count() == 1
assert o2.fees.count() == 1
def test_split_paid_no_payment_fees(self):
self.order.status = Order.STATUS_PAID
self.order.save()
# Split
self.ocm.split(self.op2)
self.ocm.commit()
self.order.refresh_from_db()
self.op2.refresh_from_db()
# First order
assert self.order.total == Decimal('23.00')
assert not self.order.fees.exists()
# New order
assert self.op2.order != self.order
o2 = self.op2.order
assert o2.total == Decimal('23.00')
assert o2.status == Order.STATUS_PAID
assert o2.positions.count() == 1
assert o2.fees.count() == 0
def test_split_invoice_address(self):
ia = InvoiceAddress.objects.create(
order=self.order, is_business=True, vat_id='ATU1234567', vat_id_validated=True,
country=Country('AT'), company='Sample'
)
# Split
self.ocm.split(self.op2)
self.ocm.commit()
self.order.refresh_from_db()
self.op2.refresh_from_db()
# First order
assert self.order.total == Decimal('23.00')
assert self.order.invoice_address == ia
# New order
assert self.op2.order != self.order
o2 = self.op2.order
o2.refresh_from_db()
ia = InvoiceAddress.objects.get(pk=ia.pk)
assert o2.total == Decimal('23.00')
assert o2.positions.count() == 1
assert o2.invoice_address != ia
assert o2.invoice_address.company == 'Sample'
def test_split_reverse_charge(self):
ia = self._enable_reverse_charge()
# Set payment fees
self.event.settings.set('tax_rate_default', self.tr19.pk)
prov = self.ocm._get_payment_provider()
prov.settings.set('_fee_percent', Decimal('2.00'))
prov.settings.set('_fee_reverse_calc', False)
self.ocm.recalculate_taxes()
self.ocm.commit()
self.order.refresh_from_db()
# Check if reverse charge is active
assert self.order.total == Decimal('43.86')
fee = self.order.fees.get(fee_type=OrderFee.FEE_TYPE_PAYMENT)
assert fee.value == Decimal('0.86')
assert fee.tax_rate == Decimal('0.00')
self.op1.refresh_from_db()
self.op2.refresh_from_db()
assert self.op1.price == Decimal('21.50')
assert self.op2.price == Decimal('21.50')
# Split
self.ocm.split(self.op2)
self.ocm.commit()
self.order.refresh_from_db()
self.op2.refresh_from_db()
# First order
assert self.order.total == Decimal('21.93')
fee = self.order.fees.get(fee_type=OrderFee.FEE_TYPE_PAYMENT)
assert fee.value == Decimal('0.43')
assert fee.tax_rate == Decimal('0.00')
assert fee.tax_value == Decimal('0.00')
assert self.order.positions.count() == 1
assert self.order.fees.count() == 1
assert self.order.positions.first().price == Decimal('21.50')
assert self.order.positions.first().tax_value == Decimal('0.00')
# New order
assert self.op2.order != self.order
o2 = self.op2.order
print([p.price for p in o2.positions.all()], [p.value for p in o2.fees.all()])
assert o2.total == Decimal('21.93')
fee = o2.fees.get(fee_type=OrderFee.FEE_TYPE_PAYMENT)
assert fee.value == Decimal('0.43')
assert fee.tax_rate == Decimal('0.00')
assert fee.tax_value == Decimal('0.00')
assert o2.positions.count() == 1
assert o2.positions.first().price == Decimal('21.50')
assert o2.positions.first().tax_value == Decimal('0.00')
assert o2.fees.count() == 1
ia = InvoiceAddress.objects.get(pk=ia.pk)
assert o2.invoice_address != ia
assert o2.invoice_address.vat_id_validated is True
def test_split_other_fees(self):
# Check if reverse charge is active
self.order.fees.create(fee_type=OrderFee.FEE_TYPE_SHIPPING, tax_rule=self.tr19, value=Decimal('2.50'))
self.order.total += Decimal('2.50')
self.order.save()
# Split
self.ocm.split(self.op2)
self.ocm.commit()
self.order.refresh_from_db()
self.op2.refresh_from_db()
# First order
assert self.order.total == Decimal('25.50')
fee = self.order.fees.get(fee_type=OrderFee.FEE_TYPE_SHIPPING)
assert fee.value == Decimal('2.50')
assert fee.tax_value == Decimal('0.40')
assert self.order.positions.count() == 1
assert self.order.fees.count() == 1
# New order
assert self.op2.order != self.order
o2 = self.op2.order
assert o2.total == Decimal('25.50')
fee = o2.fees.get(fee_type=OrderFee.FEE_TYPE_SHIPPING)
assert fee.value == Decimal('2.50')
assert fee.tax_value == Decimal('0.40')
assert o2.positions.count() == 1
assert o2.positions.first().price == Decimal('23.00')
assert o2.fees.count() == 1
def test_split_to_empty(self):
self.ocm.split(self.op1)
self.ocm.split(self.op2)
with self.assertRaises(OrderError):
self.ocm.commit()
def test_split_paid_payment_fees(self):
# Set payment fees
self.event.settings.set('tax_rate_default', self.tr19.pk)
prov = self.ocm._get_payment_provider()
prov.settings.set('_fee_percent', Decimal('2.00'))
prov.settings.set('_fee_abs', Decimal('1.00'))
prov.settings.set('_fee_reverse_calc', False)
self.ocm.change_price(self.op1, Decimal('23.00'))
self.ocm.commit()
self.order.refresh_from_db()
assert self.order.total == Decimal('47.92')
fee = self.order.fees.get(fee_type=OrderFee.FEE_TYPE_PAYMENT)
assert fee.value == Decimal('1.92')
assert fee.tax_rate == Decimal('19.00')
self.order.status = Order.STATUS_PAID
self.order.save()
# Split
self.ocm.split(self.op2)
self.ocm.commit()
self.order.refresh_from_db()
self.op2.refresh_from_db()
# First order
assert self.order.total == Decimal('24.92')
fee = self.order.fees.get(fee_type=OrderFee.FEE_TYPE_PAYMENT)
assert fee.value == Decimal('1.92')
assert fee.tax_rate == Decimal('19.00')
assert self.order.positions.count() == 1
assert self.order.fees.count() == 1
# New order
assert self.op2.order != self.order
o2 = self.op2.order
assert o2.total == Decimal('23.00')
assert o2.fees.count() == 0
def test_split_invoice(self):
generate_invoice(self.order)
assert self.order.invoices.count() == 1
assert self.order.invoices.last().lines.count() == 2
self.ocm.split(self.op2)
self.ocm.commit()
self.order.refresh_from_db()
self.op2.refresh_from_db()
o2 = self.op2.order
assert self.order.invoices.count() == 3
assert self.order.invoices.last().lines.count() == 1
assert o2.invoices.count() == 1
assert o2.invoices.last().lines.count() == 1
def test_split_to_free_invoice(self):
self.event.settings.invoice_include_free = False
self.ocm.change_price(self.op2, Decimal('0.00'))
self.ocm.commit()
self.op2.refresh_from_db()
self.ocm._invoice_dirty = False
generate_invoice(self.order)
assert self.order.invoices.count() == 1
assert self.order.invoices.last().lines.count() == 1
self.ocm.split(self.op2)
self.ocm.commit()
self.order.refresh_from_db()
self.op2.refresh_from_db()
o2 = self.op2.order
assert self.order.invoices.count() == 1
assert self.order.invoices.last().lines.count() == 1
assert o2.invoices.count() == 0
def test_split_to_original_free(self):
self.ocm.change_price(self.op2, Decimal('0.00'))
self.ocm.commit()
self.op2.refresh_from_db()
self.ocm.split(self.op1)
self.ocm.commit()
self.order.refresh_from_db()
self.op2.refresh_from_db()
o2 = self.op1.order
assert self.order.total == Decimal('0.00')
assert self.order.status == Order.STATUS_PAID
assert o2.total == Decimal('23.00')
assert o2.status == Order.STATUS_PENDING
def test_split_to_new_free(self):
self.ocm.change_price(self.op2, Decimal('0.00'))
self.ocm.commit()
self.op2.refresh_from_db()
self.ocm.split(self.op2)
self.ocm.commit()
self.order.refresh_from_db()
self.op2.refresh_from_db()
o2 = self.op2.order
assert self.order.total == Decimal('23.00')
assert self.order.status == Order.STATUS_PENDING
assert o2.total == Decimal('0.00')
assert o2.status == Order.STATUS_PAID