diff --git a/stock_ux/__manifest__.py b/stock_ux/__manifest__.py
index 93cfa265a..6fc42877d 100644
--- a/stock_ux/__manifest__.py
+++ b/stock_ux/__manifest__.py
@@ -19,7 +19,7 @@
##############################################################################
{
"name": "Stock UX",
- "version": "18.0.1.5.0",
+ "version": "18.0.1.5.1",
"category": "Warehouse Management",
"sequence": 14,
"summary": "",
diff --git a/stock_ux/models/__init__.py b/stock_ux/models/__init__.py
index d58254965..01982affa 100644
--- a/stock_ux/models/__init__.py
+++ b/stock_ux/models/__init__.py
@@ -15,3 +15,4 @@
from . import stock_scrap
from . import stock_location
from . import stock_forecasted
+from . import ir_actions_client
diff --git a/stock_ux/models/ir_actions_client.py b/stock_ux/models/ir_actions_client.py
new file mode 100644
index 000000000..98d5b6ee5
--- /dev/null
+++ b/stock_ux/models/ir_actions_client.py
@@ -0,0 +1,16 @@
+from odoo import models
+from odoo.tools.safe_eval import safe_eval
+
+
+class IRActionsClient(models.Model):
+ _inherit = "ir.actions.client"
+
+ def read(self, fields=None, load='_classic_read'):
+ res = super().read(fields=fields, load=load)
+ company = self.env.company
+ if company and company.country_code and company.country_code == "AR":
+ for i, action in enumerate(res):
+ ctx = safe_eval(action.get("context") or "{}")
+ ctx["company_country_code"] = company.country_code
+ res[i]["context"] = ctx
+ return res
diff --git a/stock_ux/models/stock_move.py b/stock_ux/models/stock_move.py
index 7f4679bf0..dbf6cd093 100644
--- a/stock_ux/models/stock_move.py
+++ b/stock_ux/models/stock_move.py
@@ -53,32 +53,32 @@ def _compute_origin_description(self):
@api.constrains("quantity")
def _check_quantity(self):
- # TODO: Odoo BTL - lock for AR
- precision = self.env["decimal.precision"].precision_get("Product Unit of Measure")
- if any(self.filtered(lambda x: x.scrapped)):
- return
- moves = self.filtered(
- lambda x: x.picking_id.picking_type_id.block_additional_quantity
- and float_compare(x.product_uom_qty, x.quantity, precision_digits=precision) == -1
- )
- if not moves:
- return
-
- # Si lo ejecuta el superusuario (scheduler), revertir el cambio y loguear
- if self.env.is_superuser():
- for move in moves:
- # Revertir el cambio de quantity
- move.quantity = move.product_uom_qty
- move.picking_id.message_post(
- body=_(
- "Se intentó transferir una cantidad mayor a la demanda inicial en el movimiento %s durante la ejecución automática (scheduler). El sistema ignoró el cambio y mantuvo la cantidad original."
+ if self.env.company.country_code == 'AR':
+ precision = self.env["decimal.precision"].precision_get("Product Unit of Measure")
+ if any(self.filtered(lambda x: x.scrapped)):
+ return
+ moves = self.filtered(
+ lambda x: x.picking_id.picking_type_id.block_additional_quantity
+ and float_compare(x.product_uom_qty, x.quantity, precision_digits=precision) == -1
+ )
+ if not moves:
+ return
+
+ # Si lo ejecuta el superusuario (scheduler), revertir el cambio y loguear
+ if self.env.is_superuser():
+ for move in moves:
+ # Revertir el cambio de quantity
+ move.quantity = move.product_uom_qty
+ move.picking_id.message_post(
+ body=_(
+ "Se intentó transferir una cantidad mayor a la demanda inicial en el movimiento %s durante la ejecución automática (scheduler). El sistema ignoró el cambio y mantuvo la cantidad original."
+ )
+ % move.display_name
)
- % move.display_name
- )
- return
+ return
- # Comportamiento normal: raise si corresponde
- raise ValidationError(_("You can not transfer more than the initial demand!"))
+ # Comportamiento normal: raise si corresponde
+ raise ValidationError(_("You can not transfer more than the initial demand!"))
def action_view_linked_record(self):
"""This function returns an action that display existing sales order
@@ -98,27 +98,27 @@ def action_view_linked_record(self):
def default_get(self, fields_list):
# We override the default_get to make stock moves created when the picking
# was confirmed , this way restrict to add more quantity that initial demand
- # TODO: Odoo BTL - lock for AR
defaults = super().default_get(fields_list)
- if self.env.context.get("default_picking_id"):
- picking_id = self.env["stock.picking"].browse(self.env.context["default_picking_id"])
- if picking_id.state == "confirmed":
- defaults["state"] = "confirmed"
- defaults["product_uom_qty"] = 0.0
- defaults["additional"] = True
+ if self.env.company.country_code == 'AR':
+ if self.env.context.get("default_picking_id"):
+ picking_id = self.env["stock.picking"].browse(self.env.context["default_picking_id"])
+ if picking_id.state == "confirmed":
+ defaults["state"] = "confirmed"
+ defaults["product_uom_qty"] = 0.0
+ defaults["additional"] = True
return defaults
@api.constrains("state")
def check_cancel(self):
- # TODO: Odoo BTL - lock for AR
- if self._context.get("cancel_from_order") or self.env.is_superuser():
- return
- if self.filtered(
- lambda x: x.picking_id
- and x.state == "cancel"
- and not self.env.user.has_group("stock_ux.allow_picking_cancellation")
- ):
- raise ValidationError("Only User with 'Picking cancelation allow' rights can cancel pickings")
+ if self.env.company.country_code == 'AR':
+ if self._context.get("cancel_from_order") or self.env.is_superuser():
+ return
+ if self.filtered(
+ lambda x: x.picking_id
+ and x.state == "cancel"
+ and not self.env.user.has_group("stock_ux.allow_picking_cancellation")
+ ):
+ raise ValidationError("Only User with 'Picking cancelation allow' rights can cancel pickings")
def _merge_moves(self, merge_into=False):
# 22/04/2024: Agregamos esto porque sino al intentar confirmar compras con usuarios sin permisos, podia pasar que salga la constrain de arriba (check_cancel)
@@ -146,17 +146,17 @@ def create(self, vals_list):
@api.depends("state", "picking_id")
def _compute_is_initial_demand_editable(self):
- # TODO: Odoo BTL - lock for AR
super(StockMove, self)._compute_is_initial_demand_editable()
- for move in self:
- if move.picking_id.picking_type_id.block_additional_quantity and move.picking_id.state != "draft":
- move.is_initial_demand_editable = False
+ if self.env.company.country_code == 'AR':
+ for move in self:
+ if move.picking_id.picking_type_id.block_additional_quantity and move.picking_id.state != "draft":
+ move.is_initial_demand_editable = False
def _trigger_assign(self):
"""To avoid to check_quantity_available when an assing in move is trigger we
send a context that checks if the assign comes from this method
"""
- # TODO: Odoo BTL - lock for AR
- if not self.env.context.get("trigger_assign"):
- return super().with_context(trigger_assign=True)._trigger_assign()
+ if self.env.company.country_code == 'AR':
+ if not self.env.context.get("trigger_assign"):
+ return super().with_context(trigger_assign=True)._trigger_assign()
return super()._trigger_assign()
diff --git a/stock_ux/models/stock_move_line.py b/stock_ux/models/stock_move_line.py
index 981486d69..d10b428ba 100644
--- a/stock_ux/models/stock_move_line.py
+++ b/stock_ux/models/stock_move_line.py
@@ -36,53 +36,53 @@ class StockMoveLine(models.Model):
@api.depends_context("location")
def _compute_product_uom_qty_location(self):
- # TODO: Odoo BTL - lock for AR
- location = self._context.get("location")
- if not location:
- self.update({"product_uom_qty_location": 0.0})
- return False
- # because now we use location_id to select location, we have compelte
- # location name. If y need we can use some code of
- # _get_domain_locations on stock/product.py
- location_name = location[0]
- if isinstance(location[0], int):
- location_name = self.env["stock.location"].browse(location[0]).name
- locations = self.env["stock.location"].search([("complete_name", "ilike", location_name)])
for rec in self:
- product_uom_qty_location = rec.quantity
- if rec.location_id in locations:
- # if location is source and destiny, then 0
- product_uom_qty_location = 0.0 if rec.location_dest_id in locations else -rec.quantity
- rec.product_uom_qty_location = product_uom_qty_location
+ rec.product_uom_qty_location = 0.0
+ if self.env.company.country_code == 'AR':
+ location = self._context.get("location")
+ if not location:
+ self.update({"product_uom_qty_location": 0.0})
+ return False
+ # because now we use location_id to select location, we have compelte
+ # location name. If y need we can use some code of
+ # _get_domain_locations on stock/product.py
+ location_name = location[0]
+ if isinstance(location[0], int):
+ location_name = self.env["stock.location"].browse(location[0]).name
+ locations = self.env["stock.location"].search([("complete_name", "ilike", location_name)])
+ for rec in self:
+ product_uom_qty_location = rec.quantity
+ if rec.location_id in locations:
+ # if location is source and destiny, then 0
+ product_uom_qty_location = 0.0 if rec.location_dest_id in locations else -rec.quantity
+ rec.product_uom_qty_location = product_uom_qty_location
@api.constrains("quantity")
def _check_manual_lines(self):
- # TODO: Odoo BTL - lock for AR
- if self._context.get("put_in_pack", False):
- return
- invalid_lines = self.filtered(
- lambda x: not x.location_id.should_bypass_reservation()
- and x.picking_id.picking_type_id.block_manual_lines
- and x._check_quantity_available() < 0
- )
- if not invalid_lines:
- return
-
- # Si lo ejecuta el superusuario (odoobot), revertir el cambio y loguear
- if self.env.is_superuser():
- for line in invalid_lines:
- # Revertir el cambio de quantity
- line.quantity = max(0, line._check_quantity_available() + line.quantity)
- if line.picking_id:
- line.picking_id.message_post(
- body=_(
- "Se intentó transferir una cantidad mayor al stock disponible en la línea %s durante la ejecución automática (odoobot/scheduler). El sistema ignoró el cambio y mantuvo la cantidad original."
+ if self.env.company.country_code == 'AR':
+ if self._context.get("put_in_pack", False):
+ return
+ invalid_lines = self.filtered(
+ lambda x: not x.location_id.should_bypass_reservation()
+ and x.picking_id.picking_type_id.block_manual_lines
+ and x._check_quantity_available() < 0
+ )
+ if not invalid_lines:
+ return
+ # Si lo ejecuta el superusuario (odoobot), revertir el cambio y loguear
+ if self.env.is_superuser():
+ for line in invalid_lines:
+ # Revertir el cambio de quantity
+ line.quantity = max(0, line._check_quantity_available() + line.quantity)
+ if line.picking_id:
+ line.picking_id.message_post(
+ body=_(
+ "Se intentó transferir una cantidad mayor al stock disponible en la línea %s durante la ejecución automática (odoobot/scheduler). El sistema ignoró el cambio y mantuvo la cantidad original."
+ )
+ % line.display_name
)
- % line.display_name
- )
- return
-
- raise ValidationError(_("You can't transfer more quantity than the quantity on stock!"))
+ return
+ raise ValidationError(_("You can't transfer more quantity than the quantity on stock!"))
def _check_quantity_available(self):
self.ensure_one()
@@ -111,11 +111,11 @@ def create(self, vals_list):
"""This is to solve a bug when create the sml (the value is not completed after creation)
and should be reported to odoo to solve."""
recs = super().create(vals_list)
- # TODO: Odoo BTL - lock for AR
- for rec in recs:
- if rec.picking_id and not rec.description_picking:
- product = rec.product_id.with_context(lang=rec.picking_id.partner_id.lang or rec.env.user.lang)
- rec.description_picking = product._get_description(rec.picking_id.picking_type_id)
+ if self.env.company.country_code == 'AR':
+ for rec in recs:
+ if rec.picking_id and not rec.description_picking:
+ product = rec.product_id.with_context(lang=rec.picking_id.partner_id.lang or rec.env.user.lang)
+ rec.description_picking = product._get_description(rec.picking_id.picking_type_id)
return recs
def _get_aggregated_product_quantities(self, **kwargs):
diff --git a/stock_ux/models/stock_picking.py b/stock_ux/models/stock_picking.py
index cbeeda024..c992c6cec 100644
--- a/stock_ux/models/stock_picking.py
+++ b/stock_ux/models/stock_picking.py
@@ -28,57 +28,83 @@ def unlink(self):
To avoid errors we block deletion of pickings in other state than
draft or cancel
"""
- # TODO: Odoo BTL - lock for AR
- not_del_pickings = self.filtered(
- lambda x: x.picking_type_id.block_picking_deletion or x.state not in ("draft", "cancel")
- )
- if not_del_pickings:
- raise ValidationError(
- _(
- 'You can not delete this pickings because "Block picking '
- 'deletion" is enable on the picking type/s "%s" '
- "or the state of the picking is not draft or cancel.\n"
- "Picking Ids: %s"
- )
- % (",".join(not_del_pickings.mapped("picking_type_id.name")), not_del_pickings.ids)
+ if self.env.company.country_code == 'AR':
+ not_del_pickings = self.filtered(
+ lambda x: x.picking_type_id.block_picking_deletion or x.state not in ("draft", "cancel")
)
+ if not_del_pickings:
+ raise ValidationError(
+ _(
+ 'You can not delete this pickings because "Block picking '
+ 'deletion" is enable on the picking type/s "%s" '
+ "or the state of the picking is not draft or cancel.\n"
+ "Picking Ids: %s"
+ )
+ % (",".join(not_del_pickings.mapped("picking_type_id.name")), not_del_pickings.ids)
+ )
return super().unlink()
def copy(self, default=None):
- # TODO: Odoo BTL - lock for AR
- for picking in self:
- if not default and picking.picking_type_id.block_additional_quantity:
- raise UserError(
- _(
- 'You can not duplicate a Picking because "Block Additional Quantity" is enabled on the picking type "%(name)s"'
+ if self.env.company.country_code == 'AR':
+ for picking in self:
+ if not default and picking.picking_type_id.block_additional_quantity:
+ raise UserError(
+ _(
+ 'You can not duplicate a Picking because "Block Additional Quantity" is enabled on the picking type "%(name)s"'
+ )
+ % {"name": picking.picking_type_id.name}
)
- % {"name": picking.picking_type_id.name}
- )
return super().copy(default=default)
@api.onchange("location_id")
def change_location(self):
- # TODO: Odoo BTL - lock for AR
- # we only change moves locations if picking in draft
- if self.state == "draft":
- self.move_ids.update({"location_id": self.location_id.id})
+ if self.env.company.country_code == 'AR':
+ # we only change moves locations if picking in draft
+ if self.state == "draft":
+ self.move_ids.update({"location_id": self.location_id.id})
@api.onchange("location_dest_id")
def change_location_dest(self):
- # TODO: Odoo BTL - lock for AR
- # we only change moves locations if picking in draft
- if self.state == "draft":
- self.move_ids.update({"location_dest_id": self.location_dest_id.id})
+ if self.env.company.country_code == 'AR':
+ # we only change moves locations if picking in draft
+ if self.state == "draft":
+ self.move_ids.update({"location_dest_id": self.location_dest_id.id})
def _send_confirmation_email(self):
- # TODO: Odoo BTL - lock for AR
- for rec in self:
- if rec.picking_type_id.mail_template_id:
+ if self.env.company.country_code == 'AR':
+ for rec in self:
+ if rec.picking_type_id.mail_template_id:
+ try:
+ rec.with_context(
+ email_notification_force_header=True,
+ email_notification_force_footer=True,
+ ).message_post_with_source(rec.picking_type_id.mail_template_id)
+ except Exception as error:
+ title = _("ERROR: Picking was not sent via email")
+ rec.message_post(
+ body="
".join(
+ [
+ "" + title + "",
+ _("Please check the email template associated with the picking type."),
+ "" + str(error) + "",
+ ]
+ ),
+ body_is_html=True,
+ )
+ else:
+ super(StockPicking, self)._send_confirmation_email()
+ else:
+ super(StockPicking, self)._send_confirmation_email()
+
+ def _action_done(self):
+ if self.env.company.country_code == 'AR':
+ for rec in self.with_context(
+ mail_notify_force_send=False,
+ email_notification_force_header=True,
+ email_notification_force_footer=True,
+ ).filtered("picking_type_id.mail_template_id"):
try:
- rec.with_context(
- email_notification_force_header=True,
- email_notification_force_footer=True,
- ).message_post_with_source(rec.picking_type_id.mail_template_id)
+ rec.message_post_with_template(rec.picking_type_id.mail_template_id.id)
except Exception as error:
title = _("ERROR: Picking was not sent via email")
rec.message_post(
@@ -89,31 +115,7 @@ def _send_confirmation_email(self):
"" + str(error) + "",
]
),
- body_is_html=True,
)
- else:
- super(StockPicking, self)._send_confirmation_email()
-
- def _action_done(self):
- # TODO: Odoo BTL - lock for AR
- for rec in self.with_context(
- mail_notify_force_send=False,
- email_notification_force_header=True,
- email_notification_force_footer=True,
- ).filtered("picking_type_id.mail_template_id"):
- try:
- rec.message_post_with_template(rec.picking_type_id.mail_template_id.id)
- except Exception as error:
- title = _("ERROR: Picking was not sent via email")
- rec.message_post(
- body="
".join(
- [
- "" + title + "",
- _("Please check the email template associated with the picking type."),
- "" + str(error) + "",
- ]
- ),
- )
return super()._action_done()
def new_force_availability(self):
@@ -159,17 +161,17 @@ def write(self, vals):
:return: Result of the superclass write method.
"""
- if "picking_type_id" in vals:
- user = self.env.user
- # TODO: Odoo BTL - lock for AR
- if user.has_group("stock_ux.group_restrict_edit_picking_type"):
- for picking in self:
- if picking.picking_type_id:
- raise UserError(
- _(
- "You cannot change the Operation Type once it has been set. "
- "This action is restricted for your user. "
- "Please contact your Inventory Manager if you need to perform this operation."
+ if self.env.company.country_code == 'AR':
+ if "picking_type_id" in vals:
+ user = self.env.user
+ if user.has_group("stock_ux.group_restrict_edit_picking_type"):
+ for picking in self:
+ if picking.picking_type_id:
+ raise UserError(
+ _(
+ "You cannot change the Operation Type once it has been set. "
+ "This action is restricted for your user. "
+ "Please contact your Inventory Manager if you need to perform this operation."
+ )
)
- )
return super().write(vals)
diff --git a/stock_ux/models/stock_rule.py b/stock_ux/models/stock_rule.py
index 32652cf8a..71ee8ce41 100644
--- a/stock_ux/models/stock_rule.py
+++ b/stock_ux/models/stock_rule.py
@@ -24,19 +24,18 @@ def _run_pull(self, procurements):
"""
result = super()._run_pull(procurements)
- # Extract orderpoints from procurements and recompute their qty_to_order_computed
- # procurement is a namedtuple:
- # (product_id, product_qty, product_uom, location_id, name, origin, company_id, values)
- # TODO: Odoo BTL - lock for AR
- orderpoints = self.env["stock.warehouse.orderpoint"]
- for procurement, rule in procurements:
- # Access values dict from the namedtuple (index 7 or .values attribute)
- values = procurement.values
- if values.get("orderpoint_id"):
- orderpoints |= values["orderpoint_id"]
-
- # Recompute only the affected orderpoints for performance
- if orderpoints:
- orderpoints.sudo()._compute_qty_to_order_computed()
-
+ if self.env.company.country_code == 'AR':
+ # Extract orderpoints from procurements and recompute their qty_to_order_computed
+ # procurement is a namedtuple:
+ # (product_id, product_qty, product_uom, location_id, name, origin, company_id, values)
+ orderpoints = self.env["stock.warehouse.orderpoint"]
+ for procurement, rule in procurements:
+ # Access values dict from the namedtuple (index 7 or .values attribute)
+ values = procurement.values
+ if values.get("orderpoint_id"):
+ orderpoints |= values["orderpoint_id"]
+
+ # Recompute only the affected orderpoints for performance
+ if orderpoints:
+ orderpoints.sudo()._compute_qty_to_order_computed()
return result
diff --git a/stock_ux/models/stock_warehouse_orderpoint.py b/stock_ux/models/stock_warehouse_orderpoint.py
index 81e5da4c7..f7f2af176 100644
--- a/stock_ux/models/stock_warehouse_orderpoint.py
+++ b/stock_ux/models/stock_warehouse_orderpoint.py
@@ -131,15 +131,15 @@ def write(self, vals):
"""When archive a replenishment rule
set min, max and multiple quantities in 0.
"""
- # TODO: Odoo BTL - lock for AR
- if "active" in vals and not vals["active"]:
- self.write(
- {
- "product_min_qty": 0.0,
- "product_max_qty": 0.0,
- "qty_multiple": 0.0,
- }
- )
+ if self.env.company.country_code == 'AR':
+ if "active" in vals and not vals["active"]:
+ self.write(
+ {
+ "product_min_qty": 0.0,
+ "product_max_qty": 0.0,
+ "qty_multiple": 0.0,
+ }
+ )
return super().write(vals)
def _get_orderpoint_action(self):
@@ -182,7 +182,7 @@ def update_qty_to_order(self):
def _cron_compute_rotation(self):
"""Cron method to compute the rotation of orderpoints."""
- # TODO: Odoo BTL - lock for AR
- orderpoints = self.with_context(active_test=False).search([])
- orderpoints._compute_rotation()
- return True
+ if self.env.company.country_code == 'AR':
+ orderpoints = self.with_context(active_test=False).search([])
+ orderpoints._compute_rotation()
+ return True
diff --git a/stock_ux/static/src/forecasted_buttons.xml b/stock_ux/static/src/forecasted_buttons.xml
index 17a0ab190..54d8ad7f3 100644
--- a/stock_ux/static/src/forecasted_buttons.xml
+++ b/stock_ux/static/src/forecasted_buttons.xml
@@ -2,12 +2,14 @@
-
+
+
+
diff --git a/stock_ux/views/stock_picking_views.xml b/stock_ux/views/stock_picking_views.xml
index b430295fa..a78e9dfd0 100644
--- a/stock_ux/views/stock_picking_views.xml
+++ b/stock_ux/views/stock_picking_views.xml
@@ -108,14 +108,6 @@
-
-
-
-
-
-
-
-
stock.picking.formstock.picking