[IMP] sale_stock_on_hand_popup: open in a popup

This commit is contained in:
Alessio Renda
2024-02-01 18:40:50 +01:00
parent 2cd2b9cd19
commit 85ec8effe2
21 changed files with 263 additions and 96 deletions

View File

@@ -7,7 +7,7 @@ Sale Stock On Hand Popup
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:8afb561c639c15549500e32ef695b6653bda17d45ad957f6cc908e525343b79c
!! source digest: sha256:be440b588a162856106f68611bf86c8d8e0c1a27641a9ebb7b2fedcfc0d5b905
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png

View File

@@ -1 +1,2 @@
from . import models
from . import wizards

View File

@@ -7,8 +7,15 @@
"version": "14.0.1.0.1",
"license": "AGPL-3",
"depends": ["sale_stock", "stock_available"],
"data": ["views/sale_order_views.xml"],
"qweb": ["static/src/xml/sale_stock.xml"],
"data": [
"security/ir.model.access.csv",
"security/security.xml",
"views/assets.xml",
"views/res_config_settings_views.xml",
"views/sale_order_views.xml",
"wizards/product_quant_wizard_views.xml",
],
"qweb": ["static/src/xml/stock_on_hand_widget.xml"],
"installable": True,
"application": False,
}

View File

@@ -1 +1,2 @@
from . import product
from . import product_product
from . import res_config_settings

View File

@@ -1,20 +0,0 @@
from odoo import _, models
class Product(models.Model):
_inherit = "product.product"
def action_open_quants_show_products(self):
res = self.action_open_quants()
res.update(
{
"name": _("Stock Lines"),
"context": {
**res.get("context", {}),
"single_product": False,
"edit": False,
},
}
)
return res

View File

@@ -0,0 +1,20 @@
from odoo import models
class Product(models.Model):
_inherit = "product.product"
def action_open_quants_show_products(self):
self.ensure_one()
return {
"name": self.display_name,
"view_type": "form",
"view_mode": "form",
"res_model": "product.quant.wizard",
"type": "ir.actions.act_window",
"target": "new",
"context": {
**self.env.context,
"default_product_id": self.id,
},
}

View File

@@ -0,0 +1,11 @@
from odoo import fields, models
class ResConfigSettings(models.TransientModel):
_inherit = "res.config.settings"
group_show_transit_location_stock_wizard = fields.Boolean(
"Display also transit location lines in sale line stock popup.",
implied_group="sale_stock_on_hand_popup.group_show_transit_location_stock_wizard",
default=False,
)

View File

@@ -0,0 +1,2 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_product_quant_wizard,access_product_quant_wizard,model_product_quant_wizard,base.group_user,1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_product_quant_wizard access_product_quant_wizard model_product_quant_wizard base.group_user 1 1 1 1

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<record id="group_show_transit_location_stock_wizard" model="res.groups">
<field
name="name"
>Display transit location lines in sale line stock popup</field>
<field name="category_id" ref="base.module_category_hidden" />
</record>
</odoo>

View File

