From 94fbc58c1b94d5eb2286fb80f33ae63581d6ea9d Mon Sep 17 00:00:00 2001 From: "Bastien (bvdn)" Date: Thu, 17 Aug 2023 14:56:58 +0200 Subject: [PATCH] [IMP] sale_stock: adapt product_catalog for stock module When the stock module is installed some conditions changes in the sale orders Here's the list and the implementation made to help this Patched the kanban_record so that the asked_qty of a product cannot go below the delivered_qty the delivered_qty is added in the productCatalogData of the record through the _get_catalog_info() hook in the following PR: https://github.com/odoo/odoo/pull/132341/commits Patched the sale_order_line xml to disabled removeProduct and decreaseProduct buttons when the quantity is equal to the delivered quantity if the user tries to remove an already delivered product it'll set to the delivered quantity instead of 0 if the user tries to set a quantity that is below the delivered quantity it'll set it to the delivered quantity Added action action_product_forecast_report to the menuitem of the products Task-3343547 closes odoo/odoo#127161 Related: odoo/enterprise#43469 Related: odoo/upgrade#4887 Signed-off-by: Xavier Bol (xbo) --- addons/sale/controllers/catalog.py | 2 +- .../js/product_catalog/kanban_controller.js | 24 ++++++++++------- .../src/js/product_catalog/kanban_renderer.js | 27 ++++++++++++------- .../sale_order_line/sale_order_line.js | 8 ++++++ .../sale_order_line/sale_order_line.xml | 18 ++++++++----- .../src/product_catalog/kanban_record.js | 20 ++++++++++++++ .../sale_order_line/sale_order_line.js | 13 +++++++++ .../views/sale_product_kanban_views.xml | 3 +++ 8 files changed, 89 insertions(+), 26 deletions(-) create mode 100644 addons/sale_stock/static/src/product_catalog/kanban_record.js 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 +