[IMP] purchase_requsition: convert custom js to OWL
For original specs, see [1]. Some performance changes: the best price/date/unit price fields are now loaded by JS instead of passed via action context (allows for easier/ consistent reuse of view + now we can refresh without undoing any state changes due to buttons being pushed). [1] https://github.com/odoo/odoo/commit/f533e40f0e3f1cd34f1047b70dfdf95837d84501 closes odoo/odoo#101502 X-original-commit: 41ae508c2e6fc3c40b29b1bfc4b5dd7c4440a719 Signed-off-by: Antony Lesuisse <al@odoo.com> Signed-off-by: Tiffany Chang <tic@odoo.com>
This commit is contained in:
@@ -27,12 +27,10 @@ are agreements you have with vendors to benefit from a predetermined pricing.
|
||||
],
|
||||
'assets': {
|
||||
'web.assets_backend': [
|
||||
'purchase_requisition/static/src/js/purchase_order_alternatives_widget.js',
|
||||
'purchase_requisition/static/src/js/purchase_order_compare_list_renderer.js',
|
||||
'purchase_requisition/static/src/js/purchase_order_line_compare_list_controller.js',
|
||||
'purchase_requisition/static/src/js/purchase_order_line_compare_list_renderer.js',
|
||||
'purchase_requisition/static/src/js/purchase_order_line_compare_list_view.js',
|
||||
'purchase_requisition/static/src/xml/purchase_order_line_compare.xml',
|
||||
'purchase_requisition/static/src/*/**.js',
|
||||
'purchase_requisition/static/src/views/*/**.js',
|
||||
'purchase_requisition/static/src/*/**.scss',
|
||||
'purchase_requisition/static/src/*/**.xml',
|
||||
],
|
||||
},
|
||||
'license': 'LGPL-3',
|
||||
|
||||
@@ -195,15 +195,9 @@ class PurchaseOrder(models.Model):
|
||||
}
|
||||
|
||||
def action_compare_alternative_lines(self):
|
||||
best_price_ids, best_date_ids, best_price_unit_ids = self.get_tender_best_lines()
|
||||
ctx = dict(
|
||||
self.env.context,
|
||||
search_default_groupby_product=True,
|
||||
params={
|
||||
'best_price_ids': best_price_ids,
|
||||
'best_date_ids': best_date_ids,
|
||||
'best_price_unit_ids': best_price_unit_ids,
|
||||
},
|
||||
)
|
||||
view_id = self.env.ref('purchase_requisition.purchase_order_line_compare_tree').id
|
||||
return {
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import field_registry from 'web.field_registry';
|
||||
import { FieldMany2Many } from 'web.relational_fields';
|
||||
import PurchaseOrderCompareListRenderer from '@purchase_requisition/js/purchase_order_compare_list_renderer';
|
||||
|
||||
const FieldMany2ManySameWindow = FieldMany2Many.extend({
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
init: function (parent, name, record, options) {
|
||||
this._super(...arguments);
|
||||
this.isMany2Many = true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Override to ensure we can't unlink a PO from itself (i.e. confusing behavior)
|
||||
* @override
|
||||
*/
|
||||
_getRenderer: function () {
|
||||
if (this.view.arch.tag === 'tree') {
|
||||
return PurchaseOrderCompareListRenderer;
|
||||
}
|
||||
return this._super(...arguments);
|
||||
},
|
||||
|
||||
/**
|
||||
* Override to open record in same window w/breadcrumb extended
|
||||
* @override
|
||||
*/
|
||||
_onOpenRecord: function (ev) {
|
||||
ev.stopPropagation();
|
||||
const targetId = ev.target.state.data.find(obj => {
|
||||
return obj.id === ev.data.id;
|
||||
}).res_id;
|
||||
// don't re-open current record
|
||||
if (targetId !== this.record.res_id) {
|
||||
this._rpc({
|
||||
model: this.model,
|
||||
method: 'get_formview_action',
|
||||
args: [[targetId]],
|
||||
context: this.record.getContext(this.recordParams),
|
||||
domain: this.record.getDomain(this.recordParams),
|
||||
}).then(action => {
|
||||
this.trigger_up('do_action', {action: action});
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
field_registry.add('many2many_same_window', FieldMany2ManySameWindow);
|
||||
@@ -1,26 +0,0 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import ListRenderer from 'web.ListRenderer';
|
||||
|
||||
|
||||
const PurchaseOrderCompareListRenderer = ListRenderer.extend({
|
||||
init: function (parent, state, params) {
|
||||
this._super(...arguments);
|
||||
this.active_id = parent.res_id;
|
||||
},
|
||||
|
||||
/**
|
||||
* @override
|
||||
* @private
|
||||
* @returns {Promise}
|
||||
*/
|
||||
_renderRow: function (record) {
|
||||
const $tr = this._super(...arguments);
|
||||
if (record.res_id === this.active_id && $tr.find('.o_list_record_remove').length) {
|
||||
$tr.find(".o_list_record_remove").remove();
|
||||
}
|
||||
return $tr;
|
||||
}
|
||||
});
|
||||
|
||||
export default PurchaseOrderCompareListRenderer;
|
||||
-66
@@ -1,66 +0,0 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import ListController from 'web.ListController';
|
||||
|
||||
const PurchaseOrderLineCompareListController = ListController.extend({
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Public
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
init: function (parent, model, renderer, params) {
|
||||
this.context = renderer.state.getContext();
|
||||
this.best_date_ids = this.context.params.best_date_ids;
|
||||
this.best_price_ids = this.context.params.best_price_ids;
|
||||
this.best_price_unit_ids = this.context.params.best_price_unit_ids;
|
||||
return this._super(...arguments);
|
||||
},
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Handlers
|
||||
// -------------------------------------------------------------------------
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
_onButtonClicked: function (ev) {
|
||||
if (ev.data.attrs.class && ev.data.attrs.class.split(' ').includes('o_clear_qty_buttons')) {
|
||||
ev.stopPropagation();
|
||||
var self = this;
|
||||
return this._callButtonAction(ev.data.attrs, ev.data.record).then(() => {
|
||||
const context = this.model.localData[0] && this.model.localData[0].getContext() || {};
|
||||
return self._rpc({
|
||||
model: "purchase.order",
|
||||
method: 'get_tender_best_lines',
|
||||
args: [self.context.active_id],
|
||||
context: context,
|
||||
}).then((best_lines) => {
|
||||
self.renderer.best_date_ids = best_lines[0] || [];
|
||||
self.renderer.best_price_ids = best_lines[1] || [];
|
||||
self.renderer.best_price_unit_ids = best_lines[2] || [];
|
||||
self.reload();
|
||||
});
|
||||
});
|
||||
} else {
|
||||
this._super.apply(this, arguments);
|
||||
}
|
||||
},
|
||||
|
||||
_onHeaderButtonClicked: async function (node) {
|
||||
await this._super(...arguments);
|
||||
if (node.attrs.name && node.attrs.name === 'action_clear_quantities') {
|
||||
const context = this.model.localData[0] && this.model.localData[0].getContext() || {};
|
||||
const best_lines = await this._rpc({
|
||||
model: 'purchase.order',
|
||||
method: 'get_tender_best_lines',
|
||||
args: this.context.active_ids,
|
||||
context,
|
||||
});
|
||||
this.renderer.best_date_ids = best_lines[0];
|
||||
this.renderer.best_price_ids = best_lines[1];
|
||||
this.renderer.best_price_unit_ids = best_lines[2];
|
||||
this.reload();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
export default PurchaseOrderLineCompareListController;
|
||||
-35
@@ -1,35 +0,0 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import ListRenderer from 'web.ListRenderer';
|
||||
|
||||
|
||||
const PurchaseOrderLineCompareListRenderer = ListRenderer.extend({
|
||||
|
||||
init: function (parent, state, params) {
|
||||
this._super(...arguments);
|
||||
this.best_date_ids = this.state.context.params.best_date_ids;
|
||||
this.best_price_ids = this.state.context.params.best_price_ids;
|
||||
this.best_price_unit_ids = this.state.context.params.best_price_unit_ids;
|
||||
},
|
||||
|
||||
/**
|
||||
* @override
|
||||
* @private
|
||||
* @returns {Promise}
|
||||
*/
|
||||
_renderRow: function (record) {
|
||||
const $tr = this._super(...arguments);
|
||||
if (this.best_date_ids.includes(record.res_id)) {
|
||||
$tr.find('[name="date_planned"]').toggleClass('text-success');
|
||||
}
|
||||
if (this.best_price_ids.includes(record.res_id)) {
|
||||
$tr.find('[name="price_subtotal"]').toggleClass('text-success');
|
||||
}
|
||||
if (this.best_price_unit_ids.includes(record.res_id)) {
|
||||
$tr.find('[name="price_unit"]').toggleClass('text-success');
|
||||
}
|
||||
return $tr;
|
||||
}
|
||||
});
|
||||
|
||||
export default PurchaseOrderLineCompareListRenderer;
|
||||
@@ -1,17 +0,0 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import ListView from 'web.ListView';
|
||||
import PurchaseOrderLineCompareListController from '@purchase_requisition/js/purchase_order_line_compare_list_controller';
|
||||
import PurchaseOrderLineCompareListRenderer from '@purchase_requisition/js/purchase_order_line_compare_list_renderer';
|
||||
import viewRegistry from 'web.view_registry';
|
||||
|
||||
const PurchaseOrderLineCompareListView = ListView.extend({
|
||||
config: Object.assign({}, ListView.prototype.config, {
|
||||
Controller: PurchaseOrderLineCompareListController,
|
||||
Renderer: PurchaseOrderLineCompareListRenderer,
|
||||
}),
|
||||
});
|
||||
|
||||
viewRegistry.add('purchase_order_line_compare', PurchaseOrderLineCompareListView);
|
||||
|
||||
export default PurchaseOrderLineCompareListView;
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
/** @odoo-module */
|
||||
|
||||
import { ListRenderer } from "@web/views/list/list_renderer";
|
||||
|
||||
const { onWillStart, useState, useSubEnv } = owl;
|
||||
|
||||
export class PurchaseOrderLineCompareListRenderer extends ListRenderer {
|
||||
setup() {
|
||||
super.setup();
|
||||
this.bestFields = useState({
|
||||
best_price_ids: [],
|
||||
best_date_ids: [],
|
||||
best_price_unit_ids: [],
|
||||
});
|
||||
onWillStart(async () => {
|
||||
await this.updateBestFields();
|
||||
});
|
||||
const defaultOnClickViewButton = this.env.onClickViewButton;
|
||||
useSubEnv({
|
||||
onClickViewButton: async (params) => {
|
||||
await defaultOnClickViewButton(params);
|
||||
await this.updateBestFields();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async updateBestFields() {
|
||||
[this.bestFields.best_price_ids,
|
||||
this.bestFields.best_date_ids,
|
||||
this.bestFields.best_price_unit_ids] = await this.props.list.model.orm.call(
|
||||
"purchase.order",
|
||||
"get_tender_best_lines",
|
||||
[this.props.list.context.active_id],
|
||||
{ context: this.props.list.context }
|
||||
);
|
||||
}
|
||||
|
||||
getCellClass(column, record) {
|
||||
let classNames = super.getCellClass(...arguments);
|
||||
const customClassNames = [];
|
||||
if (column.name === "price_subtotal" && this.bestFields.best_price_ids.includes(record.resId)) {
|
||||
customClassNames.push("text-success");
|
||||
}
|
||||
if (column.name === "date_planned" && this.bestFields.best_date_ids.includes(record.resId)) {
|
||||
customClassNames.push("text-success");
|
||||
}
|
||||
if (column.name === "price_unit" && this.bestFields.best_price_unit_ids.includes(record.resId)) {
|
||||
customClassNames.push("text-success");
|
||||
}
|
||||
return classNames.concat(" ", customClassNames.join(" "));
|
||||
}
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import { listView } from '@web/views/list/list_view';
|
||||
import { registry } from "@web/core/registry";
|
||||
import { PurchaseOrderLineCompareListRenderer } from "./purchase_order_line_compare_list_renderer";
|
||||
|
||||
|
||||
export const PurchaseOrderLineCompareListView = {
|
||||
...listView,
|
||||
Renderer: PurchaseOrderLineCompareListRenderer,
|
||||
};
|
||||
|
||||
registry.category("views").add("purchase_order_line_compare", PurchaseOrderLineCompareListView);
|
||||
@@ -0,0 +1,46 @@
|
||||
/** @odoo-module */
|
||||
|
||||
import { registry } from "@web/core/registry";
|
||||
import { useService } from "@web/core/utils/hooks";
|
||||
import { X2ManyField } from "@web/views/fields/x2many/x2many_field";
|
||||
import { ListRenderer } from "@web/views/list/list_renderer";
|
||||
|
||||
|
||||
export class FieldMany2ManyAltPOsRenderer extends ListRenderer {
|
||||
isCurrentRecord(record) {
|
||||
return record.data.id === this.env.model.root.data.id;
|
||||
}
|
||||
}
|
||||
|
||||
FieldMany2ManyAltPOsRenderer.recordRowTemplate = "purchase_requisition.AltPOsListRenderer.RecordRow";
|
||||
|
||||
export class FieldMany2ManyAltPOs extends X2ManyField {
|
||||
setup() {
|
||||
this.orm = useService("orm");
|
||||
this.action = useService("action");
|
||||
// TODO: this is a terrible hack, make this a proper extension of many2many if/when possible
|
||||
this.props.record.activeFields[this.props.name].widget = "many2many";
|
||||
super.setup();
|
||||
}
|
||||
|
||||
/**
|
||||
* Override to: avoid reopening currently open record
|
||||
* open record in same window w/breadcrumb extended
|
||||
* @override
|
||||
*/
|
||||
async openRecord(record) {
|
||||
if (record.data.id !== this.props.record.data.id) {
|
||||
const action = await this.orm.call(record.resModel, "get_formview_action", [[record.data.id]], {
|
||||
context: this.props.context,
|
||||
});
|
||||
await this.action.doAction(action);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FieldMany2ManyAltPOs.components = {
|
||||
...X2ManyField.components,
|
||||
ListRenderer: FieldMany2ManyAltPOsRenderer,
|
||||
};
|
||||
|
||||
registry.category("fields").add("many2many_alt_pos", FieldMany2ManyAltPOs);
|
||||
@@ -0,0 +1,3 @@
|
||||
.o_field_many2many_alt_pos {
|
||||
width: 100%;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates xml:space="preserve">
|
||||
|
||||
<!-- Ensure we can't unlink a PO from itself (i.e. confusing behavior) -->
|
||||
<t t-name="purchase_requisition.AltPOsListRenderer.RecordRow" t-inherit="web.ListRenderer.RecordRow" t-inherit-mode="primary" owl="1">
|
||||
<xpath expr="//t[@t-if='displayOptionalFields or activeActions']" position="attributes">
|
||||
<attribute name="t-if">activeActions and !isCurrentRecord(record)</attribute>
|
||||
</xpath>
|
||||
</t>
|
||||
|
||||
</templates>
|
||||
@@ -1,12 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<templates id="template" xml:space="preserve">
|
||||
|
||||
<t t-extend="ListView.buttons" t-name="PurchaseOrderLineCompare.Buttons">
|
||||
<t t-jquery="button.o_list_button_discard" t-operation="after">
|
||||
<button class="btn btn-secondary o_clear_qty_buttons me-2" type="button">
|
||||
Clear Selected
|
||||
</button>
|
||||
</t>
|
||||
</t>
|
||||
|
||||
</templates>
|
||||
@@ -150,11 +150,13 @@ class TestPurchaseRequisition(TestPurchaseRequisitionCommon):
|
||||
alt_po_1.order_line.date_planned += timedelta(days=1)
|
||||
alt_po_1.order_line.price_unit = unit_price - 10
|
||||
action = orig_po.action_compare_alternative_lines()
|
||||
params = action['context']['params']
|
||||
best_price_pol = self.env['purchase.order.line'].browse(params['best_price_ids'])
|
||||
best_date_pol = self.env['purchase.order.line'].browse(params['best_date_ids'])
|
||||
best_price_ids, best_date_ids, best_price_unit_ids = orig_po.get_tender_best_lines()
|
||||
best_price_pol = self.env['purchase.order.line'].browse(best_price_ids)
|
||||
best_date_pol = self.env['purchase.order.line'].browse(best_date_ids)
|
||||
best_unit_price_pol = self.env['purchase.order.line'].browse(best_price_unit_ids)
|
||||
self.assertEqual(best_price_pol.order_id.id, alt_po_1.id, "Best price PO line was not correctly calculated")
|
||||
self.assertEqual(best_date_pol.order_id.id, orig_po.id, "Best date PO line was not correctly calculated")
|
||||
self.assertEqual(best_unit_price_pol.order_id.id, alt_po_1.id, "Best unit price PO line was not correctly calculated")
|
||||
|
||||
# second flow: create extra alt PO, check that all 3 POs are correctly auto-linked
|
||||
action = orig_po.action_create_alternative()
|
||||
|
||||
@@ -28,8 +28,8 @@
|
||||
</p>
|
||||
</group>
|
||||
</group>
|
||||
<field name="alternative_po_ids" attrs="{'readonly': [('id', '=', False)]}" widget="many2many_same_window" context="{'quotation_only': True}">
|
||||
<tree string="Alternative Purchase Order" create="0" decoration-muted="state in ['cancel', 'purchase', 'done']" decoration-bf="id == parent.id">
|
||||
<field name="alternative_po_ids" attrs="{'readonly': [('id', '=', False)]}" widget="many2many_alt_pos" context="{'quotation_only': True}">
|
||||
<tree string="Alternative Purchase Order" decoration-muted="state in ['cancel', 'purchase', 'done']" decoration-bf="id == parent.id">
|
||||
<control>
|
||||
<create string="Link to Existing RfQ"/>
|
||||
</control>
|
||||
|
||||
Reference in New Issue
Block a user