[REF] barcodes: simplify 'O-CMD.' implementation

With this commit, we simplify the way barcodes prefixed by `O-CMD.` are
handled, by simply executing a ui action whenever it happens. The code
is much simpler, instead of having to add a widget=barcode_handler in a
specific view.

Note that it changes the behaviour: now, barcodes are available
everywhere, instead of just a selection of form views. This seems more
natural, since we consider that the barcode scanner is basically some
kind of input device.

Part-of: odoo/odoo#95892
This commit is contained in:
Géry Debongnie
2022-07-19 11:50:31 +02:00
parent 6acfad2651
commit ce6f13c3cd
3 changed files with 84 additions and 109 deletions
+73 -14
View File
@@ -2,29 +2,88 @@
import { registry } from "@web/core/registry";
import { getVisibleElements } from "@web/core/utils/ui";
import { MacroEngine } from "@web/core/macro";
export const barcodeAutoClick = {
function clickOnButton(selector) {
const button = document.body.querySelector(selector);
if (button) {
button.click();
}
}
function updatePager(position) {
const pager = document.body.querySelector("nav.o_pager");
if (!pager || pager.innerText.includes("-")) {
// we don't change pages if we are in a multi record view
return;
}
let next;
if (position === "first") {
next = 1;
} else {
next = parseInt(pager.querySelector(".o_pager_limit").textContent, 10);
}
let current = parseInt(pager.innerText.split('/')[0], 10);
if (current === next) {
return;
}
const engine = new MacroEngine();
engine.activate({
name: "updating pager",
timeout: 1000,
interval: 0,
steps: [
{
trigger: "span.o_pager_value",
action: "click"
},
{
trigger: "input.o_pager_value",
action: "text",
value: next
}
]
});
}
const COMMANDS = {
"O-CMD.EDIT": () => clickOnButton(".o_form_button_edit"),
"O-CMD.DISCARD": () => clickOnButton(".o_form_button_cancel"),
"O-CMD.SAVE": () => clickOnButton(".o_form_button_save"),
"O-CMD.PREV": () => clickOnButton(".o_pager_previous"),
"O-CMD.NEXT": () => clickOnButton(".o_pager_next"),
"O-CMD.PAGER-FIRST": () => updatePager("first"),
"O-CMD.PAGER-LAST": () => updatePager("last"),
};
export const barcodeGenericHandlers = {
dependencies: ["ui", "barcode"],
start(env, { ui, barcode }) {
barcode.bus.addEventListener("barcode_scanned", (ev) => {
const barcode = ev.detail.barcode;
if (!barcode.startsWith("O-BTN.")) {
return;
if (barcode.startsWith("O-BTN.")) {
let targets = [];
try {
// the scanned barcode could be anything, and could crash the queryselectorall
// function
targets = getVisibleElements(ui.activeElement, `[barcode_trigger=${barcode.slice(6)}]`);
} catch (_e) {
console.warn(`Barcode '${barcode}' is not valid`);
}
for (let elem of targets) {
elem.click();
}
}
let targets = [];
try {
// the scanned barcode could be anything, and could crash the queryselectorall
// function
targets = getVisibleElements(ui.activeElement, `[barcode_trigger=${barcode.slice(6)}]`);
} catch (_e) {
console.warn(`Barcode '${barcode}' is not valid`)
}
for (let elem of targets) {
elem.click();
if (barcode.startsWith("O-CMD.")) {
const fn = COMMANDS[barcode];
if (fn) {
fn();
} else {
console.warn(`Barcode '${barcode}' is not a valid command`);
}
}
});
}
};
registry.category("services").add("barcode_autoclick", barcodeAutoClick);
registry.category("services").add("barcode_handlers", barcodeGenericHandlers);
@@ -25,13 +25,6 @@ FormController.include({
this.activeBarcode = {
form_view: {
commands: {
'O-CMD.EDIT': this._barcodeEdit.bind(this),
'O-CMD.DISCARD': this._barcodeDiscard.bind(this),
'O-CMD.SAVE': this._barcodeSave.bind(this),
'O-CMD.PREV': this._barcodePagerPrevious.bind(this),
'O-CMD.NEXT': this._barcodePagerNext.bind(this),
'O-CMD.PAGER-FIRST': this._barcodePagerFirst.bind(this),
'O-CMD.PAGER-LAST': this._barcodePagerLast.bind(this),
},
},
};
@@ -71,84 +64,6 @@ FormController.include({
return this._barcodeWithoutCandidate(record, barcode, activeBarcode);
}
},
/**
* @private
*/
_barcodeDiscard: function () {
return this.discardChanges();
},
/**
* @private
*/
_barcodeEdit: function () {
return this._setMode('edit');
},
/**
* @private
*/
_barcodePagerFirst: async function () {
return this._updatePage(() => 1);
},
/**
* @private
*/
_barcodePagerLast: async function () {
return this._updatePage((min, state) => state.count);
},
/**
* @private
*/
_barcodePagerNext: function () {
return this._updatePage((min, state) => {
min += 1;
if (min > state.count) {
min = 1;
}
return min;
});
},
/**
* @private
*/
_barcodePagerPrevious: function () {
return this._updatePage((min, state) => {
min -= 1;
if (min < 1) {
min = state.count;
}
return min;
});
},
/**
* Change the current minimum value of the pager using provided function.
* This function will be given the current minimum and state and must return
* the updated value.
*
* @private
* @param {Function(currentMin: Number, state: Object)} updater
*/
_updatePage: async function (updater) {
await this.mutex.exec(() => {});
const state = this.model.get(this.handle, { raw: true });
const pagingInfo = this._getPagingInfo(state);
if (!pagingInfo) {
return this.displayNotification({ message: _t('Pager unavailable'), type: 'danger' });
}
const currentMinimum = updater(pagingInfo.currentMinimum, state);
const limit = pagingInfo.limit;
const reloadParams = state.groupedBy && state.groupedBy.length ? {
groupsLimit: limit,
groupsOffset: currentMinimum - 1,
} : {
limit,
offset: currentMinimum - 1,
};
await this.reload(reloadParams);
// reset the scroll position to the top on page changed only
if (state.limit === limit) {
this.trigger_up('scrollTo', { top: 0 });
}
},
/**
* Returns true iff the given barcode matches the given record (candidate).
*
@@ -161,12 +76,6 @@ FormController.include({
_barcodeRecordFilter: function (candidate, barcode, activeBarcode) {
return candidate.data.product_barcode === barcode;
},
/**
* @private
*/
_barcodeSave: function () {
return this.saveRecord();
},
/**
* @private
* @param {Object} candidate: record in the x2m
+11 -4
View File
@@ -2,10 +2,11 @@ odoo.define('barcodes.tests', function (require) {
"use strict";
const {barcodeService} = require("@barcodes/barcode_service");
const {barcodeAutoClick} = require("@barcodes/barcode_handlers");
const {barcodeGenericHandlers} = require("@barcodes/barcode_handlers");
const {barcodeRemapperService} = require("@barcodes/js/barcode_events");
const { makeTestEnv } = require("@web/../tests/helpers/mock_env");
const { registry } = require("@web/core/registry");
const { mockTimeout } = require("@web/../tests/helpers/utils");
var AbstractField = require('web.AbstractField');
var fieldRegistry = require('web.field_registry');
@@ -40,7 +41,7 @@ QUnit.module('Barcodes', {
before() {
barcodeService.maxTimeBetweenKeysInMs = 0;
registry.category("services").add("barcode", barcodeService, { force: true});
registry.category("services").add("barcode_autoclick", barcodeAutoClick, { force: true});
registry.category("services").add("barcode_autoclick", barcodeGenericHandlers, { force: true});
// remove this one later
registry.category("services").add("barcode_remapper", barcodeRemapperService);
this.env = makeTestEnv();
@@ -172,10 +173,12 @@ QUnit.test('edit, save and cancel buttons', async function (assert) {
});
QUnit.test('pager buttons', async function (assert) {
const mock = mockTimeout();
assert.expect(5);
var form = await createView({
View: FormView,
debug: true,
model: 'product',
data: this.data,
arch: '<form><field name="display_name"/></form>',
@@ -197,11 +200,15 @@ QUnit.test('pager buttons', async function (assert) {
assert.strictEqual(form.$('.o_field_widget').text(), 'Large Cabinet');
// O-CMD.PAGER-LAST
simulateBarCode(["O","-","C","M","D",".","P","A","G","E","R","-","L","A","S","T","Enter"]);
await testUtils.nextTick();
// need to await 2 macro steps
await mock.advanceTime(20);
await mock.advanceTime(20);
assert.strictEqual(form.$('.o_field_widget').text(), 'Cabinet with Doors');
// O-CMD.PAGER-FIRST
simulateBarCode(["O","-","C","M","D",".","P","A","G","E","R","-","F","I","R","S","T","Enter"]);
await testUtils.nextTick();
// need to await 2 macro steps
await mock.advanceTime(20);
await mock.advanceTime(20);
assert.strictEqual(form.$('.o_field_widget').text(), 'Large Cabinet');
form.destroy();