Skip to content

Commit

Permalink
Merge branch 'inventory-wizard-sort' into opw-testing
Browse files Browse the repository at this point in the history
  • Loading branch information
cbusillo committed Jan 3, 2025
2 parents 713f065 + f1379b8 commit 1f14978
Show file tree
Hide file tree
Showing 4 changed files with 75 additions and 49 deletions.
20 changes: 13 additions & 7 deletions product_connect/mixins/notification_manager_mixin.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import logging
import traceback

from odoo import api, models, fields

Expand Down Expand Up @@ -43,7 +44,7 @@ def notify_channel(
channel_name: str,
record: models.Model | None = None,
env: api.Environment | None = None,
logs: list[str] | None = None,
error_traceback: str | None = None,
) -> None:
env = env or self.env
notification_history = env["notification.history"]
Expand All @@ -54,9 +55,10 @@ def notify_channel(
channel = env["discuss.channel"].search([("name", "=", channel_name)], limit=1)
if not channel:
channel = env["discuss.channel"].create({"name": channel_name})
if logs:
body += "\n\nRecent logs:\n"
body += "\n".join(logs)

if error_traceback:
body += "\n\nError traceback:\n"
body += error_traceback

_logger.debug(
"Sending message to channel %s with message %s for record %s",
Expand All @@ -78,13 +80,17 @@ def notify_channel_on_error(
subject: str,
body: str,
record: models.Model | None = None,
logs: list[str] | None = None,
error: Exception | None = None,
) -> None:
error_traceback = None
if error:
error_traceback = "".join(traceback.format_exception(type(error), error, error.__traceback__))

new_cr = self.env.registry.cursor()
try:
new_env = api.Environment(new_cr, self.env.uid, self.env.context)
self.notify_channel(subject, body, "errors", record, new_env, logs)
self.send_email_notification_to_admin(subject, body)
self.notify_channel(subject, body, "errors", record, new_env, error_traceback)
self.send_email_notification_to_admin(subject, body + "\n\n" + error_traceback)
new_cr.commit()
finally:
new_cr.close()
Expand Down
19 changes: 3 additions & 16 deletions product_connect/models/shopify_sync.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,19 +24,6 @@
_logger = logging.getLogger(__name__)


class MemoryHandler(logging.Handler):
def __init__(self) -> None:
super().__init__()
self.logs: list[str] = []

def emit(self, record: logging.LogRecord) -> None:
self.logs.append(self.format(record))


memory_handler = MemoryHandler()
logging.getLogger().addHandler(memory_handler)


def apply_rate_limit_patch_to_shopify_execute() -> None:
class ThrottledError(Exception):
pass
Expand Down Expand Up @@ -139,7 +126,7 @@ def sync_with_shopify(self) -> None:
self.notify_channel_on_error(
"Shopify sync failed",
str(error),
logs=memory_handler.logs,
error=error,
)
raise error

Expand Down Expand Up @@ -267,7 +254,7 @@ def import_or_update_shopify_product(self, shopify_product: dict, last_import_ti
"Import from Shopify failed",
str(error),
record=odoo_product_product,
logs=memory_handler.logs,
error=error,
)
raise error
if status == "no_sku":
Expand Down Expand Up @@ -740,7 +727,7 @@ def export_to_shopify(self) -> None:
"Export from Shopify failed",
f"{str(error)}",
record=odoo_product,
logs=memory_handler.logs,
error=error,
)
raise error

Expand Down
36 changes: 31 additions & 5 deletions product_connect/views/product_inventory_wizard_views.xml
Original file line number Diff line number Diff line change
Expand Up @@ -33,19 +33,45 @@
<field name="scan_box" placeholder="Scan or type SKU"/>
<field name="bin" readonly="1" force_save="1"/>
<field name="bin_needs_update" invisible="1"/>
<field name="count_of_products_not_selected"/>
<field name="count_of_products_not_selected_with_quantity"/>
<field name="count_of_products_not_selected" string="Missing Products"/>
<field name="hide_last_scanned_product" widget="boolean_toggle" class="large-toggle"
string="Hide Last Scanned?"/>
</group>
<group>
<field name="use_available_quantity" widget="boolean_toggle"/>
<field name="use_available_quantity" widget="boolean_toggle" class="large-toggle"/>
<field name="product_labels_to_print" invisible="use_available_quantity"/>
<field name="total_product_labels_to_print"/>

</group>
</group>

<group colspan="2" string="Last Scanned Product"
invisible="not last_scanned_product or hide_last_scanned_product">
<group>
<field name="last_scanned_product_image" widget="image" readonly="1" nolabel="1"
class="bg-transparent"/>
</group>

<group>
<field name="last_scanned_product_name" readonly="1"/>
<field name="last_scanned_product_default_code" readonly="1"/>
<field name="last_scanned_product_bin" readonly="1"
decoration-danger="last_scanned_product_bin != bin"/>
<label for="last_scanned_product_qty"/>
<div style="display: inline-flex; align-items: center;">
<field name="last_scanned_product_qty" widget="float" options="{'digits': [3,0]}"
readonly="1" nolabel="1" style="margin-right: 8px;"/>
<span title="Warning" class="fa fa-exclamation-triangle text-warning"
invisible="last_scanned_product_qty &lt; 2"
style="display: inline-block; position: relative;top: -3px"/>
</div>
<field name="last_scanned_product_scanned_quantity" string="Quantity Scanned" readonly="1"/>
<field name="last_scanned_product" readonly="1"/>
</group>
</group>
<field name="products" nolabel="1">
<list create="0" editable="bottom">
<field name="is_selected" widget="boolean_toggle" width="40px"/>
<list create="0" editable="bottom" default_order="is_selected asc">
<field name="is_selected" widget="boolean_toggle" width="50px" class="large-toggle"/>
<field name="default_code"/>
<field name="name"/>
<field name="bin" decoration-danger="bin != parent.bin"/>
Expand Down
49 changes: 28 additions & 21 deletions product_connect/wizards/product_inventory_wizard.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
import logging
from typing import Literal

from odoo import models, fields, api

_logger = logging.getLogger(__name__)


class ProductInventoryWizardLine(models.TransientModel):
_name = "product.inventory.wizard.line"
_description = "Product Inventory Wizard Line"
_order = "is_selected asc, id desc"

wizard = fields.Many2one("product.inventory.wizard", ondelete="cascade")
product = fields.Many2one("product.template")
Expand All @@ -31,30 +35,27 @@ class ProductInventoryWizard(models.TransientModel):
bin_needs_update = fields.Boolean(compute="_compute_bin_needs_update")
total_product_labels_to_print = fields.Integer(compute="_compute_total_product_labels_to_print")
count_of_products_not_selected = fields.Integer(compute="_compute_products_not_selected")
count_of_products_not_selected_with_quantity = fields.Integer(
compute="_compute_products_not_selected_with_quantity"

hide_last_scanned_product = fields.Boolean()
last_scanned_product = fields.Many2one("product.inventory.wizard.line", readonly=True)
last_scanned_product_qty = fields.Float(related="last_scanned_product.qty_available", readonly=True)
last_scanned_product_bin = fields.Char(related="last_scanned_product.bin", readonly=True)
last_scanned_product_name = fields.Char(related="last_scanned_product.name", readonly=True)
last_scanned_product_default_code = fields.Char(related="last_scanned_product.default_code", readonly=True)
last_scanned_product_image = fields.Binary(related="last_scanned_product.product.image_512", readonly=True)
last_scanned_product_scanned_quantity = fields.Integer(
related="last_scanned_product.quantity_scanned", readonly=True
)

@api.depends("products", "products.is_selected")
def _compute_products_not_selected(self) -> None:
for wizard in self:
wizard.count_of_products_not_selected = len(wizard.products.filtered(lambda p: not p.is_selected))

@api.depends("products", "products.is_selected", "products.qty_available")
def _compute_products_not_selected_with_quantity(self) -> None:
@api.depends("products", "products.bin", "bin", "products.is_selected")
def _compute_bin_needs_update(self) -> None:
for wizard in self:
wizard.count_of_products_not_selected_with_quantity = len(
wizard.products.filtered(lambda p: not p.is_selected and p.qty_available)
)

def notify_user(
self, message: "str", title: str or None, message_type: Literal["info", "success", "warning", "danger"] | None
):
self.env["bus.bus"]._sendone(
self.env.user.partner_id,
"simple_notification",
{"title": title or "Notification", "message": message, "sticky": False, "type": message_type or "info"},
)
wizard.bin_needs_update = any(p.bin != wizard.bin for p in wizard.products)

@api.depends(
"products",
Expand All @@ -70,10 +71,14 @@ def _compute_total_product_labels_to_print(self) -> None:
for p in wizard.products.filtered("is_selected")
)

@api.depends("products", "products.bin", "bin", "products.is_selected")
def _compute_bin_needs_update(self) -> None:
for wizard in self:
wizard.bin_needs_update = any(p.bin != wizard.bin for p in wizard.products)
def notify_user(
self, message: "str", title: str or None, message_type: Literal["info", "success", "warning", "danger"] | None
):
self.env["bus.bus"]._sendone(
self.env.user.partner_id,
"simple_notification",
{"title": title or "Notification", "message": message, "sticky": False, "type": message_type or "info"},
)

def _handle_product_scan(self) -> bool:
product_searched = self.env["product.template"].search([("default_code", "=", self.scan_box)], limit=1)
Expand All @@ -89,15 +94,17 @@ def _handle_product_scan(self) -> bool:
product_in_wizard.is_selected = False

else:
self.products += self.env["product.inventory.wizard.line"].create(
product_in_wizard = self.env["product.inventory.wizard.line"].create(
{
"wizard": self.id,
"product": product_searched.id,
"quantity_scanned": 1,
"is_selected": True,
}
)
self.products += product_in_wizard

self.last_scanned_product = product_in_wizard
return True

def _handle_bin_scan(self) -> None:
Expand Down

0 comments on commit 1f14978

Please sign in to comment.