<commit_before>import os

from datetime import date, datetime
from decimal import Decimal

from django.contrib.auth.models import User
from django.core.exceptions import ValidationError
from django.test import TestCase

import plata
from plata.contact.models import Contact
from plata.discount.models import Discount
from plata.product.models import TaxClass, Product, ProductVariation, Category,\
    ProductPrice, OptionGroup, Option
from plata.product.stock.models import Period, StockTransaction
from plata.shop.models import Order, OrderStatus, OrderPayment

from plata.tests.base import PlataTest, get_request


class OrderTest(PlataTest):
    def test_00_test(self):
        """Test assertRaisesWithCode works as expected"""
        def raise_validationerror():
            raise ValidationError('test', code='test')

        def raise_notimplementederror():
            raise NotImplementedError

        self.assertRaisesWithCode(ValidationError, raise_validationerror, code='test')

        self.assertRaises(ValidationError,
            lambda: self.assertRaisesWithCode(ValidationError,
                raise_validationerror, code='something'))

        self.assertRaises(NotImplementedError,
            lambda: self.assertRaisesWithCode(ValidationError,
                raise_notimplementederror, code='something'))

        self.assertRaises(Exception,
            lambda: self.assertRaisesWithCode(ValidationError,
                lambda: None, code='something'))

    def test_01_basic_order(self):
        """Test basic order and product properties"""
        product = self.create_product()
        order = self.create_order()

        item_price = Decimal('79.90')
        line_item_price = item_price * 2
        order_total = Decimal('159.80')
        tax_factor = Decimal('1.076')

        price = product.get_price(currency=order.currency)
        self.assertEqual(price.currency, order.currency)
        self.assertAlmostEqual(price.unit_price, item_price)
        self.assertAlmostEqual(price.unit_price_incl_tax, price.unit_price)
        self.assertAlmostEqual(price.unit_price_excl_tax, item_price / tax_factor)
        self.assertAlmostEqual(price.unit_tax, price.unit_price_excl_tax * Decimal('0.076'))

        prices = dict(product.get_prices())
        self.assertAlmostEqual(prices['CHF']['normal'].unit_price, Decimal('99.90'))
        self.assertAlmostEqual(prices['CHF']['sale'].unit_price, Decimal('79.90'))
        self.assertAlmostEqual(prices['EUR']['normal'].unit_price, Decimal('49.90'))
        self.assertEqual(prices['EUR']['sale'], None)
        self.assertTrue(product.in_sale('CHF'))
        self.assertFalse(product.in_sale('EUR'))

        order.modify_item(product, 5)
        order.modify_item(product, -4)
        item = order.modify_item(product, 1)

        self.assertEqual(order.items.count(), 1)

        self.assertEqual(item.quantity, 2)

        self.assertAlmostEqual(order.items_subtotal, order_total / tax_factor)
        self.assertAlmostEqual(order.items_subtotal + order.items_tax, order_total)
        self.assertAlmostEqual(order.total, order_total)

        self.assertAlmostEqual(item._unit_price, item_price / tax_factor)
        self.assertAlmostEqual(item.discounted_subtotal_incl_tax, line_item_price)
        self.assertAlmostEqual(item.discounted_subtotal_excl_tax, line_item_price / tax_factor)
        self.assertAlmostEqual(item.discounted_subtotal_incl_tax, line_item_price)

        self.assertAlmostEqual(item.unit_price, item_price)
        self.assertAlmostEqual(item.line_item_discount, 0)
        self.assertAlmostEqual(item.discounted_subtotal, item.discounted_subtotal_incl_tax)

        # Switch around tax handling and re-test
        plata.settings.PLATA_PRICE_INCLUDES_TAX = False

        self.assertAlmostEqual(item.unit_price, item_price / tax_factor)
        self.assertAlmostEqual(item.line_item_discount, 0 / tax_factor)
        self.assertAlmostEqual(item.discounted_subtotal, item.discounted_subtotal_excl_tax)

        # Switch tax handling back
        plata.settings.PLATA_PRICE_INCLUDES_TAX = True

        product.prices.filter(currency='CHF', is_sale=False).delete()
        prices = dict(product.get_prices())
        self.assertAlmostEqual(prices['CHF']['normal'].unit_price, Decimal('99.90'))
        self.assertAlmostEqual(prices['CHF']['sale'].unit_price, Decimal('79.90'))

        from django.core.cache import cache
        cache.delete('product-prices-%s' % product.pk)

        prices = dict(product.get_prices())
        self.assertEqual(prices['CHF']['normal'], None)
        self.assertAlmostEqual(prices['CHF']['sale'].unit_price, Decimal('79.90'))

    def test_02_eur_order(self):
        """Test basic order in EUR works as expected"""
        product = self.create_product()
        order = self.create_order()

        order.currency = 'EUR'
        order.save()

        item = order.modify_item(product, 2)

        self.assertEqual(item.unit_price, Decimal('49.90'))
        self.assertEqual(item.currency, order.currency)

    def test_03_mixed_currencies(self):
        """Test orders with mixed currencies are rejected during validation"""

        p1 = self.create_product()
        p2 = self.create_product()
        order = self.create_order()

        order.currency = 'CHF'
        i1 = order.modify_item(p1, 3)

        order.currency = 'EUR'
        self.assertRaisesWithCode(ValidationError, lambda: order.modify_item(p2, 2),
            code='multiple_currency')

        # Validation should still fail
        self.assertRaisesWithCode(ValidationError, lambda: order.validate(),
            code='multiple_currency')

        order.currency = 'CHF'
        # Order should validate now
        order.validate()

    def test_04_order_modify_item(self):
        """Test Order.modify_item method is well behaving"""
        p1 = self.create_product()
        p2 = self.create_product()
        order = self.create_order()

        order.modify_item(p1, 42)
        order.modify_item(p2, 42)
        self.assertEqual(order.items.count(), 2)

        order.modify_item(p1, -42)
        self.assertEqual(order.items.count(), 1)

        item = order.modify_item(p1, relative=3)
        self.assertEqual(item.quantity, 3)
        item = order.modify_item(p1, relative=2)
        self.assertEqual(item.quantity, 5)
        item = order.modify_item(p1, absolute=33)
        self.assertEqual(item.quantity, 33)

    def test_05_order_status(self):
        """Test order status modification"""
        order = self.create_order()

        self.assertRaisesWithCode(ValidationError, lambda: order.update_status(
            Order.CHECKOUT,
            'Checkout process has started',
            ), code='order_empty')

        product = self.create_product()
        order.modify_item(product, 1)

        # Should be possible to update order status now
        order.update_status(
            Order.CONFIRMED,
            'Order has been confirmed',
            )

        # Should not be possible to modify order once checkout process has started
        self.assertRaisesWithCode(ValidationError, lambda: order.modify_item(product, 2),
            code='order_sealed')

        self.assertEqual(order.status, Order.CONFIRMED)

    def test_06_order_percentage_discount(self):
        """Test a simple percentage discount"""
        order = self.create_order()
        p1 = self.create_product()
        p2 = self.create_product()

        order.modify_item(p1, 3)
        order.modify_item(p2, 5)

        discount = Discount.objects.create(
            is_active=False,
            type=Discount.PERCENTAGE,
            code='asdf',
            name='Percentage discount',
            value=30)

        self.assertRaises(ValidationError, lambda: order.add_discount(discount))
        discount.is_active = True
        discount.save()

        order.add_discount(discount)
        order.recalculate_total()<commit_msg>Test order status modification<commit_after>import os

