Skip to content

Commit 9fda72f

Browse files
author
shopinvader-git-bot
committed
Merge PR #1599 into 16.0
Signed-off-by sebastienbeau
2 parents 6f56cba + 2670223 commit 9fda72f

File tree

9 files changed

+212
-31
lines changed

9 files changed

+212
-31
lines changed

sale_cart/README.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ This addon add a typology on sale.order to distinguish the sale orders that are
2626
in the process of being written (cart) and those that are quotations
2727
ready to be confirmed.
2828

29+
It also automatically converts the cart into a sale when the payment is
30+
authorized, set as pending, or done.
31+
2932
**Table of contents**
3033

3134
.. contents::

sale_cart/models/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1+
from . import payment_transaction
12
from . import sale_order
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# Copyright 2025 Akretion (http://www.akretion.com).
2+
# @author Florian Mounier <[email protected]>
3+
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
4+
5+
from odoo import models
6+
7+
8+
class PaymentTransaction(models.Model):
9+
_inherit = "payment.transaction"
10+
11+
def _get_related_carts(self):
12+
"""Return the carts related to this transaction."""
13+
# We only consider sale orders with typology 'cart'
14+
return self.sale_order_ids.filtered(lambda so: so.typology == "cart")
15+
16+
def _confirm_related_carts(self):
17+
"""Confirm the carts related to this transaction."""
18+
self._get_related_carts().action_confirm_cart()
19+
20+
def _set_authorized(self, state_message=None):
21+
"""Override to set typology to sale when payment is authorized."""
22+
self._confirm_related_carts()
23+
return super()._set_authorized(state_message=state_message)
24+
25+
def _set_pending(self, state_message=None):
26+
"""Override to set typology to cart when payment is pending."""
27+
self._confirm_related_carts()
28+
return super()._set_pending(state_message=state_message)
29+
30+
def _set_done(self, state_message=None):
31+
"""Override to set typology to sale when payment is done."""
32+
self._confirm_related_carts()
33+
return super()._set_done(state_message=state_message)

sale_cart/models/sale_order.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55

66

77
class SaleOrder(models.Model):
8-
98
_inherit = "sale.order"
109

1110
typology = fields.Selection([("sale", "Sale"), ("cart", "Cart")], default="sale")

sale_cart/readme/DESCRIPTION.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
11
This addon add a typology on sale.order to distinguish the sale orders that are
22
in the process of being written (cart) and those that are quotations
33
ready to be confirmed.
4+
5+
It also automatically converts the cart into a sale when the payment is
6+
authorized, set as pending, or done.

sale_cart/static/description/index.html

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
<?xml version="1.0" encoding="utf-8"?>
21
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
32
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
43
<head>
@@ -9,10 +8,11 @@
98

