diff options
Diffstat (limited to 'tests/test_program_numbers.py')
| -rw-r--r-- | tests/test_program_numbers.py | 316 |
1 files changed, 305 insertions, 11 deletions
diff --git a/tests/test_program_numbers.py b/tests/test_program_numbers.py index 9a698f3cf9b..01da22c1de4 100644 --- a/tests/test_program_numbers.py +++ b/tests/test_program_numbers.py @@ -2,6 +2,8 @@ # Part of Odoo. See LICENSE file for full copyright and licensing details. from openerp.addons.sale_coupon.tests.common import TestSaleCouponCommon +from odoo.exceptions import UserError + class TestSaleCouponProgramNumbers(TestSaleCouponCommon): @@ -9,6 +11,8 @@ class TestSaleCouponProgramNumbers(TestSaleCouponCommon): super(TestSaleCouponProgramNumbers, self).setUp() self.iPadMini = self.env.ref('product.product_product_6').copy() + self.iPod = self.env.ref('product.product_product_11').copy() + self.wirelessKeyboard = self.env.ref('product.product_product_9').copy() self.computerCase = self.env.ref('product.product_product_16').copy() self.littleServer = self.env.ref('product.consu_delivery_02').copy() self.steve = self.env['res.partner'].create({ @@ -64,7 +68,7 @@ class TestSaleCouponProgramNumbers(TestSaleCouponCommon): self.env['sale.coupon.apply.code'].sudo().apply_coupon(order, 'test_10pc') self.assertEqual(len(order.order_line.ids), 3, "We should 3 lines as we should have a new line for promo code reduction") self.assertEqual(order.amount_total, 864, "Only paid product should have their price discounted") - order.order_line.filtered(lambda x: 'Discount' in x.name).unlink() # Remove Discount + order.order_line.filtered(lambda x: 'Discount' in x.name).unlink() # Remove Discount # Check free product is removed since we are below minimum required quantity sol1.product_uom_qty = 3 @@ -113,7 +117,7 @@ class TestSaleCouponProgramNumbers(TestSaleCouponCommon): self.assertEqual(len(order.order_line.ids), 4, "We should have 4 lines as we should have a new line for promo code reduction") # Check you can still have auto applied promotion if you have a promo code set to the order - sol4 = self.env['sale.order.line'].create({ + self.env['sale.order.line'].create({ 'product_id': self.iPadMini.id, 'name': 'iPad Mini', 'product_uom_qty': 4.0, @@ -155,11 +159,16 @@ class TestSaleCouponProgramNumbers(TestSaleCouponCommon): sol1._compute_tax_id() order.recompute_coupon_lines() self.assertEqual(len(order.order_line.ids), 2, "We should now get the reduction line since we have 320$ tax included (ipad is 320$ tax included)") - # (320 +15% tax) - (20% of (320 + 15% tax) = 368 + 73.6 = 294.4 + # Name | Qty | price_unit | Tax | HTVA | TVAC | TVA | + # -------------------------------------------------------------------------------- + # iPod | 1 | 320.00 | 15% excl | 320.00 | 368.00 | 48.00 + # 20% discount on ipad | 1 | -64.00 | 15% excl | -64.00 | -73.60 | -9.60 + # -------------------------------------------------------------------------------- + # TOTAL | 256.00 | 294.40 | 38.40 self.assertEqual(order.amount_total, 294.4, "Check discount has been applied correctly (eg: on taxes aswell)") # test coupon with code works the same as auto applied_programs - p_specific_product.write({'promo_code_usage':'code_needed', 'promo_code':'20pc'}) + p_specific_product.write({'promo_code_usage': 'code_needed', 'promo_code': '20pc'}) order.order_line.filtered(lambda l: l.is_reward_line).unlink() order.recompute_coupon_lines() self.assertEqual(len(order.order_line.ids), 1, "Reduction should be removed since we deleted it and it is now a promo code usage, it shouldn't be automatically reapplied") @@ -168,21 +177,306 @@ class TestSaleCouponProgramNumbers(TestSaleCouponCommon): order.recompute_coupon_lines() self.assertEqual(len(order.order_line.ids), 2, "We should now get the reduction line since we have 320$ tax included (ipad is 320$ tax included)") - #check discount applied only on ipad - sol2 = self.env['sale.order.line'].create({ + # check discount applied only on ipad + self.env['sale.order.line'].create({ 'product_id': self.computerCase.id, 'name': 'Computer Case', 'product_uom_qty': 10.0, 'order_id': order.id, }) order.recompute_coupon_lines() - # (10x25) + (320 +15% tax) - (20% of (320 + 15% tax) = 250 + 368 - 73.6 = 544.4 + # Name | Qty | price_unit | Tax | HTVA | TVAC | TVA | + # -------------------------------------------------------------------------------- + # Computer Case | 10 | 25.00 | / | 250.00 | 250.00 | / + # iPad | 1 | 320.00 | 15% excl | 320.00 | 368.00 | 48.00 + # 20% discount on ipad | 1 | -64.00 | 15% excl | -64.00 | -73.60 | -9.60 + # -------------------------------------------------------------------------------- + # TOTAL | 506.00 | 544.40 | 38.40 self.assertEqual(order.amount_total, 544.4, "We should only get reduction on ipad") sol1.product_uom_qty = 10 order.recompute_coupon_lines() - # (10x25) + (10x (320 +15% tax)) - (20% of (10x (320 + 15% tax))) - 2 free iPads = 250 + 3680 - 736 - 736 = 2458 - self.assertEqual(order.amount_total, 2458, "Changing ipad quantity should change discount amount correctly") - + # Note: Since we now have 2 free ipads, we should discount only 8 of the 10 ipads in carts since we don't want to discount free ipads + # Name | Qty | price_unit | Tax | HTVA | TVAC | TVA | + # -------------------------------------------------------------------------------- + # Computer Case | 10 | 25.00 | / | 250.00 | 250.00 | / + # iPad | 10 | 320.00 | 15% excl | 3200.00 | 3680.00 | 480.00 + # Free iPad | 2 | -320.00 | 15% excl | -640.00 | -736.00 | -96.00 + # 20% discount on ipad | 1 | -512.00 | 15% excl | -512.00 | -588.80 | -78.80 + # -------------------------------------------------------------------------------- + # TOTAL | 2298.00 | 2605.20 | 305.20 + self.assertEqual(order.amount_total, 2605.20, "Changing ipad quantity should change discount amount correctly") p_specific_product.discount_max_amount = 200 order.recompute_coupon_lines() - self.assertEqual(order.amount_total, 2994, "The discount should be limited to $200") + # Name | Qty | price_unit | Tax | HTVA | TVAC | TVA | + # -------------------------------------------------------------------------------- + # Computer Case | 10 | 25.00 | / | 250.00 | 250.00 | / + # iPad | 10 | 320.00 | 15% excl | 3200.00 | 3680.00 | 480.00 + # Free iPad | 2 | -320.00 | 15% excl | -640.00 | -736.00 | -96.00 + # 20% discount on ipad | 1 | -200.00 | 15% excl | -200.00 | -230.00 | -30.00 + # limited to 200 HTVA + # -------------------------------------------------------------------------------- + # TOTAL | 2610.00 | 2964.00 | 354.00 + self.assertEqual(order.amount_total, 2964, "The discount should be limited to $200 tax excluded") + self.assertEqual(order.amount_untaxed, 2610, "The discount should be limited to $200 tax excluded (2)") + + def test_program_numbers_one_discount_line_per_tax(self): + order = self.empty_order + # Create taxes + self.tax_15pc_excl = self.env['account.tax'].create({ + 'name': "15% Tax excl", + 'amount_type': 'percent', + 'amount': 15, + }) + self.tax_50pc_excl = self.env['account.tax'].create({ + 'name': "50% Tax excl", + 'amount_type': 'percent', + 'amount': 50, + }) + self.tax_35pc_incl = self.env['account.tax'].create({ + 'name': "35% Tax incl", + 'amount_type': 'percent', + 'amount': 35, + 'price_include': True, + }) + + # Set tax and prices on products as neeed for the test + (self.product_A + self.iPadMini + self.iPod + self.wirelessKeyboard + self.computerCase).write({'list_price': 100}) + (self.iPadMini + self.computerCase).write({'taxes_id': [(4, self.tax_15pc_excl.id, False)]}) + self.iPod.taxes_id = self.tax_10pc_incl + self.wirelessKeyboard.taxes_id = None + self.product_A.taxes_id = (self.tax_35pc_incl + self.tax_50pc_excl) + + # Add products in order + self.env['sale.order.line'].create({ + 'product_id': self.iPadMini.id, + 'name': 'iPad', + 'product_uom_qty': 7.0, + 'order_id': order.id, + }) + sol2 = self.env['sale.order.line'].create({ + 'product_id': self.iPod.id, + 'name': 'iPod', + 'product_uom_qty': 5.0, + 'order_id': order.id, + }) + self.env['sale.order.line'].create({ + 'product_id': self.wirelessKeyboard.id, + 'name': 'Wireless Keyboard', + 'product_uom_qty': 10.0, + 'order_id': order.id, + }) + self.env['sale.order.line'].create({ + 'product_id': self.product_A.id, + 'name': 'product A with multiple taxes', + 'product_uom_qty': 3.0, + 'order_id': order.id, + }) + self.env['sale.order.line'].create({ + 'product_id': self.computerCase.id, + 'name': 'Computer Case', + 'product_uom_qty': 2.0, + 'order_id': order.id, + }) + + # Create needed programs + self.p2.active = False + self.p_ipad = self.env['sale.coupon.program'].create({ + 'name': 'Buy 1 ipad, get one for free', + 'promo_code_usage': 'no_code_needed', + 'reward_type': 'product', + 'program_type': 'promotion_program', + 'reward_product_id': self.iPadMini.id, + 'rule_products_domain': '[["name","ilike","ipad mini"]]', + }) + self.p_ipod = self.env['sale.coupon.program'].create({ + 'name': 'Buy 1 ipod, get one for free', + 'promo_code_usage': 'no_code_needed', + 'reward_type': 'product', + 'program_type': 'promotion_program', + 'reward_product_id': self.iPod.id, + 'rule_products_domain': '[["name","ilike","ipod"]]', + }) + self.p_keyboard = self.env['sale.coupon.program'].create({ + 'name': 'Buy 1 keyboard, get one for free', + 'promo_code_usage': 'no_code_needed', + 'reward_type': 'product', + 'program_type': 'promotion_program', + 'reward_product_id': self.wirelessKeyboard.id, + 'rule_products_domain': '[["name","ilike","keyboard"]]', + }) + + # Name | Qty | price_unit | Tax | HTVA | TVAC | TVA | + # -------------------------------------------------------------------------------- + # iPod | 5 | 100.00 | 10% incl | 454.55 | 500.00 | 45.45 + # Keyboards | 10 | 100.00 | / | 1000.00 | 1000.00 | / + # iPad | 7 | 100.00 | 15% excl | 700.00 | 805.00 | 105.00 + # Computer Case | 2 | 100.00 | 15% excl | 200.00 | 230.00 | 30.00 + # Product A | 3 | 100.00 | 35% incl | 222.22 | 450.00 | 227.78 + # 50% excl + # -------------------------------------------------------------------------------- + # TOTAL | 2576.77 | 2985.00 | 408.23 + + self.assertEqual(order.amount_total, 2985, "The order total without any programs should be 2985") + self.assertEqual(order.amount_untaxed, 2576.77, "The order untaxed total without any programs should be 2576.77") + self.assertEqual(len(order.order_line.ids), 5, "The order without any programs should have 5 lines") + + # Apply all the programs + order.recompute_coupon_lines() + + # Name | Qty | price_unit | Tax | HTVA | TVAC | TVA | + # -------------------------------------------------------------------------------- + # Free iPod | 2 | -100.00 | 10% incl | -181.82 | -200.00 | -18.18 + # Free Keyboard | 5 | -100.00 | / | -500.00 | -500.00 | / + # Free iPad | 3 | -100.00 | 15% excl | -300.00 | -345.00 | -45.00 + # -------------------------------------------------------------------------------- + # TOTAL AFTER APPLYING FREE PRODUCT PROGRAMS | 1594.95 | 1940.00 | 345.05 + + self.assertEqual(order.amount_total, 1940.00, "The order total with programs should be 1940") + self.assertEqual(order.amount_untaxed, 1594.95, "The order untaxed total with programs should be 1594.95") + self.assertEqual(len(order.order_line.ids), 8, "Order should contains 5 regular product lines and 3 free product lines") + + # Apply 10% on top of everything + self.env['sale.coupon.apply.code'].sudo().apply_coupon(order, 'test_10pc') + + # Name | Qty | price_unit | Tax | HTVA | TVAC | TVA | + # -------------------------------------------------------------------------------- + # 10% on tax 10% incl | 1 | -30.00 | 10% incl | -27.27 | -30.00 | -2.73 + # 10% on no tax | 1 | -50.00 | / | -50.00 | -50.00 | / + # 10% on tax 15% excl | 1 | -40.00 | 15% excl | -60.00 | -69.00 | -9.00 + # 10% on tax 35%+50% | 1 | -30.00 | 35% incl | -22.22 | -45.00 | -22.78 + # 50% excl + # -------------------------------------------------------------------------------- + # TOTAL AFTER APPLYING 10% GLOBAL PROGRAM | 1435.46 | 1746.00 | -310.55 + + self.assertEqual(order.amount_total, 1746, "The order total with programs should be 1746") + self.assertEqual(order.amount_untaxed, 1435.46, "The order untaxed total with programs should be 1435.46") + self.assertEqual(len(order.order_line.ids), 12, "Order should contains 5 regular product lines, 3 free product lines and 4 discount lines (one for every tax)") + + # -- This is a test inside the test + order.order_line._compute_tax_id() + self.assertEqual(order.amount_total, 1746, "Recomputing tax on sale order lines should not change total amount") + self.assertEqual(order.amount_untaxed, 1435.46, "Recomputing tax on sale order lines should not change untaxed amount") + self.assertEqual(len(order.order_line.ids), 12, "Recomputing tax on sale order lines should not change number of order line") + order.recompute_coupon_lines() + self.assertEqual(order.amount_total, 1746, "Recomputing tax on sale order lines should not change total amount") + self.assertEqual(order.amount_untaxed, 1435.46, "Recomputing tax on sale order lines should not change untaxed amount") + self.assertEqual(len(order.order_line.ids), 12, "Recomputing tax on sale order lines should not change number of order line") + # -- End test inside the test + + # Now we want to apply a 20% discount only on iPad + self.env['sale.coupon.program'].create({ + 'name': '20% reduction on ipad in cart', + 'promo_code_usage': 'no_code_needed', + 'reward_type': 'discount', + 'program_type': 'promotion_program', + 'discount_type': 'percentage', + 'discount_percentage': 20.0, + 'discount_apply_on': 'specific_product', + 'discount_specific_product_id': self.iPadMini.id, + }) + order.recompute_coupon_lines() + # Note: we have 7 regular ipads and 3 free ipads. We should then discount only 4 really paid iPads + + # Name | Qty | price_unit | Tax | HTVA | TVAC | TVA | + # -------------------------------------------------------------------------------- + # 20% on iPad | 1 | -80.00 | 15% excl | -80.00 | -92.00 | -12.00 + # -------------------------------------------------------------------------------- + # TOTAL AFTER APPLYING 20% ON IPAD | 1355.45 | 1654.00 | -298.55 + + self.assertEqual(order.amount_total, 1654, "The order total with programs should be 1654") + self.assertEqual(order.amount_untaxed, 1355.46, "The order untaxed total with programs should be 1435.45") + self.assertEqual(len(order.order_line.ids), 13, "Order should have a new discount line for 20% on iPad") + + # Check that if you delete one of the discount tax line, the others tax lines from the same promotion got deleted as well. + order.order_line.filtered(lambda l: '10%' in l.name)[0].unlink() + self.assertEqual(len(order.order_line.ids), 9, "All of the 10% discount line per tax should be removed") + # At this point, removing the iPod's discount line (split per tax) removed also the others discount lines + # linked to the same program (eg: other taxes lines). So the coupon got removed from the SO since there were no discount lines left + + # Add back the coupon to continue the test flow + self.env['sale.coupon.apply.code'].sudo().apply_coupon(order, 'test_10pc') + order.recompute_coupon_lines() + self.assertEqual(len(order.order_line.ids), 13, "The 10% discount line should be back") + + # Check that if you change a product qty, his discount tax line got updated + sol2.product_uom_qty = 7 + order.recompute_coupon_lines() + # iPod | 5 | 100.00 | 10% incl | 454.55 | 500.00 | 45.45 + # Free iPod | 2 | -100.00 | 10% incl | -181.82 | -200.00 | -18.18 + # 10% on tax 10% incl | 1 | -30.00 | 10% incl | -27.27 | -30.00 | -2.73 + # -------------------------------------------------------------------------------- + # TOTAL OF IPOD LINES | 245.46 | 270.00 | 24.54 + # ==> Should become: + # iPod | 7 | 100.00 | 10% incl | 636.36 | 700.00 | 63.64 + # Free iPod | 3 | -100.00 | 10% incl | -272.73 | -300.00 | -27.27 + # 10% on tax 10% incl | 1 | -40.00 | 10% incl | -36.36 | -40.00 | -3.64 + # -------------------------------------------------------------------------------- + # TOTAL OF IPOD LINES AFTER ADDING 2 IPODS | 327.27 | 360.00 | 32.73 + # -------------------------------------------------------------------------------- + # => DIFFERENCES BEFORE/AFTER | 81.81 | 90.00 | 8.19 + self.assertEqual(order.amount_untaxed, 1355.46 + 81.81, "The order should have one more paid ipod with 10% incl tax and discounted by 10%") + + # Check that if you remove a product, his reward lines got removed, especially the discount per tax one + sol2.unlink() + order.recompute_coupon_lines() + # Name | Qty | price_unit | Tax | HTVA | TVAC | TVA | + # -------------------------------------------------------------------------------- + # Keyboards | 10 | 100.00 | / | 1000.00 | 1000.00 | / + # iPad | 7 | 100.00 | 15% excl | 700.00 | 805.00 | 105.00 + # Computer Case | 2 | 100.00 | 15% excl | 200.00 | 230.00 | 30.00 + # Product A | 3 | 100.00 | 35% incl | 222.22 | 450.00 | 227.78 + # 50% excl + # Free Keyboard | 5 | -100.00 | / | -500.00 | -500.00 | / + # Free iPad | 3 | -100.00 | 15% excl | -300.00 | -345.00 | -45.00 + # 20% on iPad | 1 | -80.00 | 15% excl | -80.00 | -92.00 | -12.00 + # -------------------------------------------------------------------------------- + # TOTAL | 1242.22 | 1548.00 | 305.78 + self.assertEqual(order.amount_total, 1548, "The order total with programs should be 1548") + self.assertEqual(order.amount_untaxed, 1242.22, "The order untaxed total with programs should be 1242.22") + self.assertEqual(len(order.order_line.ids), 7, "Order should contains 7 lines: 4 products lines, 2 free products lines and a 20% discount line") + + def test_program_numbers_extras(self): + # Check that you can't apply a global discount promo code if there is already an auto applied global discount + self.p1.copy({'promo_code_usage': 'no_code_needed', 'name': 'Auto applied 10% global discount'}) + order = self.empty_order + self.env['sale.order.line'].create({ + 'product_id': self.iPadMini.id, + 'name': 'iPad', + 'product_uom_qty': 1.0, + 'order_id': order.id, + }) + order.recompute_coupon_lines() + self.assertEqual(len(order.order_line.ids), 2, "We should get 1 iPad line and 1 10% auto applied global discount line") + self.assertEqual(order.amount_total, 288, "320$ - 10%") + with self.assertRaises(UserError): + # Can't apply a second global discount + self.env['sale.coupon.apply.code'].with_context(active_id=order.id).create({ + 'coupon_code': 'test_10pc' + }).process_coupon() + + def test_program_fixed_price(self): + # Check fixed amount discount + order = self.empty_order + fixed_amount_program = self.env['sale.coupon.program'].create({ + 'name': '$249 discount', + 'promo_code_usage': 'no_code_needed', + 'program_type': 'promotion_program', + 'discount_type': 'fixed_amount', + 'discount_fixed_amount': 249.0, + }) + sol1 = self.env['sale.order.line'].create({ + 'product_id': self.computerCase.id, + 'name': 'Computer Case', + 'product_uom_qty': 1.0, + 'order_id': order.id, + }) + order.recompute_coupon_lines() + self.assertEqual(order.amount_total, 0, "Total should be null. The fixed amount discount is higher than the SO total, it should be reduced to the SO total") + self.assertEqual(len(order.order_line.ids), 2, "There should be the product line and the reward line") + sol1.product_uom_qty = 17 + order.recompute_coupon_lines() + self.assertEqual(order.amount_total, 176, "Fixed amount discount should be totally deduced") + self.assertEqual(len(order.order_line.ids), 2, "Number of lines should be unchanged as we just recompute the reward line") + fixed_amount_program.write({'active': False}) # Check archived product will remove discount lines on recompute + order.recompute_coupon_lines() + self.assertEqual(len(order.order_line.ids), 1, "Archiving the program should remove the program reward line") |