@@ -367,7 +367,7 @@ ul.auto-toc {
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:8afb561c639c15549500e32ef695b6653bda17d45ad957f6cc908e525343b79c
!! source digest: sha256:be440b588a162856106f68611bf86c8d8e0c1a27641a9ebb7b2fedcfc0d5b905
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
<p><a class="reference external image-reference" href="https://odoo-community.org/page/development-status"><img alt="Beta" src="https://img.shields.io/badge/maturity-Beta-yellow.png" /></a> <a class="reference external image-reference" href="http://www.gnu.org/licenses/agpl-3.0-standalone.html"><img alt="License: AGPL-3" src="https://img.shields.io/badge/licence-AGPL--3-blue.png" /></a> <a class="reference external image-reference" href="https://github.com/OCA/stock-logistics-warehouse/tree/14.0/sale_stock_on_hand_popup"><img alt="OCA/stock-logistics-warehouse" src="https://img.shields.io/badge/github-OCA%2Fstock--logistics--warehouse-lightgray.png?logo=github" /></a> <a class="reference external image-reference" href="https://translation.odoo-community.org/projects/stock-logistics-warehouse-14-0/stock-logistics-warehouse-14-0-sale_stock_on_hand_popup"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external image-reference" href="https://runboat.odoo-community.org/builds?repo=OCA/stock-logistics-warehouse&amp;target_branch=14.0"><img alt="Try me on Runboat" src="https://img.shields.io/badge/runboat-Try%20me-875A7B.png" /></a></p>
<p>This module allows user to check from sale order line all inventory lines related to that product.</p>

View File

@@ -1,29 +0,0 @@
odoo.define("sale_stock_on_hand_popup.QtyAtDateWidget", function (require) {
"use strict";
const QtyAtDateWidget = require("sale_stock.QtyAtDateWidget");
QtyAtDateWidget.include({
async _openStock(ev) {
ev.stopPropagation();
return this.trigger_up("button_clicked", {
attrs: {
name: "action_open_quants_show_products",
type: "object",
},
record: this.data.product_id,
});
},
_getContent() {
const $content = this._super.apply(this, arguments);
if ($content) {
$content.on("click", ".action_open_stock", this._openStock.bind(this));
}
return $content;
},
});
return QtyAtDateWidget;
});

View File

@@ -0,0 +1,47 @@
odoo.define("sale_stock_on_hand_popup.StockOnHandWidget", function (require) {
"use strict";
var Widget = require("web.Widget");
var widget_registry = require("web.widget_registry");
var StockOnHandWidget = Widget.extend({
template: "sale_stock_on_hand_popup.qtyOnHand",
events: _.extend({}, Widget.prototype.events, {
"click .fa-arrow-right": "_onClickButton",
}),
/**
* @override
* @param {Widget|null} parent
* @param {Object} params
*/
init: function (parent, params) {
this.data = params.data;
this.fields = params.fields;
this._super(parent);
},
updateState: function (state) {
var candidate = state.data[this.getParent().currentRow];
if (candidate) {
this.data = candidate.data;
this.renderElement();
}
},
_onClickButton: async function (ev) {
ev.stopPropagation();
var action = await this._rpc({
model: "product.product",
method: "action_open_quants_show_products",
args: [[this.data.product_id.data.id]],
});
action.views = [[false, "form"]];
return this.do_action(action);
},
});
widget_registry.add("stock_on_hand_widget", StockOnHandWidget);
return StockOnHandWidget;
});

View File

@@ -1,20 +0,0 @@
<?xml version="1.0" encoding="utf-8" ?>
<templates>
<t
t-name="sale_stock_available_info_popup.QtyDetailPopOver"
t-inherit="sale_stock.QtyDetailPopOver"
t-inherit-mode="extension"
owl="1"
>
<xpath expr="//button" position="before">
<button
t-if="!data.is_mto"
class="text-left btn btn-link action_open_stock"
type="button"
>
<i class="fa fa-fw o_button_icon fa-arrow-right" />
View Stock
</button>
</xpath>
</t>
</templates>

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8" ?>
<templates>
<div t-name="sale_stock_on_hand_popup.qtyOnHand">
<div t-att-class="!widget.data.display_qty_widget ? 'invisible' : ''">
<a tabindex="0" t-attf-class="fa fa-arrow-right" />
</div>
</div>
</templates>

View File

@@ -5,20 +5,32 @@ class TestSaleStockOnHandPopup(common.SavepointCase):
@classmethod
def setUpClass(cls):
super().setUpClass()
cls.Product = cls.env["product.product"]
cls.product_1 = cls.env.ref("product.product_product_1")
cls.product_2 = cls.env.ref("product.product_product_2")
cls.product_1 = cls.env.ref("product.product_product_6")
cls.product_2 = cls.env.ref("product.product_product_7")
def test_action_open_quants_show_products(self):
action_data = self.product_1.action_open_quants_show_products()
self.assertNotEqual(
action_data,
self.product_1.action_open_quants(),
)
self.assertEqual("Stock Lines", action_data.get("name"))
self.assertEqual(self.product_1.display_name, action_data.get("name"))
context = action_data.get("context")
self.assertFalse(context.get("single_product", True))
self.assertFalse(context.get("edit", True))
self.assertEqual(self.product_1.id, context.get("default_product_id"))
def test_get_stock_quant(self):
wiz_prod_1 = self.env["product.quant.wizard"].create(
{
"product_id": self.product_1.id,
}
)
wiz_prod_2 = self.env["product.quant.wizard"].create(
{
"product_id": self.product_2.id,
}
)
(wiz_prod_1 | wiz_prod_2)._compute_stock_quant_ids()
self.assertNotEqual(wiz_prod_1.stock_quant_ids, wiz_prod_2.stock_quant_ids)
self.assertNotEqual(
wiz_prod_1.stock_quant_ids, wiz_prod_1.product_id.stock_quant_ids
)

View File

@@ -0,0 +1,15 @@
<?xml version="1.0" ?>
<odoo>
<template
id="assets_backend"
name="sale stock assets"
inherit_id="web.assets_backend"
>
<xpath expr="." position="inside">
<script
type="text/javascript"
src="/sale_stock_on_hand_popup/static/src/js/stock_on_hand_widget.js"
/>
</xpath>
</template>
</odoo>

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<record id="view_stock_configuration" model="ir.ui.view">
<field name="name">Stock settings: Display transit location lines</field>
<field name="model">res.config.settings</field>
<field name="inherit_id" ref="stock.res_config_settings_view_form" />
<field name="arch" type="xml">
<xpath expr="//div[@data-key='stock']" position="inside">
<h2 id="stock_on_hand_info">Sale Stock on Hand</h2>
<div class="row mt16 o_settings_container">
<div class="col-xs-12 col-md-6 o_setting_box">
<div class="o_setting_left_pane">
<field name="group_show_transit_location_stock_wizard" />
</div>
<div class="o_setting_right_pane">
<label for="group_show_transit_location_stock_wizard" />
</div>
</div>
</div>
</xpath>
</field>
</record>
</odoo>

View File

@@ -1,15 +1,24 @@
<?xml version="1.0" ?>
<odoo>
<template
id="sale_order_line_view_list"
name="sale.order.line.view.list"
inherit_id="web.assets_backend"
>
<xpath expr="." position="inside">
<script
type="text/javascript"
src="/sale_stock_on_hand_popup/static/src/js/qty_at_date_widget.js"
/>
</xpath>
</template>
<record id="view_order_form_inherit_sale_stock_qty" model="ir.ui.view">
<field name="model">sale.order</field>
<field
name="inherit_id"
ref="sale_stock.view_order_form_inherit_sale_stock_qty"
/>
<field name="arch" type="xml">
<xpath
expr="//field[@name='order_line']/form/group/group/div[@name='ordered_qty']/widget[@name='qty_at_date_widget']"
position="after"
>
<widget name="stock_on_hand_widget" width="0.1px" />
</xpath>
<xpath
expr="//field[@name='order_line']/tree/widget[@name='qty_at_date_widget']"
position="after"
>
<widget name="stock_on_hand_widget" width="20px" />
</xpath>
</field>
</record>
</odoo>

View File

@@ -0,0 +1 @@
from . import product_quant_wizard

View File

@@ -0,0 +1,22 @@
from odoo import api, fields, models
class ProductQuantWizard(models.TransientModel):
_name = "product.quant.wizard"
_description = "Product Quant Wizard"
product_id = fields.Many2one("product.product")
stock_quant_ids = fields.Many2many(
"stock.quant", compute="_compute_stock_quant_ids"
)
@api.depends("product_id")
def _compute_stock_quant_ids(self):
for rec in self:
rec.stock_quant_ids = rec.product_id.stock_quant_ids.filtered(
lambda x: x.location_id.usage in ["internal", "transit"]
if self.env.user.has_group(
"sale_stock_on_hand_popup.group_show_transit_location_stock_wizard"
)
else x.location_id.usage == "internal"
)

View File

@@ -0,0 +1,48 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<record id="product_quant_wizard_view_form" model="ir.ui.view">
<field name="name">product.quant.wizard.form</field>
<field name="model">product.quant.wizard</field>
<field name="arch" type="xml">
<form>
<field name="product_id" invisible="1" />
<field name="stock_quant_ids" readonly="1">
<tree>
<field name="product_id" optional="hide" />
<field name="location_id" optional="show" />
<field
name="lot_id"
groups="stock.group_production_lot"
optional="show"
/>
<field
name="package_id"
groups="stock.group_tracking_lot"
optional="show"
/>
<field
name="owner_id"
groups="stock.group_tracking_owner"
optional="show"
/>
<field name="available_quantity" optional="show" />
<field name="quantity" optional="show" />
<field
name="product_uom_id"
groups="uom.group_uom"
optional="show"
/>
<field
name='company_id'
groups="base.group_multi_company"
optional="show"
/>
</tree>
</field>
<footer>
<button string="Close" class="btn-secondary" special="cancel" />
</footer>
</form>
</field>
</record>
</odoo>