109
/*
1110
:Author: David Goodger ([email protected])
12-
:Id: $Id: html4css1.css 8954 2022-01-20 10:10:25Z milde $
11+
:Id: $Id: html4css1.css 9511 2024-01-13 09:50:07Z milde $
1312
:Copyright: This stylesheet has been placed in the public domain.
1413
1514
Default cascading style sheet for the HTML output of Docutils.
15+
Despite the name, some widely supported CSS2 features are used.
1616
1717
See https://docutils.sourceforge.io/docs/howto/html-stylesheets.html for how to
1818
customize this style sheet.
@@ -275,7 +275,7 @@
275275
margin-left: 2em ;
276276
margin-right: 2em }
277277

278-
pre.code .ln { color: grey; } /* line numbers */
278+
pre.code .ln { color: gray; } /* line numbers */
279279
pre.code, code { background-color: #eeeeee }
280280
pre.code .comment, code .comment { color: #5C6576 }
281281
pre.code .keyword, code .keyword { color: #3B0D06; font-weight: bold }
@@ -301,7 +301,7 @@
301301
span.pre {
302302
white-space: pre }
303303

304-
span.problematic {
304+
span.problematic, pre.problematic {
305305
color: red }
306306

307307
span.section-subtitle {
@@ -373,6 +373,8 @@ <h1 class="title">Sale Cart</h1>
373373
<p>This addon add a typology on sale.order to distinguish the sale orders that are
374374
in the process of being written (cart) and those that are quotations
375375
ready to be confirmed.</p>
376+
<p>It also automatically converts the cart into a sale when the payment is
377+
authorized, set as pending, or done.</p>
376378
<p><strong>Table of contents</strong></p>
377379
<div class="contents local topic" id="contents">
378380
<ul class="simple">

sale_cart/tests/common.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# Copyright 2022 ACSONE SA/NV
2+
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
3+
4+
from odoo.tests.common import TransactionCase
5+
6+
7+
class SaleCartCommon(TransactionCase):
8+
@classmethod
9+
def setUpClass(cls):
10+
super(SaleCartCommon, cls).setUpClass()
11+
cls.env = cls.env(context=dict(cls.env.context, tracking_disable=True))
12+
cls.product = cls.env["product.product"].create(
13+
{
14+
"name": "product",
15+
"uom_id": cls.env.ref("uom.product_uom_unit").id,
16+
}
17+
)
18+
cls.partner = cls.env["res.partner"].create({"name": "partner"})
19+
cls.so_cart = cls.env["sale.order"].create(
20+
{
21+
"partner_id": cls.partner.id,
22+
"order_line": [
23+
(
24+
0,
25+
0,
26+
{"product_id": cls.product.id, "product_uom_qty": 1},
27+
)
28+
],
29+
"typology": "cart",
30+
}
31+
)

sale_cart/tests/test_sale_cart.py

Lines changed: 2 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,11 @@
11
# Copyright 2022 ACSONE SA/NV
22
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
33

4-
from odoo.tests.common import TransactionCase
54

5+
from .common import SaleCartCommon
66

7-
class TestSaleCart(TransactionCase):
8-
@classmethod
9-
def setUpClass(cls):
10-
super(TestSaleCart, cls).setUpClass()
11-
cls.env = cls.env(context=dict(cls.env.context, tracking_disable=True))
12-
cls.product = cls.env["product.product"].create(
13-
{
14-
"name": "product",
15-
"uom_id": cls.env.ref("uom.product_uom_unit").id,
16-
}
17-
)
18-
cls.partner = cls.env["res.partner"].create({"name": "partner"})
19-
cls.so_cart = cls.env["sale.order"].create(
20-
{
21-
"partner_id": cls.partner.id,
22-
"order_line": [
23-
(
24-
0,
25-
0,
26-
{"product_id": cls.product.id, "product_uom_qty": 1},
27-
)
28-
],
29-
"typology": "cart",
30-
}
31-
)
327

8+
class TestSaleCart(SaleCartCommon):
339
def test_action_confirm_cart(self):
3410
self.assertEqual("draft", self.so_cart.state)
3511
self.assertEqual("cart", self.so_cart.typology)
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
# Copyright 2025 Akretion (http://www.akretion.com).
2+
# @author Florian Mounier <[email protected]>
3+
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
4+
5+
from odoo.addons.payment.tests.common import PaymentCommon
6+
7+
from .common import SaleCartCommon
8+
9+
10+
class TestSaleCartPayment(SaleCartCommon, PaymentCommon):
11+
def test_action_confirm_cart_on_transaction_authorized(self):
12+
self.assertEqual("draft", self.so_cart.state)
13+
self.assertEqual("cart", self.so_cart.typology)
14+
transaction = self.env["payment.transaction"].create(
15+
{
16+
"amount": self.so_cart.amount_total,
17+
"currency_id": self.currency.id,
18+
"provider_id": self.provider.id,
19+
"reference": self.so_cart.name,
20+
"operation": "online_redirect",
21+
"partner_id": self.partner.id,
22+
}
23+
)
24+
self.so_cart.transaction_ids |= transaction
25+
self.provider.support_manual_capture = True
26+
transaction._set_authorized()
27+
self.assertEqual("sale", self.so_cart.typology)
28+
# The sale order is confirmed since a transaction with the right amount
29+
# is authorized
30+
self.assertEqual("sale", self.so_cart.state)
31+
32+
def test_action_confirm_cart_on_transaction_pending(self):
33+
self.assertEqual("draft", self.so_cart.state)
34+
self.assertEqual("cart", self.so_cart.typology)
35+
transaction = self.env["payment.transaction"].create(
36+
{
37+
"amount": self.so_cart.amount_total,
38+
"currency_id": self.currency.id,
39+
"provider_id": self.provider.id,
40+
"reference": self.so_cart.name,
41+
"operation": "online_redirect",
42+
"partner_id": self.partner.id,
43+
}
44+
)
45+
self.so_cart.transaction_ids |= transaction
46+
transaction._set_pending()
47+
self.assertEqual("sale", self.so_cart.typology)
48+
# The sale order is set to 'sent' when a transaction is pending
49+
self.assertEqual("sent", self.so_cart.state)
50+
transaction._reconcile_after_done()
51+
self.assertEqual("sent", self.so_cart.state)
52+
53+
def test_action_confirm_cart_on_transaction_done(self):
54+
self.assertEqual("draft", self.so_cart.state)
55+
self.assertEqual("cart", self.so_cart.typology)
56+
transaction = self.env["payment.transaction"].create(
57+
{
58+
"amount": self.so_cart.amount_total,
59+
"currency_id": self.currency.id,
60+
"provider_id": self.provider.id,
61+
"reference": self.so_cart.name,
62+
"operation": "online_redirect",
63+
"partner_id": self.partner.id,
64+
}
65+
)
66+
self.so_cart.transaction_ids |= transaction
67+
transaction._set_done()
68+
self.assertEqual("sale", self.so_cart.typology)
69+
# The sale order is not confirmed instantly on done
70+
self.assertEqual("draft", self.so_cart.state)
71+
# The sale order is confirmed after the post-processing
72+
transaction._reconcile_after_done()
73+
self.assertEqual("sale", self.so_cart.state)
74+
75+
def test_action_confirm_cart_on_transaction_error(self):
76+
self.assertEqual("draft", self.so_cart.state)
77+
self.assertEqual("cart", self.so_cart.typology)
78+
transaction = self.env["payment.transaction"].create(
79+
{
80+
"amount": self.so_cart.amount_total,
81+
"currency_id": self.currency.id,
82+
"provider_id": self.provider.id,
83+
"reference": self.so_cart.name,
84+
"operation": "online_redirect",
85+
"partner_id": self.partner.id,
86+
}
87+
)
88+
self.so_cart.transaction_ids |= transaction
89+
transaction._set_error(state_message="Test error")
90+
# The sale order stays as a cart on error
91+
self.assertEqual("cart", self.so_cart.typology)
92+
# The sale order is not confirmed on error
93+
self.assertEqual("draft", self.so_cart.state)
94+
95+
def test_action_confirm_cart_on_transaction_cancel(self):
96+
self.assertEqual("draft", self.so_cart.state)
97+
self.assertEqual("cart", self.so_cart.typology)
98+
transaction = self.env["payment.transaction"].create(
99+
{
100+
"amount": self.so_cart.amount_total,
101+
"currency_id": self.currency.id,
102+
"provider_id": self.provider.id,
103+
"reference": self.so_cart.name,
104+
"operation": "online_redirect",
105+
"partner_id": self.partner.id,
106+
}
107+
)
108+
self.so_cart.transaction_ids |= transaction
109+
transaction._set_canceled()
110+
# The sale order stays as a cart on cancel
111+
self.assertEqual("cart", self.so_cart.typology)
112+
# The sale order is not confirmed on cancel
113+
self.assertEqual("draft", self.so_cart.state)
114+
115+
def test_action_confirm_cart_on_transaction_authorize_wrong_amount(self):
116+
self.assertEqual("draft", self.so_cart.state)
117+
self.assertEqual("cart", self.so_cart.typology)
118+
transaction = self.env["payment.transaction"].create(
119+
{
120+
"amount": self.so_cart.amount_total + 100, # Wrong amount
121+
"currency_id": self.currency.id,
122+
"provider_id": self.provider.id,
123+
"reference": self.so_cart.name,
124+
"operation": "online_redirect",
125+
"partner_id": self.partner.id,
126+
}
127+
)
128+
self.so_cart.transaction_ids |= transaction
129+
self.provider.support_manual_capture = True
130+
transaction._set_authorized()
131+
self.assertEqual("sale", self.so_cart.typology)
132+
# The sale order is not confirmed since the transaction amount is wrong
133+
self.assertEqual("draft", self.so_cart.state)

0 commit comments

Comments
 (0)