from datetime import date, datetime
from decimal import Decimal

from django.contrib.auth.models import User
from django.core.exceptions import ValidationError
from django.test import TestCase

import plata
from plata.contact.models import Contact
from plata.discount.models import Discount
from plata.product.models import TaxClass, Product, ProductVariation, Category,\
    ProductPrice, OptionGroup, Option
from plata.product.stock.models import Period, StockTransaction
from plata.shop.models import Order, OrderStatus, OrderPayment

from plata.tests.base import PlataTest, get_request


class OrderTest(PlataTest):
    def test_00_test(self):
        """Test assertRaisesWithCode works as expected"""
        def raise_validationerror():
            raise ValidationError('test', code='test')

        def raise_notimplementederror():
            raise NotImplementedError

        self.assertRaisesWithCode(ValidationError, raise_validationerror, code='test')

        self.assertRaises(ValidationError,
            lambda: self.assertRaisesWithCode(ValidationError,
                raise_validationerror, code='something'))

        self.assertRaises(NotImplementedError,
            lambda: self.assertRaisesWithCode(ValidationError,
                raise_notimplementederror, code='something'))

        self.assertRaises(Exception,
            lambda: self.assertRaisesWithCode(ValidationError,
                lambda: None, code='something'))

    def test_01_basic_order(self):
        """Test basic order and product properties"""
        product = self.create_product()
        order = self.create_order()

        item_price = Decimal('79.90')
        line_item_price = item_price * 2
        order_total = Decimal('159.80')
        tax_factor = Decimal('1.076')

        price = product.get_price(currency=order.currency)
        self.assertEqual(price.currency, order.currency)
        self.assertAlmostEqual(price.unit_price, item_price)
        self.assertAlmostEqual(price.unit_price_incl_tax, price.unit_price)
        self.assertAlmostEqual(price.unit_price_excl_tax, item_price / tax_factor)
        self.assertAlmostEqual(price.unit_tax, price.unit_price_excl_tax * Decimal('0.076'))

        prices = dict(product.get_prices())
        self.assertAlmostEqual(prices['CHF']['normal'].unit_price, Decimal('99.90'))
        self.assertAlmostEqual(prices['CHF']['sale'].unit_price, Decimal('79.90'))
        self.assertAlmostEqual(prices['EUR']['normal'].unit_price, Decimal('49.90'))
        self.assertEqual(prices['EUR']['sale'], None)
        self.assertTrue(product.in_sale('CHF'))
        self.assertFalse(product.in_sale('EUR'))

        order.modify_item(product, 5)
        order.modify_item(product, -4)
        item = order.modify_item(product, 1)

        self.assertEqual(order.items.count(), 1)

        self.assertEqual(item.quantity, 2)

        self.assertAlmostEqual(order.items_subtotal, order_total / tax_factor)
        self.assertAlmostEqual(order.items_subtotal + order.items_tax, order_total)
        self.assertAlmostEqual(order.total, order_total)

        self.assertAlmostEqual(item._unit_price, item_price / tax_factor)
        self.assertAlmostEqual(item.discounted_subtotal_incl_tax, line_item_price)
        self.assertAlmostEqual(item.discounted_subtotal_excl_tax, line_item_price / tax_factor)
        self.assertAlmostEqual(item.discounted_subtotal_incl_tax, line_item_price)

        self.assertAlmostEqual(item.unit_price, item_price)
        self.assertAlmostEqual(item.line_item_discount, 0)
        self.assertAlmostEqual(item.discounted_subtotal, item.discounted_subtotal_incl_tax)

        # Switch around tax handling and re-test
        plata.settings.PLATA_PRICE_INCLUDES_TAX = False

        self.assertAlmostEqual(item.unit_price, item_price / tax_factor)
        self.assertAlmostEqual(item.line_item_discount, 0 / tax_factor)
        self.assertAlmostEqual(item.discounted_subtotal, item.discounted_subtotal_excl_tax)

        # Switch tax handling back
        plata.settings.PLATA_PRICE_INCLUDES_TAX = True

        product.prices.filter(currency='CHF', is_sale=False).delete()
        prices = dict(product.get_prices())
        self.assertAlmostEqual(prices['CHF']['normal'].unit_price, Decimal('99.90'))
        self.assertAlmostEqual(prices['CHF']['sale'].unit_price, Decimal('79.90'))

        from django.core.cache import cache
        cache.delete('product-prices-%s' % product.pk)

        prices = dict(product.get_prices())
        self.assertEqual(prices['CHF']['normal'], None)
        self.assertAlmostEqual(prices['CHF']['sale'].unit_price, Decimal('79.90'))

    def test_02_eur_order(self):
        """Test basic order in EUR works as expected"""
        product = self.create_product()
        order = self.create_order()

        order.currency = 'EUR'
        order.save()

        item = order.modify_item(product, 2)

        self.assertEqual(item.unit_price, Decimal('49.90'))
        self.assertEqual(item.currency, order.currency)

    def test_03_mixed_currencies(self):
        """Test orders with mixed currencies are rejected during validation"""

        p1 = self.create_product()
        p2 = self.create_product()
        order = self.create_order()

        order.currency = 'CHF'
        i1 = order.modify_item(p1, 3)

        order.currency = 'EUR'
        self.assertRaisesWithCode(ValidationError, lambda: order.modify_item(p2, 2),
            code='multiple_currency')

        # Validation should still fail
        self.assertRaisesWithCode(ValidationError, lambda: order.validate(),
            code='multiple_currency')

        order.currency = 'CHF'
        # Order should validate now
        order.validate()

    def test_04_order_modify_item(self):
        """Test Order.modify_item method is well behaving"""
        p1 = self.create_product()
        p2 = self.create_product()
        order = self.create_order()

        order.modify_item(p1, 42)
        order.modify_item(p2, 42)
        self.assertEqual(order.items.count(), 2)

        order.modify_item(p1, -42)
        self.assertEqual(order.items.count(), 1)

        item = order.modify_item(p1, relative=3)
        self.assertEqual(item.quantity, 3)
        item = order.modify_item(p1, relative=2)
        self.assertEqual(item.quantity, 5)
        item = order.modify_item(p1, absolute=33)
        self.assertEqual(item.quantity, 33)

    def test_05_order_status(self):
        """Test order status modification"""
        order = self.create_order()

        self.assertRaisesWithCode(ValidationError, lambda: order.update_status(
            Order.CHECKOUT,
            'Checkout process has started',
            ), code='order_empty')

        product = self.create_product()
        order.modify_item(product, 1)

        # Should be possible to update order status now
        order.update_status(
            Order.CONFIRMED,
            'Order has been confirmed',
            )

        # Should not be possible to modify order once checkout process has started
        self.assertRaisesWithCode(ValidationError, lambda: order.modify_item(product, 2),
            code='order_sealed')

        self.assertEqual(order.status, Order.CONFIRMED)
        self.assertEqual(Order.objects.get().status, Order.CONFIRMED)

    def test_06_order_percentage_discount(self):
        """Test a simple percentage discount"""
        order = self.create_order()
        p1 = self.create_product()
        p2 = self.create_product()

        order.modify_item(p1, 3)
        order.modify_item(p2, 5)

        discount = Discount.objects.create(
            is_active=False,
            type=Discount.PERCENTAGE,
            code='asdf',
            name='Percentage discount',
            value=30)

        self.assertRaises(ValidationError, lambda: order.add_discount(discount))
        discount.is_active = True
        discount.save()

        order.add_discount(discount)
        order.recalculate_total()
{'fraction_non_alphanumeric': 0.10275642227639227, 'fraction_numerical': 0.013625851615725984, 'mean_word_length': 3.057823991884352, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3703611', 'n_tokens_mistral': 4877, 'n_tokens_neox': 4587, 'n_words': 832}