diff --git a/addons/sale/controllers/catalog.py b/addons/sale/controllers/catalog.py index 8df5319893b..c704af4abb0 100644 --- a/addons/sale/controllers/catalog.py +++ b/addons/sale/controllers/catalog.py @@ -38,7 +38,7 @@ class CatalogController(Controller): product_ids.remove(product.id) default_data = request.env['sale.order.line']._get_catalog_info() - default_data['readOnly'] = order._is_readonly() + default_data['readOnly'] = order._is_readonly() if order else False sale_order_line_info.update({ id: { diff --git a/addons/sale/static/src/js/product_catalog/kanban_controller.js b/addons/sale/static/src/js/product_catalog/kanban_controller.js index 9a66fcaa5ab..af4ab311048 100644 --- a/addons/sale/static/src/js/product_catalog/kanban_controller.js +++ b/addons/sale/static/src/js/product_catalog/kanban_controller.js @@ -3,7 +3,7 @@ import { KanbanController } from "@web/views/kanban/kanban_controller"; import { onWillStart } from "@odoo/owl"; import { useService } from "@web/core/utils/hooks"; - +import { _t } from "@web/core/l10n/translation"; export class ProductCatalogKanbanController extends KanbanController { static template = "sale.ProductCatalogKanbanController"; @@ -14,14 +14,7 @@ export class ProductCatalogKanbanController extends KanbanController { this.orm = useService("orm"); this.orderId = this.props.context.order_id; - onWillStart(async () => { - // Define the content of the button. - const orderStateInfo = await this.orm.searchRead( - "sale.order", [["id", "=", this.orderId]], ["state"] - ); - const orderIsQuotation = ["draft", "sent"].includes(orderStateInfo[0].state); - this.buttonString = `Back to ${orderIsQuotation ? "Quotation" : "Order"}` - }) + onWillStart(async () => this._defineButtonContent()); } // Force the slot for the "Back to Quotation" button to always be shown. @@ -29,6 +22,19 @@ export class ProductCatalogKanbanController extends KanbanController { return true; } + async _defineButtonContent() { + // Define the content of the button. + const orderStateInfo = await this.orm.searchRead( + "sale.order", [["id", "=", this.orderId]], ["state"] + ); + const orderIsQuotation = ["draft", "sent"].includes(orderStateInfo[0].state); + if (orderIsQuotation) { + this.buttonString = _t("Back to Quotation"); + } else { + this.buttonString = _t("Back to Order"); + } + } + async backToQuotation() { // Restore the last form view from the breadcrumbs if breadcrumbs are available. // If, for some weird reason, the user reloads the page then the breadcrumbs are diff --git a/addons/sale/static/src/js/product_catalog/kanban_renderer.js b/addons/sale/static/src/js/product_catalog/kanban_renderer.js index 9cc19f7163d..d5866da625e 100644 --- a/addons/sale/static/src/js/product_catalog/kanban_renderer.js +++ b/addons/sale/static/src/js/product_catalog/kanban_renderer.js @@ -5,12 +5,11 @@ import { useService } from "@web/core/utils/hooks"; import { ProductCatalogKanbanRecord } from "./kanban_record"; - export class ProductCatalogKanbanRenderer extends KanbanRenderer { static template = "sale.ProductCatalogKanbanRenderer"; static components = { ...KanbanRenderer.components, - KanbanRecord: ProductCatalogKanbanRecord + KanbanRecord: ProductCatalogKanbanRecord, }; setup() { @@ -18,13 +17,23 @@ export class ProductCatalogKanbanRenderer extends KanbanRenderer { this.action = useService("action"); } + get createProductContext() { + return {}; + } + async createProduct() { - await this.action.doAction({ - type: "ir.actions.act_window", - res_model: "product.product", - target: "new", - views: [[false, "form"]], - view_mode: "form", - }); + await this.action.doAction( + { + type: "ir.actions.act_window", + res_model: "product.product", + target: "new", + views: [[false, "form"]], + view_mode: "form", + context: this.createProductContext, + }, + { + onClose: () => this.props.list.model.load(), + } + ); } } diff --git a/addons/sale/static/src/js/product_catalog/sale_order_line/sale_order_line.js b/addons/sale/static/src/js/product_catalog/sale_order_line/sale_order_line.js index 39f1951e2c1..7bf0edff90e 100644 --- a/addons/sale/static/src/js/product_catalog/sale_order_line/sale_order_line.js +++ b/addons/sale/static/src/js/product_catalog/sale_order_line/sale_order_line.js @@ -19,6 +19,14 @@ export class ProductCatalogSOL extends Component { return this.props.quantity !== 0; } + get disableRemove() { + return false; + } + + get disabledButtonTooltip() { + return ""; + } + get price() { return formatMonetary(this.props.price, { currencyId: this.env.currencyId, digits: this.env.digits }); } diff --git a/addons/sale/static/src/js/product_catalog/sale_order_line/sale_order_line.xml b/addons/sale/static/src/js/product_catalog/sale_order_line/sale_order_line.xml index e90c9da8a3e..6546b61b889 100644 --- a/addons/sale/static/src/js/product_catalog/sale_order_line/sale_order_line.xml +++ b/addons/sale/static/src/js/product_catalog/sale_order_line/sale_order_line.xml @@ -13,7 +13,9 @@
Add - +
+ +
diff --git a/addons/sale_stock/static/src/product_catalog/kanban_record.js b/addons/sale_stock/static/src/product_catalog/kanban_record.js new file mode 100644 index 00000000000..71ead5d604a --- /dev/null +++ b/addons/sale_stock/static/src/product_catalog/kanban_record.js @@ -0,0 +1,20 @@ +/** @odoo-module */ +import { ProductCatalogKanbanRecord } from "@sale/js/product_catalog/kanban_record"; +import { patch } from "@web/core/utils/patch"; + +patch(ProductCatalogKanbanRecord.prototype, { + updateQuantity(quantity) { + if ( + this.productCatalogData.quantity === this.productCatalogData.deliveredQty && + quantity < this.productCatalogData.quantity + ) { + // This condition is triggered on the following specific condition that the product was already at the minimum quantity possible, + // as stated in the sale_stock module, then the user inputs a quantity lower than this limit, in this case we need the record to forcefully update + // the record + this.props.record.load(); + this.props.record.model.notify(); + } else { + super.updateQuantity(Math.max(quantity, this.productCatalogData.deliveredQty)); + } + }, +}); diff --git a/addons/sale_stock/static/src/product_catalog/sale_order_line/sale_order_line.js b/addons/sale_stock/static/src/product_catalog/sale_order_line/sale_order_line.js index e99e2cbbfba..b87f0ee5ab9 100644 --- a/addons/sale_stock/static/src/product_catalog/sale_order_line/sale_order_line.js +++ b/addons/sale_stock/static/src/product_catalog/sale_order_line/sale_order_line.js @@ -2,6 +2,7 @@ import { ProductCatalogSOL } from "@sale/js/product_catalog/sale_order_line/sale_order_line"; import { patch } from "@web/core/utils/patch"; +import { _t } from "@web/core/l10n/translation"; patch(ProductCatalogSOL, { props: { @@ -9,3 +10,15 @@ patch(ProductCatalogSOL, { deliveredQty: Number, }, }); + +patch(ProductCatalogSOL.prototype, { + get disableRemove() { + return this.props.quantity === this.props.deliveredQty; + }, + get disabledButtonTooltip() { + if (this.disableRemove) { + return _t("The ordered quantity cannot be decreased below the amount already delivered. Instead, create a return in your inventory."); + } + return super.disabledButtonTooltip; + }, +}); diff --git a/addons/sale_stock/views/sale_product_kanban_views.xml b/addons/sale_stock/views/sale_product_kanban_views.xml index ba564496aab..4ea42c6f5b2 100644 --- a/addons/sale_stock/views/sale_product_kanban_views.xml +++ b/addons/sale_stock/views/sale_product_kanban_views.xml @@ -15,6 +15,9 @@ + + View Availability +