[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) <xbo@odoo.com>
This commit is contained in:
Bastien (bvdn)
2023-10-12 13:16:39 +00:00
parent 569421ca8c
commit 94fbc58c1b
8 changed files with 89 additions and 26 deletions
+1 -1
View File
@@ -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: {
@@ -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
@@ -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(),
}
);
}
}
@@ -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 });
}
@@ -13,7 +13,9 @@
<div t-if="isInSaleOrder()"
class="input-group o_sale_product_catalog_quantity o_sale_product_catalog_cancel_global_click w-50">
<button class="btn btn-primary border"
t-on-click="this.env.decreaseQuantity">
t-on-click="this.env.decreaseQuantity"
t-att-disabled="disableRemove"
t-att-data-tooltip="disabledButtonTooltip">
<i class="fa fa-minus center"/>
</button>
<input class="o_input form-control border text-center text-bg-light"
@@ -32,12 +34,14 @@
<i class="fa fa-shopping-cart"/>
Add
</button>
<button t-else=""
t-on-click="this.env.removeProduct"
class="btn btn-light border">
<i class="fa fa-trash"/>
Remove
</button>
<div t-else="" class="o_tooltip_div_remove" t-att-data-tooltip="this.disabledButtonTooltip">
<button t-on-click="this.env.removeProduct"
t-att-disabled="disableRemove"
class="btn btn-light border">
<i class="fa fa-trash"/>
Remove
</button>
</div>
</div>
</div>
</t>
@@ -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));
}
},
});
@@ -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;
},
});
@@ -15,6 +15,9 @@
<field name="qty_available"/>
</div>
</div>
<a role="menuitem" type="edit" position="after">
<a role="menuitem" type="object" name="action_product_forecast_report" class="dropdown-item border-top-0">View Availability</a>
</a>
</field>
</record>