[REF] all: pass pre-commit on the whole repo
Needed for the lint job of the CI to go green. Mostly automatic hook fixes (black, isort, prettier, pyupgrade, autoflake, whitespace). By hand: - flake8: two unused imports, an ambiguous variable name `l`. - pylint-odoo mandatory: manifest keys equal to their default value (installable, application, data, external_dependencies). - end-of-file-fixer skips *.patch too, like trailing-whitespace: the upstream patches stay as git format-patch wrote them. No behaviour change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
d9d9e8be2d
commit
fbd43bb459
45 changed files with 330 additions and 295 deletions
|
|
@ -68,8 +68,9 @@ repos:
|
|||
# lines of a diff are a single space, stripping it breaks them
|
||||
exclude: /README\.rst$|\.pot?$|\.patch$
|
||||
- id: end-of-file-fixer
|
||||
# exclude autogenerated files
|
||||
exclude: /README\.rst$|\.pot?$
|
||||
# exclude autogenerated files, and patches: they are kept as
|
||||
# git format-patch wrote them
|
||||
exclude: /README\.rst$|\.pot?$|\.patch$
|
||||
- id: debug-statements
|
||||
- id: check-case-conflict
|
||||
- id: check-docstring-first
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
from odoo import fields, models
|
||||
from odoo import fields
|
||||
from odoo import models
|
||||
|
||||
|
||||
class ResConfigSettings(models.TransientModel):
|
||||
|
|
|
|||
|
|
@ -3,7 +3,8 @@
|
|||
import logging
|
||||
from datetime import timedelta
|
||||
|
||||
from odoo import fields, models
|
||||
from odoo import fields
|
||||
from odoo import models
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
|
|
|||
|
|
@ -13,7 +13,4 @@
|
|||
"membership_variable_period",
|
||||
"account",
|
||||
],
|
||||
"data": [],
|
||||
"installable": True,
|
||||
"application": False,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,8 @@
|
|||
import math
|
||||
from datetime import timedelta
|
||||
|
||||
from odoo import fields, models
|
||||
from odoo import fields
|
||||
from odoo import models
|
||||
|
||||
|
||||
class AccountMove(models.Model):
|
||||
|
|
@ -74,7 +75,11 @@ class AccountMove(models.Model):
|
|||
return fields.Date.today()
|
||||
|
||||
def _compute_membership_dates(
|
||||
self, partner, product, payment_date, quantity=1.0,
|
||||
self,
|
||||
partner,
|
||||
product,
|
||||
payment_date,
|
||||
quantity=1.0,
|
||||
exclude_line_ids=None,
|
||||
):
|
||||
"""Calculate date_from and date_to for a membership line.
|
||||
|
|
|
|||
|
|
@ -4,11 +4,10 @@
|
|||
from datetime import date
|
||||
|
||||
from freezegun import freeze_time
|
||||
|
||||
from psycopg2 import ProgrammingError
|
||||
|
||||
from odoo import fields
|
||||
from odoo.tests import Form, common
|
||||
from odoo.tests import Form
|
||||
from odoo.tests import common
|
||||
|
||||
|
||||
@freeze_time("2026-01-15")
|
||||
|
|
@ -123,9 +122,7 @@ class TestMembershipPaymentDate(common.TransactionCase):
|
|||
def _create_invoice(self, product, invoice_date, quantity=1.0):
|
||||
"""Create and return a draft out_invoice with the given product."""
|
||||
invoice_form = Form(
|
||||
self.env["account.move"].with_context(
|
||||
default_move_type="out_invoice"
|
||||
)
|
||||
self.env["account.move"].with_context(default_move_type="out_invoice")
|
||||
)
|
||||
invoice_form.invoice_date = invoice_date
|
||||
invoice_form.partner_id = self.partner
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
from odoo import _, http
|
||||
from odoo.exceptions import AccessError, MissingError
|
||||
from odoo import _
|
||||
from odoo import http
|
||||
from odoo.exceptions import AccessError
|
||||
from odoo.exceptions import MissingError
|
||||
from odoo.http import request
|
||||
|
||||
from odoo.addons.portal.controllers.portal import CustomerPortal
|
||||
|
|
|
|||
|
|
@ -22,8 +22,7 @@ patch(TicketScreen.prototype, {
|
|||
for (const toRefundDetail of toRefundDetails) {
|
||||
// Per line, and net of what was already refunded: the same
|
||||
// guard the core applies in `_onUpdateSelectedOrderline`.
|
||||
const refundableQty =
|
||||
toRefundDetail.line.qty - toRefundDetail.line.refunded_qty;
|
||||
const refundableQty = toRefundDetail.line.qty - toRefundDetail.line.refunded_qty;
|
||||
if (refundableQty > 0) {
|
||||
toRefundDetail.qty = refundableQty;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,9 +32,6 @@ registry.category("web_tour.tours").add("pos_full_refund_tour", {
|
|||
|
||||
{ ...ProductScreen.back(), isActive: ["mobile"] },
|
||||
ProductScreen.isShown(),
|
||||
inLeftSide([
|
||||
...Order.hasLine("Desk Pad", "-2"),
|
||||
...Order.hasLine("Letter Tray", "-3"),
|
||||
]),
|
||||
inLeftSide([...Order.hasLine("Desk Pad", "-2"), ...Order.hasLine("Letter Tray", "-3")]),
|
||||
].flat(),
|
||||
});
|
||||
|
|
|
|||
|
|
@ -19,5 +19,4 @@
|
|||
"pos_payment_method_cashdro_fix/static/src/js/models_fix.esm.js",
|
||||
],
|
||||
},
|
||||
"installable": True,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,4 @@
|
|||
"views/res_config_settings_views.xml",
|
||||
"views/account_move_views.xml",
|
||||
],
|
||||
"installable": True,
|
||||
"application": False,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
# Copyright 2024 Criptomart
|
||||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
|
||||
from odoo import api, fields, models
|
||||
from odoo import api
|
||||
from odoo import fields
|
||||
from odoo import models
|
||||
|
||||
|
||||
class AccountMove(models.Model):
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
# Copyright 2024 Criptomart
|
||||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
|
||||
from odoo import fields, models
|
||||
from odoo import fields
|
||||
from odoo import models
|
||||
|
||||
|
||||
class ResCompany(models.Model):
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
# Copyright 2024 Criptomart
|
||||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
|
||||
from odoo import api, fields, models
|
||||
from odoo import fields
|
||||
from odoo import models
|
||||
|
||||
|
||||
class ResConfigSettings(models.TransientModel):
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
# Copyright 2024 Criptomart
|
||||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
|
||||
from odoo import fields, models
|
||||
from odoo import fields
|
||||
from odoo import models
|
||||
|
||||
|
||||
class ResPartner(models.Model):
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
from odoo import fields
|
||||
from odoo.exceptions import UserError
|
||||
from odoo.tests import tagged
|
||||
|
||||
from odoo.addons.account.tests.common import AccountTestInvoicingCommon
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,10 @@
|
|||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
|
||||
from collections import defaultdict
|
||||
|
||||
from odoo import _, api, fields, models
|
||||
from odoo import _
|
||||
from odoo import api
|
||||
from odoo import fields
|
||||
from odoo import models
|
||||
from odoo.exceptions import UserError
|
||||
|
||||
|
||||
|
|
@ -52,8 +55,7 @@ class AccountMoveRappelWizard(models.TransientModel):
|
|||
|
||||
# Filter only posted vendor bills and refunds
|
||||
valid_moves = moves.filtered(
|
||||
lambda m: m.move_type in ("in_invoice", "in_refund")
|
||||
and m.state == "posted"
|
||||
lambda m: m.move_type in ("in_invoice", "in_refund") and m.state == "posted"
|
||||
)
|
||||
if not valid_moves:
|
||||
raise UserError(
|
||||
|
|
|
|||
|
|
@ -13,5 +13,4 @@
|
|||
"membership",
|
||||
"website_membership",
|
||||
],
|
||||
"data": [],
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
# Copyright 2026 Criptomart
|
||||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
|
||||
|
||||
from odoo import fields, http
|
||||
from odoo import fields
|
||||
from odoo import http
|
||||
from odoo.http import request
|
||||
from odoo.tools.translate import _
|
||||
|
||||
|
|
@ -28,9 +29,7 @@ class WebsiteMembership(BaseWebsiteMembership):
|
|||
self._add_associate_members(response, membership_id, country_id, page, post)
|
||||
return response
|
||||
|
||||
def _add_associate_members(
|
||||
self, response, membership_id, country_id, page, post
|
||||
):
|
||||
def _add_associate_members(self, response, membership_id, country_id, page, post):
|
||||
values = response.qcontext
|
||||
memberships_partner_ids = values.get("memberships_partner_ids", {})
|
||||
if not memberships_partner_ids:
|
||||
|
|
@ -67,25 +66,19 @@ class WebsiteMembership(BaseWebsiteMembership):
|
|||
if associate.id not in new_memberships_partner_ids[key]:
|
||||
new_memberships_partner_ids[key].append(associate.id)
|
||||
values["memberships_partner_ids"] = {
|
||||
key: ids
|
||||
for key, ids in new_memberships_partner_ids.items()
|
||||
if ids
|
||||
key: ids for key, ids in new_memberships_partner_ids.items() if ids
|
||||
}
|
||||
|
||||
displayed_ids = set(
|
||||
pid
|
||||
for ids in values["memberships_partner_ids"].values()
|
||||
for pid in ids
|
||||
)
|
||||
displayed_ids = {
|
||||
pid for ids in values["memberships_partner_ids"].values() for pid in ids
|
||||
}
|
||||
values["partners"] = {
|
||||
p.id: p
|
||||
for p in request.env["res.partner"]
|
||||
.sudo()
|
||||
.browse(list(displayed_ids))
|
||||
for p in request.env["res.partner"].sudo().browse(list(displayed_ids))
|
||||
}
|
||||
|
||||
all_host_ids, all_free_host_ids, all_associates = (
|
||||
self._get_all_visible_ids(membership_id, country_id, post_name)
|
||||
all_host_ids, all_free_host_ids, all_associates = self._get_all_visible_ids(
|
||||
membership_id, country_id, post_name
|
||||
)
|
||||
self._recompute_countries(
|
||||
values, all_host_ids, all_free_host_ids, all_associates, country_id
|
||||
|
|
@ -134,9 +127,7 @@ class WebsiteMembership(BaseWebsiteMembership):
|
|||
if membership_id == "free":
|
||||
all_host_ids = set()
|
||||
else:
|
||||
all_host_ids = self._get_all_host_ids(
|
||||
membership_id, country_id, post_name
|
||||
)
|
||||
all_host_ids = self._get_all_host_ids(membership_id, country_id, post_name)
|
||||
all_free_host_ids = (
|
||||
self._get_all_free_host_ids(country_id, post_name)
|
||||
if free_selected
|
||||
|
|
@ -214,8 +205,7 @@ class WebsiteMembership(BaseWebsiteMembership):
|
|||
if country_id:
|
||||
current_country = Country.browse(country_id).read(["id", "name"])[0]
|
||||
if not any(
|
||||
x["country_id"] and x["country_id"][0] == country_id
|
||||
for x in countries
|
||||
x["country_id"] and x["country_id"][0] == country_id for x in countries
|
||||
):
|
||||
countries.append(
|
||||
{
|
||||
|
|
@ -243,16 +233,12 @@ class WebsiteMembership(BaseWebsiteMembership):
|
|||
return
|
||||
partners = request.env["res.partner"].sudo().browse(map_ids)
|
||||
if country_id:
|
||||
partners = partners.filtered(
|
||||
lambda p: p.country_id.id == country_id
|
||||
)
|
||||
partners = partners.filtered(lambda p: p.country_id.id == country_id)
|
||||
if post_name:
|
||||
partners = partners.filtered(
|
||||
lambda p: self._partner_matches_search(p, post_name)
|
||||
)
|
||||
values["google_map_partner_ids"] = ",".join(
|
||||
str(pid) for pid in partners.ids
|
||||
)
|
||||
values["google_map_partner_ids"] = ",".join(str(pid) for pid in partners.ids)
|
||||
|
||||
def _update_pager(
|
||||
self,
|
||||
|
|
@ -268,9 +254,7 @@ class WebsiteMembership(BaseWebsiteMembership):
|
|||
if membership_id == "free":
|
||||
displayed_host_ids = set()
|
||||
else:
|
||||
host_records = request.env["res.partner"].sudo().browse(
|
||||
list(all_host_ids)
|
||||
)
|
||||
host_records = request.env["res.partner"].sudo().browse(list(all_host_ids))
|
||||
displayed_host_ids = {
|
||||
host.id
|
||||
for host in host_records
|
||||
|
|
@ -278,7 +262,7 @@ class WebsiteMembership(BaseWebsiteMembership):
|
|||
}
|
||||
count_members = len(displayed_host_ids) + len(all_free_host_ids)
|
||||
limit = self._references_per_page
|
||||
base_url = "/members%s%s" % (
|
||||
base_url = "/members{}{}".format(
|
||||
"/association/%s" % membership_id if membership_id else "",
|
||||
"/country/%s" % country_id if country_id else "",
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
# Copyright 2026 Criptomart
|
||||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
|
||||
|
||||
from odoo import api, models
|
||||
from odoo import api
|
||||
from odoo import models
|
||||
from odoo.osv import expression
|
||||
|
||||
|
||||
|
|
@ -41,10 +42,7 @@ class MembershipLine(models.Model):
|
|||
("website_description", "ilike", post_name),
|
||||
]
|
||||
host_ids = (
|
||||
self.env["res.partner"]
|
||||
.sudo()
|
||||
.search(associate_domain)
|
||||
.associate_member.ids
|
||||
self.env["res.partner"].sudo().search(associate_domain).associate_member.ids
|
||||
)
|
||||
if not host_ids:
|
||||
return domain
|
||||
|
|
@ -84,8 +82,7 @@ class MembershipLine(models.Model):
|
|||
AND p.is_company
|
||||
AND m.id IN %s
|
||||
ORDER BY p.id
|
||||
"""
|
||||
+ limit_clause,
|
||||
""" + limit_clause,
|
||||
(tuple(self.ids),),
|
||||
)
|
||||
return [row[0] for row in self.env.cr.fetchall()]
|
||||
|
|
|
|||
|
|
@ -2,7 +2,8 @@
|
|||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
from odoo import fields
|
||||
from odoo.tests import HttpCase, tagged
|
||||
from odoo.tests import HttpCase
|
||||
from odoo.tests import tagged
|
||||
|
||||
from odoo.addons.membership.tests.common import TestMembershipCommon
|
||||
|
||||
|
|
@ -125,9 +126,7 @@ class TestWebsiteMembershipAssociatePagination(HttpCase, TestMembershipCommon):
|
|||
for i in range(20)
|
||||
]
|
||||
cls.paginated_hosts = cls.env["res.partner"].create(host_vals)
|
||||
invoices = cls.paginated_hosts.create_membership_invoice(
|
||||
cls.membership_1, 75.0
|
||||
)
|
||||
invoices = cls.paginated_hosts.create_membership_invoice(cls.membership_1, 75.0)
|
||||
invoices.action_post()
|
||||
cls.env["account.payment.register"].with_context(
|
||||
active_model="account.move", active_ids=invoices.ids
|
||||
|
|
|
|||
|
|
@ -25,7 +25,4 @@
|
|||
"website_membership_signup_required/static/src/js/website_membership_signup_required.js",
|
||||
],
|
||||
},
|
||||
"external_dependencies": {},
|
||||
"installable": True,
|
||||
"application": False,
|
||||
}
|
||||
|
|
@ -1,11 +1,13 @@
|
|||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
from odoo import _, http
|
||||
from odoo.addons.auth_signup.controllers.main import AuthSignupHome
|
||||
from odoo.addons.web.controllers.home import SIGN_UP_REQUEST_PARAMS
|
||||
from odoo import _
|
||||
from odoo import http
|
||||
from odoo.exceptions import UserError
|
||||
from odoo.http import request
|
||||
|
||||
from odoo.addons.auth_signup.controllers.main import AuthSignupHome
|
||||
from odoo.addons.web.controllers.home import SIGN_UP_REQUEST_PARAMS
|
||||
|
||||
# partner_firstname exposes `firstname` / `lastname` on res.users and
|
||||
# res.partner; we need them as accepted qcontext params so the signup form
|
||||
# roundtrips them transparently through `get_auth_signup_qcontext`.
|
||||
|
|
@ -18,7 +20,10 @@ SIGN_UP_REQUEST_PARAMS.add("lastname")
|
|||
SIGN_UP_REQUEST_PARAMS.add("accepted_terms")
|
||||
|
||||
# Session keys used by the membership signup flow.
|
||||
MEMBERSHIP_SIGNUP_SESSION_KEYS = ("membership_signup_active", "membership_signup_product_id")
|
||||
MEMBERSHIP_SIGNUP_SESSION_KEYS = (
|
||||
"membership_signup_active",
|
||||
"membership_signup_product_id",
|
||||
)
|
||||
|
||||
|
||||
class AuthSignupHomeMembership(AuthSignupHome):
|
||||
|
|
@ -71,9 +76,7 @@ class AuthSignupHomeMembership(AuthSignupHome):
|
|||
values["firstname"] = firstname
|
||||
values["lastname"] = lastname
|
||||
partner_model = request.env["res.partner"]
|
||||
values["name"] = partner_model._get_computed_name(
|
||||
lastname, firstname
|
||||
)
|
||||
values["name"] = partner_model._get_computed_name(lastname, firstname)
|
||||
return values
|
||||
|
||||
@http.route()
|
||||
|
|
|
|||
|
|
@ -3,9 +3,10 @@
|
|||
from urllib.parse import urlencode
|
||||
|
||||
from odoo import http
|
||||
from odoo.addons.website_sale.controllers.main import WebsiteSale
|
||||
from odoo.http import request
|
||||
|
||||
from odoo.addons.website_sale.controllers.main import WebsiteSale
|
||||
|
||||
SIGNUP_FLAG = "membership_signup=1"
|
||||
|
||||
|
||||
|
|
@ -36,24 +37,37 @@ class WebsiteSaleMembershipSignup(WebsiteSale):
|
|||
|
||||
@http.route()
|
||||
def cart_update(
|
||||
self, product_id, add_qty=1, set_qty=0,
|
||||
self,
|
||||
product_id,
|
||||
add_qty=1,
|
||||
set_qty=0,
|
||||
product_custom_attribute_values=None,
|
||||
no_variant_attribute_value_ids=None, **kwargs
|
||||
no_variant_attribute_value_ids=None,
|
||||
**kwargs,
|
||||
):
|
||||
if self._membership_signup_is_required(product_id):
|
||||
self._membership_signup_mark_session(product_id)
|
||||
return request.redirect(self._membership_signup_redirect_url(product_id))
|
||||
return super().cart_update(
|
||||
product_id=product_id, add_qty=add_qty, set_qty=set_qty,
|
||||
product_id=product_id,
|
||||
add_qty=add_qty,
|
||||
set_qty=set_qty,
|
||||
product_custom_attribute_values=product_custom_attribute_values,
|
||||
no_variant_attribute_value_ids=no_variant_attribute_value_ids,
|
||||
**kwargs
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
@http.route()
|
||||
def cart_update_json(
|
||||
self, product_id, line_id=None, add_qty=None, set_qty=None, display=True,
|
||||
product_custom_attribute_values=None, no_variant_attribute_value_ids=None, **kwargs
|
||||
self,
|
||||
product_id,
|
||||
line_id=None,
|
||||
add_qty=None,
|
||||
set_qty=None,
|
||||
display=True,
|
||||
product_custom_attribute_values=None,
|
||||
no_variant_attribute_value_ids=None,
|
||||
**kwargs,
|
||||
):
|
||||
if self._membership_signup_is_required(product_id):
|
||||
self._membership_signup_mark_session(product_id)
|
||||
|
|
@ -90,11 +104,14 @@ class WebsiteSaleMembershipSignup(WebsiteSale):
|
|||
"notification_info": {"lines": [], "warning": ""},
|
||||
}
|
||||
return super().cart_update_json(
|
||||
product_id=product_id, line_id=line_id, add_qty=add_qty,
|
||||
set_qty=set_qty, display=display,
|
||||
product_id=product_id,
|
||||
line_id=line_id,
|
||||
add_qty=add_qty,
|
||||
set_qty=set_qty,
|
||||
display=display,
|
||||
product_custom_attribute_values=product_custom_attribute_values,
|
||||
no_variant_attribute_value_ids=no_variant_attribute_value_ids,
|
||||
**kwargs
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
def _get_mandatory_address_fields(self, country_sudo):
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
from odoo import fields, models
|
||||
from odoo import fields
|
||||
from odoo import models
|
||||
|
||||
|
||||
class ResConfigSettings(models.TransientModel):
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
from odoo import api, models
|
||||
from odoo import api
|
||||
from odoo import models
|
||||
|
||||
|
||||
class ResPartner(models.Model):
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
from odoo import api, models
|
||||
from odoo import api
|
||||
from odoo import models
|
||||
from odoo.http import request
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
from odoo import fields, models
|
||||
from odoo import fields
|
||||
from odoo import models
|
||||
|
||||
|
||||
class Website(models.Model):
|
||||
|
|
|
|||
|
|
@ -64,8 +64,9 @@ registry.category("web_tour.tours").add("website_membership_signup_required_tour
|
|||
run: "click",
|
||||
},
|
||||
{
|
||||
content: "Submit the signup form -> redirect straight to cart "
|
||||
+ "with the product already added (post-login auto-add)",
|
||||
content:
|
||||
"Submit the signup form -> redirect straight to cart " +
|
||||
"with the product already added (post-login auto-add)",
|
||||
trigger: ".oe_signup_form button[type='submit']",
|
||||
run: "click",
|
||||
expectUnloadPage: true,
|
||||
|
|
|
|||
|
|
@ -4,15 +4,16 @@ import werkzeug.datastructures
|
|||
|
||||
from odoo.exceptions import UserError
|
||||
from odoo.fields import Date
|
||||
from odoo.tests import TransactionCase
|
||||
from odoo.tests import tagged
|
||||
|
||||
from odoo.addons.website.tools import MockRequest
|
||||
from odoo.addons.website_membership_signup_required.controllers.main import (
|
||||
AuthSignupHomeMembership as AuthSignupHome,
|
||||
)
|
||||
from odoo.addons.website_membership_signup_required.controllers.website_sale import (
|
||||
WebsiteSaleMembershipSignup,
|
||||
)
|
||||
from odoo.addons.website.tools import MockRequest
|
||||
from odoo.tests import TransactionCase, tagged
|
||||
|
||||
|
||||
@tagged("post_install", "-at_install")
|
||||
|
|
@ -24,22 +25,28 @@ class TestMembershipSignup(TransactionCase):
|
|||
cls.website = cls.env.ref("website.default_website")
|
||||
cls.website.auth_signup_uninvited = "b2b"
|
||||
today = Date.to_date(Date.today())
|
||||
cls.membership_product = cls.env["product.product"].create({
|
||||
cls.membership_product = cls.env["product.product"].create(
|
||||
{
|
||||
"name": "Membership Card",
|
||||
"membership": True,
|
||||
"list_price": 50.0,
|
||||
"membership_date_from": today.replace(month=1, day=1),
|
||||
"membership_date_to": today.replace(month=12, day=31),
|
||||
})
|
||||
cls.regular_product = cls.env["product.product"].create({
|
||||
}
|
||||
)
|
||||
cls.regular_product = cls.env["product.product"].create(
|
||||
{
|
||||
"name": "Regular Mug",
|
||||
"membership": False,
|
||||
"list_price": 10.0,
|
||||
})
|
||||
cls.no_user_partner = cls.env["res.partner"].create({
|
||||
}
|
||||
)
|
||||
cls.no_user_partner = cls.env["res.partner"].create(
|
||||
{
|
||||
"firstname": "Backend",
|
||||
"lastname": "Partner",
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
# -- RF-07 / RF-default ------------------------------------------------
|
||||
|
||||
|
|
@ -198,13 +205,21 @@ class TestMembershipSignup(TransactionCase):
|
|||
# -- RF-08 -------------------------------------------------------------
|
||||
|
||||
def test_08_backend_sale_order_with_membership_product_without_user(self):
|
||||
order = self.env["sale.order"].create({
|
||||
order = self.env["sale.order"].create(
|
||||
{
|
||||
"partner_id": self.no_user_partner.id,
|
||||
"order_line": [(0, 0, {
|
||||
"order_line": [
|
||||
(
|
||||
0,
|
||||
0,
|
||||
{
|
||||
"product_id": self.membership_product.id,
|
||||
"product_uom_qty": 1.0,
|
||||
})],
|
||||
})
|
||||
},
|
||||
)
|
||||
],
|
||||
}
|
||||
)
|
||||
self.assertTrue(order)
|
||||
self.assertEqual(order.order_line.product_id, self.membership_product)
|
||||
# Confirming the SO drives membership creation (core `membership`).
|
||||
|
|
@ -276,12 +291,14 @@ class TestMembershipSignup(TransactionCase):
|
|||
The invited partner must NOT have a user yet, otherwise the token
|
||||
payload check (`user_ids`) won't match.
|
||||
"""
|
||||
partner = self.env["res.partner"].create({
|
||||
partner = self.env["res.partner"].create(
|
||||
{
|
||||
"firstname": "Invited",
|
||||
"lastname": "Researcher",
|
||||
"email": "invited.researcher@example.com",
|
||||
"signup_type": "signup",
|
||||
})
|
||||
}
|
||||
)
|
||||
self.assertFalse(partner.user_ids, "test precondition: no user yet")
|
||||
token = partner._generate_signup_token()
|
||||
info = self.env["res.partner"]._signup_retrieve_info(token)
|
||||
|
|
@ -297,11 +314,13 @@ class TestMembershipSignup(TransactionCase):
|
|||
still works — the form will show empty readonly inputs for the split
|
||||
fields, which is the best we can do without re-splitting.
|
||||
"""
|
||||
partner = self.env["res.partner"].create({
|
||||
partner = self.env["res.partner"].create(
|
||||
{
|
||||
"name": "Legacy Researcher",
|
||||
"email": "legacy@example.com",
|
||||
"signup_type": "signup",
|
||||
})
|
||||
}
|
||||
)
|
||||
# partner_firstname's post_create hook may or may not have split
|
||||
# `name` automatically; we just check the override does not crash
|
||||
# and returns the contract keys.
|
||||
|
|
|
|||
|
|
@ -7,8 +7,8 @@ import time
|
|||
from urllib.parse import unquote
|
||||
|
||||
from odoo.fields import Date
|
||||
|
||||
from odoo.tests import HttpCase, tagged
|
||||
from odoo.tests import HttpCase
|
||||
from odoo.tests import tagged
|
||||
|
||||
|
||||
@tagged("post_install", "-at_install")
|
||||
|
|
@ -21,7 +21,8 @@ class TestMembershipSignupTour(HttpCase):
|
|||
cls.website.auth_signup_uninvited = "b2b"
|
||||
cls.website.membership_signup_required = True
|
||||
today = Date.to_date(Date.today())
|
||||
cls.membership_product = cls.env["product.template"].create({
|
||||
cls.membership_product = cls.env["product.template"].create(
|
||||
{
|
||||
"name": "Membership Test Product",
|
||||
"membership": True,
|
||||
"list_price": 60.0,
|
||||
|
|
@ -30,19 +31,24 @@ class TestMembershipSignupTour(HttpCase):
|
|||
"website_id": cls.website.id,
|
||||
"membership_date_from": today.replace(month=1, day=1),
|
||||
"membership_date_to": today.replace(month=12, day=31),
|
||||
})
|
||||
cls.regular_product = cls.env["product.template"].create({
|
||||
}
|
||||
)
|
||||
cls.regular_product = cls.env["product.template"].create(
|
||||
{
|
||||
"name": "Regular Test Product",
|
||||
"membership": False,
|
||||
"list_price": 5.0,
|
||||
"is_published": True,
|
||||
"sale_ok": True,
|
||||
"website_id": cls.website.id,
|
||||
})
|
||||
}
|
||||
)
|
||||
# Enable the custom/transfer payment provider if it is installed so the
|
||||
# cart page renders fully (no checkout/payment is exercised by the
|
||||
# tours, but it avoids warnings during cart rendering).
|
||||
provider = cls.env.ref("payment.payment_provider_transfer", raise_if_not_found=False)
|
||||
provider = cls.env.ref(
|
||||
"payment.payment_provider_transfer", raise_if_not_found=False
|
||||
)
|
||||
if provider and provider.state != "enabled":
|
||||
provider.write({"state": "enabled", "is_published": True})
|
||||
|
||||
|
|
@ -54,20 +60,24 @@ class TestMembershipSignupTour(HttpCase):
|
|||
)
|
||||
# The tour submitted the signup form, so a new portal user must exist
|
||||
# and the cart of that user must contain the membership product.
|
||||
users = self.env["res.users"].search([
|
||||
users = self.env["res.users"].search(
|
||||
[
|
||||
("login", "like", "membership.tour.%@test.example.com"),
|
||||
])
|
||||
]
|
||||
)
|
||||
self.assertTrue(users, "Tour should have created a user via /web/signup")
|
||||
first = users.sorted("create_date")[-1]
|
||||
self.assertTrue(first.partner_id.firstname)
|
||||
self.assertTrue(first.partner_id.lastname)
|
||||
carts = self.env["sale.order"].search([
|
||||
carts = self.env["sale.order"].search(
|
||||
[
|
||||
("partner_id", "=", first.partner_id.id),
|
||||
("state", "=", "draft"),
|
||||
])
|
||||
]
|
||||
)
|
||||
self.assertTrue(carts)
|
||||
order_lines = carts.mapped("order_line").filtered(
|
||||
lambda l: l.product_id.product_tmpl_id == self.membership_product
|
||||
lambda line: line.product_id.product_tmpl_id == self.membership_product
|
||||
)
|
||||
self.assertTrue(order_lines, "Cart must contain the membership product")
|
||||
|
||||
|
|
@ -93,7 +103,7 @@ class TestMembershipSignupTour(HttpCase):
|
|||
return None
|
||||
secret = self.env["ir.config_parameter"].sudo().get_param("database.secret")
|
||||
max_ts = int(time.time() + 31536000) # 1y, matches Odoo's default.
|
||||
msg = f"{sid}{max_ts}".encode("utf-8")
|
||||
msg = f"{sid}{max_ts}".encode()
|
||||
hm = hmac.new(secret.encode("ascii"), msg, hashlib.sha1).hexdigest()
|
||||
return f"{hm}o{max_ts}"
|
||||
|
||||
|
|
@ -106,8 +116,10 @@ class TestMembershipSignupTour(HttpCase):
|
|||
product_url = "/shop/%s" % self.env["ir.http"]._slug(product)
|
||||
page = self.url_open(product_url, allow_redirects=True)
|
||||
self.assertEqual(
|
||||
page.status_code, 200,
|
||||
"Product page must render (got %s for %s)" % (page.status_code, product_url),
|
||||
page.status_code,
|
||||
200,
|
||||
"Product page must render (got %s for %s)"
|
||||
% (page.status_code, product_url),
|
||||
)
|
||||
match = self._CSRF_RE.search(page.text)
|
||||
csrf_token = match.group(1) if match else self._compute_csrf_token()
|
||||
|
|
|
|||
|
|
@ -18,5 +18,4 @@
|
|||
"views/res_config_settings_views.xml",
|
||||
"views/website_sale_disable_cart_templates.xml",
|
||||
],
|
||||
"installable": True,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,5 +24,4 @@
|
|||
"website_sale_lazy_loading/static/src/js/website_sale_lazy_loading.js",
|
||||
],
|
||||
},
|
||||
"installable": True,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -76,7 +76,7 @@ class WebsiteSaleLazyLoading(WebsiteSale):
|
|||
query = urlencode(params, doseq=True)
|
||||
if not query:
|
||||
return LAZY_LOADING_ROUTE
|
||||
return "%s?%s" % (LAZY_LOADING_ROUTE, query)
|
||||
return f"{LAZY_LOADING_ROUTE}?{query}"
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# AJAX endpoint
|
||||
|
|
|
|||
|
|
@ -47,10 +47,10 @@ class TestLazyLoading(HttpCase):
|
|||
|
||||
def _get_lazy_page(self, page, **params):
|
||||
"""Call the AJAX endpoint and return its decoded payload."""
|
||||
query = "&".join("%s=%s" % (key, value) for key, value in params.items())
|
||||
query = "&".join(f"{key}={value}" for key, value in params.items())
|
||||
url = "/shop/lazy_products?page=%s" % page
|
||||
if query:
|
||||
url = "%s&%s" % (url, query)
|
||||
url = f"{url}&{query}"
|
||||
response = self.url_open(url)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
return response.json()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue