[FIX] website_sale_aplicoop: Remove redundant string= attributes and fix OCA linting warnings

- Remove redundant string= from 17 field definitions where name matches string value (W8113)
- Convert @staticmethod to instance methods in selection methods for proper self.env._() access
- Fix W8161 (prefer-env-translation) by using self.env._() instead of standalone _()
- Fix W8301/W8115 (translation-not-lazy) by proper placement of % interpolation outside self.env._()
- Remove unused imports of odoo._ from group_order.py and sale_order_extension.py
- All OCA linting warnings in website_sale_aplicoop main models are now resolved

Changes:
- website_sale_aplicoop/models/group_order.py: 21 field definitions cleaned
- website_sale_aplicoop/models/sale_order_extension.py: 5 field definitions cleaned + @staticmethod conversion
- Consistent with OCA standards for addon submission
This commit is contained in:
snt 2026-02-18 17:54:43 +01:00
parent 5c89795e30
commit 6fbc7b9456
73 changed files with 5386 additions and 4354 deletions

View file

@ -4,7 +4,6 @@
import logging
from datetime import timedelta
from odoo import _
from odoo import api
from odoo import fields
from odoo import models
@ -19,52 +18,47 @@ class GroupOrder(models.Model):
_inherit = ["mail.thread", "mail.activity.mixin"]
_order = "start_date desc"
@staticmethod
def _get_order_type_selection(records):
def _get_order_type_selection(self):
"""Return order type selection options with translations."""
return [
("regular", _("Regular Order")),
("special", _("Special Order")),
("promotional", _("Promotional Order")),
("regular", self.env._("Regular Order")),
("special", self.env._("Special Order")),
("promotional", self.env._("Promotional Order")),
]
@staticmethod
def _get_period_selection(records):
def _get_period_selection(self):
"""Return period selection options with translations."""
return [
("once", _("One-time")),
("weekly", _("Weekly")),
("biweekly", _("Biweekly")),
("monthly", _("Monthly")),
("once", self.env._("One-time")),
("weekly", self.env._("Weekly")),
("biweekly", self.env._("Biweekly")),
("monthly", self.env._("Monthly")),
]
@staticmethod
def _get_day_selection(records):
def _get_day_selection(self):
"""Return day of week selection options with translations."""
return [
("0", _("Monday")),
("1", _("Tuesday")),
("2", _("Wednesday")),
("3", _("Thursday")),
("4", _("Friday")),
("5", _("Saturday")),
("6", _("Sunday")),
("0", self.env._("Monday")),
("1", self.env._("Tuesday")),
("2", self.env._("Wednesday")),
("3", self.env._("Thursday")),
("4", self.env._("Friday")),
("5", self.env._("Saturday")),
("6", self.env._("Sunday")),
]
@staticmethod
def _get_state_selection(records):
def _get_state_selection(self):
"""Return state selection options with translations."""
return [
("draft", _("Draft")),
("open", _("Open")),
("closed", _("Closed")),
("cancelled", _("Cancelled")),
("draft", self.env._("Draft")),
("open", self.env._("Open")),
("closed", self.env._("Closed")),
("cancelled", self.env._("Cancelled")),
]
# === Multicompañía ===
company_id = fields.Many2one(
"res.company",
string="Company",
required=True,
default=lambda self: self.env.company,
tracking=True,
@ -73,7 +67,6 @@ class GroupOrder(models.Model):
# === Campos básicos ===
name = fields.Char(
string="Name",
required=True,
tracking=True,
translate=True,
@ -84,7 +77,6 @@ class GroupOrder(models.Model):
"group_order_group_rel",
"order_id",
"group_id",
string="Consumer Groups",
required=True,
domain=[("is_group", "=", True)],
tracking=True,
@ -92,7 +84,6 @@ class GroupOrder(models.Model):
)
type = fields.Selection(
selection=_get_order_type_selection,
string="Order Type",
required=True,
default="regular",
tracking=True,
@ -101,13 +92,11 @@ class GroupOrder(models.Model):
# === Fechas ===
start_date = fields.Date(
string="Start Date",
required=False,
tracking=True,
help="Day when the consumer group order opens for purchases",
)
end_date = fields.Date(
string="End Date",
required=False,
tracking=True,
help="If empty, the consumer group order is permanent",
@ -116,7 +105,6 @@ class GroupOrder(models.Model):
# === Período y días ===
period = fields.Selection(
selection=_get_period_selection,
string="Recurrence Period",
required=True,
default="weekly",
tracking=True,
@ -124,14 +112,12 @@ class GroupOrder(models.Model):
)
pickup_day = fields.Selection(
selection=_get_day_selection,
string="Pickup Day",
required=False,
tracking=True,
help="Day of the week when members pick up their orders",
)
cutoff_day = fields.Selection(
selection=_get_day_selection,
string="Cutoff Day",
required=False,
tracking=True,
help="Day when purchases stop and the consumer group order is locked for this week.",
@ -139,20 +125,17 @@ class GroupOrder(models.Model):
# === Home delivery ===
home_delivery = fields.Boolean(
string="Home Delivery",
default=False,
tracking=True,
help="Whether this consumer group order includes home delivery service",
)
delivery_product_id = fields.Many2one(
"product.product",
string="Delivery Product",
domain=[("type", "=", "service")],
tracking=True,
help="Product to use for home delivery (service type)",
)
delivery_date = fields.Date(
string="Delivery Date",
compute="_compute_delivery_date",
store=True,
readonly=True,
@ -161,14 +144,12 @@ class GroupOrder(models.Model):
# === Computed date fields ===
pickup_date = fields.Date(
string="Pickup Date",
compute="_compute_pickup_date",
store=True,
readonly=True,
help="Calculated next occurrence of pickup day",
)
cutoff_date = fields.Date(
string="Cutoff Date",
compute="_compute_cutoff_date",
store=True,
readonly=True,
@ -181,7 +162,6 @@ class GroupOrder(models.Model):
"group_order_supplier_rel",
"order_id",
"supplier_id",
string="Suppliers",
domain=[("supplier_rank", ">", 0)],
tracking=True,
help="Products from these suppliers will be available.",
@ -191,7 +171,6 @@ class GroupOrder(models.Model):
"group_order_product_rel",
"order_id",
"product_id",
string="Products",
tracking=True,
help="Directly assigned products.",
)
@ -200,7 +179,6 @@ class GroupOrder(models.Model):
"group_order_category_rel",
"order_id",
"category_id",
string="Categories",
tracking=True,
help="Products in these categories will be available",
)
@ -208,29 +186,24 @@ class GroupOrder(models.Model):
# === Estado ===
state = fields.Selection(
selection=_get_state_selection,
string="State",
default="draft",
tracking=True,
)
# === Descripción e imagen ===
description = fields.Text(
string="Description",
translate=True,
help="Free text description for this consumer group order",
)
delivery_notice = fields.Text(
string="Delivery Notice",
translate=True,
help="Notice about home delivery displayed to users (shown when home delivery is enabled)",
)
image = fields.Binary(
string="Image",
help="Image displayed alongside the consumer group order name",
attachment=True,
)
display_image = fields.Binary(
string="Display Image",
compute="_compute_display_image",
store=True,
help="Image to display: uses consumer group order image if set, otherwise group image",
@ -249,7 +222,6 @@ class GroupOrder(models.Model):
record.display_image = False
available_products_count = fields.Integer(
string="Available Products Count",
compute="_compute_available_products_count",
store=False,
help="Total count of available products from all sources",
@ -270,13 +242,14 @@ class GroupOrder(models.Model):
if group.company_id and group.company_id != record.company_id:
raise ValidationError(
self.env._(
"Group {group} belongs to company {group_company}, "
"not to {record_company}."
).format(
group=group.name,
group_company=group.company_id.name,
record_company=record.company_id.name,
"Group %(group)s belongs to company %(group_company)s, "
"not to %(record_company)s."
)
% {
"group": group.name,
"group_company": group.company_id.name,
"record_company": record.company_id.name,
}
)
@api.constrains("start_date", "end_date")
@ -569,11 +542,12 @@ class GroupOrder(models.Model):
pickup_name = dict(self._get_day_selection())[str(pickup)]
cutoff_name = dict(self._get_day_selection())[str(cutoff)]
raise ValidationError(
_(
"For weekly orders, pickup day ({pickup}) must be after or equal to "
"cutoff day ({cutoff}) in the same week. Current configuration would "
self.env._(
"For weekly orders, pickup day (%(pickup)s) must be after or equal to "
"cutoff day (%(cutoff)s) in the same week. Current configuration would "
"put pickup before cutoff, which is illogical."
).format(pickup=pickup_name, cutoff=cutoff_name)
)
% {"pickup": pickup_name, "cutoff": cutoff_name}
)
# === Onchange Methods ===

View file

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
"""
@ -20,151 +19,157 @@ from odoo import _
def _register_translations():
"""
Register all JavaScript translation strings.
Called by Odoo's translation extraction system.
These calls populate the POT/PO files for translation.
"""
# ========================
# Action Labels
# ========================
_('Save Cart')
_('Reload Cart')
_('Browse Product Categories')
_('Proceed to Checkout')
_('Confirm Order')
_('Back to Cart')
_('Remove Item')
_('Add to Cart')
_('Save as Draft')
_('Load Draft')
_('Browse Product Categories')
_("Save Cart")
_("Reload Cart")
_("Browse Product Categories")
_("Proceed to Checkout")
_("Confirm Order")
_("Back to Cart")
_("Remove Item")
_("Add to Cart")
_("Save as Draft")
_("Load Draft")
_("Browse Product Categories")
# ========================
# Draft Modal Labels
# ========================
_('Draft Already Exists')
_('A saved draft already exists for this week.')
_('You have two options:')
_('Option 1: Merge with Existing Draft')
_('Combine your current cart with the existing draft.')
_('Existing draft has')
_('Current cart has')
_('item(s)')
_('Products will be merged by adding quantities. If a product exists in both, quantities will be combined.')
_('Option 2: Replace with Current Cart')
_('Delete the old draft and save only the current cart items.')
_('The existing draft will be permanently deleted.')
_('Merge')
_('Replace')
_("Draft Already Exists")
_("A saved draft already exists for this week.")
_("You have two options:")
_("Option 1: Merge with Existing Draft")
_("Combine your current cart with the existing draft.")
_("Existing draft has")
_("Current cart has")
_("item(s)")
_(
"Products will be merged by adding quantities. If a product exists in both, quantities will be combined."
)
_("Option 2: Replace with Current Cart")
_("Delete the old draft and save only the current cart items.")
_("The existing draft will be permanently deleted.")
_("Merge")
_("Replace")
# ========================
# Draft Save/Load Confirmations
# ========================
_('Are you sure you want to save this cart as draft? Items to save: ')
_('You will be able to reload this cart later.')
_('Are you sure you want to load your last saved draft?')
_('This will replace the current items in your cart')
_('with the saved draft.')
_("Are you sure you want to save this cart as draft? Items to save: ")
_("You will be able to reload this cart later.")
_("Are you sure you want to load your last saved draft?")
_("This will replace the current items in your cart")
_("with the saved draft.")
# ========================
# Cart Messages (All Variations)
# ========================
_('Your cart is empty')
_('This order\'s cart is empty.')
_('This order\'s cart is empty')
_('added to cart')
_('items')
_('Your cart has been restored')
_("Your cart is empty")
_("This order's cart is empty.")
_("This order's cart is empty")
_("added to cart")
_("items")
_("Your cart has been restored")
# ========================
# Confirmation & Validation
# ========================
_('Confirmation')
_('Confirm')
_('Cancel')
_('Please enter a valid quantity')
_("Confirmation")
_("Confirm")
_("Cancel")
_("Please enter a valid quantity")
# ========================
# Error Messages
# ========================
_('Error: Order ID not found')
_('No draft orders found for this week')
_('Connection error')
_('Error loading order')
_('Error loading draft')
_('Unknown error')
_('Error saving cart')
_('Error processing response')
_("Error: Order ID not found")
_("No draft orders found for this week")
_("Connection error")
_("Error loading order")
_("Error loading draft")
_("Unknown error")
_("Error saving cart")
_("Error processing response")
# ========================
# Success Messages
# ========================
_('Cart saved as draft successfully')
_('Draft order loaded successfully')
_('Draft merged successfully')
_('Draft replaced successfully')
_('Order loaded')
_('Thank you! Your order has been confirmed.')
_('Quantity updated')
_("Cart saved as draft successfully")
_("Draft order loaded successfully")
_("Draft merged successfully")
_("Draft replaced successfully")
_("Order loaded")
_("Thank you! Your order has been confirmed.")
_("Quantity updated")
# ========================
# Field Labels
# ========================
_('Product')
_('Supplier')
_('Price')
_('Quantity')
_('Subtotal')
_('Total')
_("Product")
_("Supplier")
_("Price")
_("Quantity")
_("Subtotal")
_("Total")
# ========================
# Checkout Page Labels
# ========================
_('Home Delivery')
_('Delivery Information')
_('Delivery Information: Your order will be delivered at {pickup_day} {pickup_date}')
_('Your order will be delivered the day after pickup between 11:00 - 14:00')
_('Important')
_('Once you confirm this order, you will not be able to modify it. Please review carefully before confirming.')
_("Home Delivery")
_("Delivery Information")
_(
"Delivery Information: Your order will be delivered at {pickup_day} {pickup_date}"
)
_("Your order will be delivered the day after pickup between 11:00 - 14:00")
_("Important")
_(
"Once you confirm this order, you will not be able to modify it. Please review carefully before confirming."
)
# ========================
# Search & Filter Labels
# ========================
_('Search')
_('Search products...')
_('No products found')
_('Categories')
_('All categories')
_("Search")
_("Search products...")
_("No products found")
_("Categories")
_("All categories")
# ========================
# Category Labels
# ========================
_('Order Type')
_('Order Period')
_('Cutoff Day')
_('Pickup Day')
_('Store Pickup Day')
_('Open until')
_("Order Type")
_("Order Period")
_("Cutoff Day")
_("Pickup Day")
_("Store Pickup Day")
_("Open until")
# ========================
# Portal Page Labels (New)
# ========================
_('Load in Cart')
_('Consumer Group')
_('Delivery Information')
_('Delivery Date:')
_('Pickup Date:')
_('Delivery Notice:')
_('No special delivery instructions')
_('Pickup Location:')
_("Load in Cart")
_("Consumer Group")
_("Delivery Information")
_("Delivery Date:")
_("Pickup Date:")
_("Delivery Notice:")
_("No special delivery instructions")
_("Pickup Location:")
# ========================
# Day Names (Required for translations)
# ========================
_('Monday')
_('Tuesday')
_('Wednesday')
_('Thursday')
_('Friday')
_('Saturday')
_('Sunday')
_("Monday")
_("Tuesday")
_("Wednesday")
_("Thursday")
_("Friday")
_("Saturday")
_("Sunday")

View file

@ -1,20 +1,23 @@
# Copyright 2025 Criptomart
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl)
from odoo import _, api, fields, models
from odoo import _
from odoo import api
from odoo import fields
from odoo import models
class ProductProduct(models.Model):
_inherit = 'product.product'
_inherit = "product.product"
group_order_ids = fields.Many2many(
'group.order',
'group_order_product_rel',
'product_id',
'order_id',
string='Group Orders',
"group.order",
"group_order_product_rel",
"product_id",
"order_id",
string="Group Orders",
readonly=True,
help='Group orders where this product is available',
help="Group orders where this product is available",
)
@api.model
@ -25,26 +28,25 @@ class ProductProduct(models.Model):
responsibilities together. Keep this wrapper so existing callers
on `product.product` keep working.
"""
order = self.env['group.order'].browse(order_id)
order = self.env["group.order"].browse(order_id)
if not order.exists():
return self.browse()
return order._get_products_for_group_order(order.id)
class ProductTemplate(models.Model):
_inherit = 'product.template'
_inherit = "product.template"
group_order_ids = fields.Many2many(
'group.order',
compute='_compute_group_order_ids',
string='Consumer Group Orders',
"group.order",
compute="_compute_group_order_ids",
string="Consumer Group Orders",
readonly=True,
help='Consumer group orders where variants of this product are available',
help="Consumer group orders where variants of this product are available",
)
@api.depends('product_variant_ids.group_order_ids')
@api.depends("product_variant_ids.group_order_ids")
def _compute_group_order_ids(self):
for template in self:
variants = template.product_variant_ids
template.group_order_ids = variants.mapped('group_order_ids')
template.group_order_ids = variants.mapped("group_order_ids")

View file

@ -1,37 +1,39 @@
# Copyright 2025-Today Criptomart
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl)
from odoo import _, fields, models
from odoo import _
from odoo import fields
from odoo import models
class ResPartner(models.Model):
_inherit = 'res.partner'
_inherit = "res.partner"
# Campo para identificar si un partner es un grupo
is_group = fields.Boolean(
string='Is a Consumer Group?',
help='Check this box if the partner represents a group of users',
string="Is a Consumer Group?",
help="Check this box if the partner represents a group of users",
default=False,
)
# Relación para los miembros de un grupo (si is_group es True)
member_ids = fields.Many2many(
'res.partner',
'res_partner_group_members_rel',
'group_id',
'member_id',
domain=[('is_group', '=', True)],
string='Consumer Groups',
help='Consumer Groups this partner belongs to',
"res.partner",
"res_partner_group_members_rel",
"group_id",
"member_id",
domain=[("is_group", "=", True)],
string="Consumer Groups",
help="Consumer Groups this partner belongs to",
)
# Inverse relation: group orders this group participates in
group_order_ids = fields.Many2many(
'group.order',
'group_order_group_rel',
'group_id',
'order_id',
string='Consumer Group Orders',
help='Group orders this consumer group participates in',
"group.order",
"group_order_group_rel",
"group_id",
"order_id",
string="Consumer Group Orders",
help="Group orders this consumer group participates in",
readonly=True,
)

View file

@ -1,56 +1,52 @@
# Copyright 2025 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 SaleOrder(models.Model):
_inherit = 'sale.order'
_inherit = "sale.order"
@staticmethod
def _get_pickup_day_selection(records):
def _get_pickup_day_selection(self):
"""Return pickup day selection options with translations."""
return [
('0', _('Monday')),
('1', _('Tuesday')),
('2', _('Wednesday')),
('3', _('Thursday')),
('4', _('Friday')),
('5', _('Saturday')),
('6', _('Sunday')),
("0", self.env._("Monday")),
("1", self.env._("Tuesday")),
("2", self.env._("Wednesday")),
("3", self.env._("Thursday")),
("4", self.env._("Friday")),
("5", self.env._("Saturday")),
("6", self.env._("Sunday")),
]
pickup_day = fields.Selection(
selection=_get_pickup_day_selection,
string='Pickup Day',
help='Day of week when this order will be picked up (inherited from group order)',
help="Day of week when this order will be picked up (inherited from group order)",
)
group_order_id = fields.Many2one(
'group.order',
string='Consumer Group Order',
help='Reference to the consumer group order that originated this sale order',
"group.order",
help="Reference to the consumer group order that originated this sale order",
)
pickup_date = fields.Date(
string='Pickup Date',
help='Calculated pickup/delivery date (inherited from consumer group order)',
help="Calculated pickup/delivery date (inherited from consumer group order)",
)
home_delivery = fields.Boolean(
string='Home Delivery',
default=False,
help='Whether this order includes home delivery (inherited from consumer group order)',
help="Whether this order includes home delivery (inherited from consumer group order)",
)
def _get_name_portal_content_view(self):
"""Override to return custom portal content template with group order info.
This method is called by the portal template to determine which content
template to render. We return our custom template that includes the
group order information (Consumer Group, Delivery/Pickup info, etc.)
"""
self.ensure_one()
if self.group_order_id:
return 'website_sale_aplicoop.sale_order_portal_content_aplicoop'
return "website_sale_aplicoop.sale_order_portal_content_aplicoop"
return super()._get_name_portal_content_view()