This commit complements the removal of pycompat PY2 helpers and shims
from base by removing / adapting references to these helpers from the
enterprise code.
Before this commit:
When a coupon_program's coupon was giving a reward on a SO and then the
recompute was removing that reward (eg the SO did not met the requirements
anymore) the coupon would not be reset and would be lost.
Now:
If a coupon's reward got removed for some reason, the coupon got reset to a
valid state (`new`) so it can be used again.
Step to reproduce:
- Generate a coupon from a coupon program that needs requirements (eg $300)
- Add that coupon to a SO that has a total over $300
- Coupon is set as `used` and the reward is granted
- Remove some products to set the SO total below $300
- Recompute coupon, the reward is correctly removed but the coupon is still
set as `used` so it won't be usable again and is basically lost.
Closes#3062
Before this commit:
If a reward had been given on the SO with a coupon_program code and that the
program had requirements and that these requirements are not met anymore,
recomputing the SO would not remove the reward.
Now:
If the SO does not met the program requirements anymore, we remove the reward
Step to reproduce:
- Create a coupon_program with quantity or amount requirements (eg: $200)
- Generate a coupon for that program
- Add $200 worth of products (matching the program domain) in the SO
- Add the generated coupon to that SO
- You should get the reward as you have $200 in the SO
- Remove some products from the SO to go below $200
- Recompute coupons, the reward won't be removed
Closes#3062
Before this commit:
We would never unvalid a `next_order` program (and thus it's generated coupon)
Now:
We also retrieve applied `next_order` programs as we need to check if they are
still valid too
Step to reproduce:
- Create a program on `next_order` that require $300
- Add $300 worth of product on a SO
- Apply program, it will generate a coupon
- Make the SO total go below $300
- The generated coupon is still Valid and the client will receive that coupon
even if it is not supposed to anymore
Closes#3062
Before this commit:
1. We were checking if the rewards were in the SO even if it was 'next_order'
program.
2. We were deducing the rewards quantity for 'next_order' program when checking
if we had enough products to meet the program requirements.
Now:
1. We don't verify if the reward is on the SO if the program is 'next_order'
2. We don't deduce reward quantity when checking if enough products to meet the
program requirements if the program is 'next_order'
opw-1907211
Closes#3062
Before this commit:
Everytime the SO met a 'on_next_order' program requirements, a coupon would be
generated on that SO. Even if there was already a coupon for that program set to
'expired', for instance when the SO did not met the requirements anymore.
Now:
When the requirements are met, it checks that an `expired` coupon does not
already exist.
It it does, it will set that coupon back to `reserved` state.
Step to reproduce:
- Create a program on `next_order` with requirements, eg $300 amount
- Create a SO and add $300 worth of products
- Update promotion, a coupon is created and set to `reserved`
- Now update the SO to go below $300
- Update promotion, the coupon is set to `expired` as it should
- Update again the SO to meet the $300 requirements again
- Update promotion, a new `reserved` coupon is generated instead of updating
the state of the previous one from `expired` to `reserved`
Closes#3062
This optimizes the confirmation of a sale_order in the ecommerce by ~73%,
for a cart with 160 unique items, the processing time goes from
7m46s to 39s, which is roughly the same amount of processing time if the
module `sale_coupon` is not installed.
This optimization is achieved in two steps:
1. By validating promotional/discounted products in batch instead of
validating each product one at a time.
2. By performing an unlink() on invalid reward lines instead of sending
a delete command by writing to the one2many, as delete commands are not
(yet) performed in batch, which means record recomputation after every
delete.
opw-1893277
This optimizes the confirmation of a sale_order in the ecommerce by ~73%,
for a cart with 160 unique items, the processing time goes from
7m46s to 39s, which is roughly the same amount of processing time if the
module `sale_coupon` is not installed.
This optimization is achieved in two steps:
1. By validating promotional/discounted products in batch instead of
validating each product one at a time.
2. By performing an unlink() on invalid reward lines instead of sending
a delete command by writing to the one2many, as delete commands are not
(yet) performed in batch, which means record recomputation after every
delete.
opw-1893277
closesodoo/enterprise#2905
Steps to reproduce the bug:
- Create a promotion program P with a fixed amount discount
- Set a customer tax T on the discount product generated for P
- Create a SO with one line
- Apply the promotion program by clicking on "Update promotions"
Bug:
The SO line with the discount product is generated but the tax T was missing.
opw:1888484
Purpose
=======
display_name is equal to doing name_get()[0][1]
Replacing name_get()[0][1] by display_name could be good for 2 things:
- Uniformization of the code
- Probable optimization if name_get is used inside a for loop on a recordset (as the values will be prefetched and put in the cache).
TaskID: 1849250
closesodoo/enterprise#2515
Commit 092200073 broke the discount computation for discount of type
fixed_amount. Indeed the appropriate method was not called anymore and such
discount type would be computed as percentage discount with default value of
10% (field default for percentage).
opw-1888484
This commit adapts the business code to changes introduced by
the change to related field's default readonly value in order to
keep the same behaviour as before.
All readonly=False fields will have to be checked afterwards to confirm
that the business case requires write access to the source field.
Purpose of this commit is to give description more "business oriented"
because those descriptions appears in Odoo Studio which is supposed to be used by end users, not only by developers.
Related Task ID : 37311
67c3de295e9 wrongly adapt coupon to multi-websites as website references should
have been written in website_sale_coupon and not sale_coupon.
Closes#2673
Using a pricelist set as `without_discount` will show a strikethrough price on
reward line (discount and free product).
This is happening because the program reward line (discount_line_product_id) is
created with the default 1$ price as this price is not usefull when computing
coupons. Only the line price is checked.
Now, we create a reward line with a -1 000 000$ price to prevent this UI
behavior.
Step to reproduce:
- Activate pricelist (Sales>Config>Settings>Pricelist formulas)
- Activate discounts in Sales Configuration (Manual discount on order lines)
- Set pricelist to 'Show public price & discount to the customer'
- Add a discount in your cart
- There in a strikethrough price of $1 above the discount line price
task-1878245
A discount on products with different taxes will result in as many discount
lines as there is different taxes.
Before this commit, removing one of these lines would not do anything else.
Then, recomputing the coupon would regenerate/add back this line.
On e-commerce, it means you would never be able to remove the program by
deleting its reward lines if there are multiple taxes (and so multiple lines)
since it would `recompute_coupon_lines` right away, adding it back directly.
Now, this commit will ensure that removing one of these lines will also remove
the other ones coming from this program on other taxes.
It means removing a regular product will remove it's reward line that will also
now remove the other program's discount line on other taxes and ultimately will
remove the program from the SO since all the program reward lines got removed.
(It won't affect auto applied program since it will be regenerated afterward)
task-1866977
Related to #2224
Previous commit fixed some bugs on `sale_coupon` (#2224).
This commit add some tests to be sure these bugs are fixed and won't appear
again.
task-1866977
Related to #2224
This commit is the continuity of a42c27f9 that fixed some bugs.
One of the bug was that a discount was only applied on price without taxes.
E.G: A $100 product with 20% excluded tax would cost $120 but a 50% discount
would give the user only $50 discount and not $60 as expected.
User would lose $10 discount.
With these fixes, the discount line would correctly be set to $60 but since the
discount line would not have tax, the merchant would lose the discount amount
on the tax, in this case 50% of $20.
This commit will improve this case by adding a tax on a discount line.
Thus, since a global discount can be applied to multiple products having
different taxes or even multiple taxes on the same product, a discount line
should be split for every product taxes the discount applies on.
Obviously, this is a big behavior change, even if the discount is now finally
correct: the discount is applied on the total price and the merchant does not
lose any taxes.
To make it more clear, the discount lines description will mention on which
taxes it is applied.
There won't be any changes in the e-commerce since we will group the discount
lines so it will appear as before even if there are multiple lines. This is
only possible because we don't show taxes on line on the e-commerce.
This commit also fixes more complex flows/use cases.
Note: Fixes & flows will be illustrated with a SO but the behavior is the
same with a cart on front-end.
== Fixes ==
===========================================================================
The biggest flow that needed to be fixed is when a promotion program give a
reward on the next order.
Let's illustrate with a promotion program giving a free `product_B` on next
order (so it will generate a coupon) if the SO meets the requirements.
The requirements are: buy a `product_A` and buy for at least $600.
(This program needs a code: 'free_b_on_next')
1. If the SO has a `product_A` and over $600, applying the promotion code
`free_b_on_next` will error saying that `product_B` is not in the cart.
This error should not appear, since the `product_B` is the reward for
the next sale order that will use the generated coupon.
Anyway, if you add `product_B` and apply the promotion code `free_b_on_next`
to generate the coupon to check the rest of the flow. It will generate the
coupon and send email to the customer.
2. Every time you will apply the promotion code `free_b_on_next`, it will
regenerate a new coupon and resend a new email to the client.
Then, create a second SO, add 2 `product_B` and try to apply the generated
coupon from the first SO.
3. It will error saying that the $600 requirement is not met.
It should not since the requirement was only required to generate the
coupon on the first SO. User has now a coupon to enjoy a free `product_B`
and should not buy $600.
Anyway, let's add some products to meet the 600$ requirement to check the rest
of the flow.
4. It will now error saying that `product_A` is not in the cart.
As bug 3., it should not since this requirement was also required to
generate the coupon on the first SO.
Anyway, let's add `product_A` to meet the requirement to check the rest of the
flow.
5. Updating the promotions will remove the reward and you won't be able to
reapply the coupon since it was `used`.
This bug was even more critical in front-end since any action on the cart
will recompute the coupons and automatically delete the reward after adding
it. The customer never saw the reward on his cart.
This bug is related to 3. and 4. Indeed, when recomputing coupons, it will
remove programs that are not applicable anymore. The code wrongly think it is
the case with this coupon since we don't have $600 or a `product_A`.
`_remove_invalid_reward_lines` should treat coupon coming from a promotion
program set on `next_order` differently and not filter on requirements.
===========================================================================
6. (extension of 2.) Every time an user would meet the requirements of a
promotion program generating a coupon for a next order, it would generate a
coupon and send an email.
E.g: If buy 2 iPads get 1 for free on next order. If user add and remove iPad
for some reason, every time he has 2 iPads in his cart it will receive
an email with a new coupon.
6'. When applying a promotion code that will reward next order, it will not be
set in the SO `code_promo_program_id` (I did not change that behavior since
it could make sense to be able to apply another coupon that will give -20%
on current order).
This will allow applying multiple promotion rewarding a next order and/or a
promotion rewarding current order. (Which would not be possible if was
stored on `code_promo_program_id` since you can only apply one code on an
order)
But this cause a bug: since the promotion program is not stored on the order,
every time you will apply the coupon it will generate a new coupon and send
an email.
So if you work on a draft sale order and do some tests, it will send many
emails to customer..
7. Having multiple global discounts (on the whole SO) was forbidden: you can't
apply a coupon promotion or a program promotion if there is already a global
coupon discount or a global promotion discount with code. But still you could
apply a global discount if there were a global promotion discount automatically
applied (without a code).
8. On the generate coupon wizard, if you select `Number of Selected Customers`,
by default it is set to 'Match all records (X records)'.
But when you click on generate, it won't generate anything even if visually
it selected every partner by default.
This is because the domain default value is an empty string. Thus, the domain
widget is selecting everything but once it is sent back to the server, since
it's an empty string it won't get through the `if` condition.
It was even weirder when you add something to the domain and remove it, you
then visually come back to where you were at the beginning but the domain got
changed from '' to '[]' so in this case it will generate the coupon for every
partner.
== More like improvements ==
1. `action_cancel` and `action_confirm` would handle correctly the states of the
applied and generated coupons (valid, consumed, expired..).
But `action_draft` would not handle the states at all.
2. The generated coupon on a SO are now sent by email only when the SO is validated.
This is part of the fix n°2 on coupon being generated multiple times and sent by
email multiple times.
The coupon sent by email was not usable anyway since it would error saying the
SO that generated the coupon was not validated.
3. Discount line limited to a max amount (set in the program) will mention on
the line description that the discount is limited.
This will avoid cases where you don't understand why 50% discount of $2000
SO shows $40.
4. If you archive a program and there is still some SO containing this program's
reward line, these line will be 'floating free' and will never be updated or
removed. Indeed, the `_remove_invalid_reward_lines` is basically checking
which programs could be applied on the order and which one are applied on the
order. If a program is applied on the order but is not supposed to, his
reward line will be removed.
Of course, since the program got archived, it won't get retrieve and the line
will never get removed. Even if the case of a free iPad: if you buy an iPad
and you removed the paid iPad from the SO. The free iPad will remain forever.
Note: We can't remove these discount line directly when archiving a program
because there might be quotations that we don't want to modify since it
is waiting for customer confirmation.
Improved behavior:
- Archiving a program archives its related reward product but unarchiving
the program does not unarchived the product.
task-1866977
opw-1832967
opw-1857843
Closes#2224
Comes with https://github.com/odoo/odoo/pull/24971
This commit adds the `website.multi.mixin` to coupon program to make it
multi-websites.
It also adds the website_id field in the views and adapt SQL constraint on code
which now became multi-websites friendly.