392 lines
13 KiB
JavaScript
392 lines
13 KiB
JavaScript
/** @odoo-module **/
|
|
|
|
import { ChatterContainer } from '@mail/components/chatter_container/chatter_container';
|
|
|
|
import BarcodePickingModel from '@stock_barcode/models/barcode_picking_model';
|
|
import BarcodeQuantModel from '@stock_barcode/models/barcode_quant_model';
|
|
import { bus } from 'web.core';
|
|
import config from 'web.config';
|
|
import GroupedLineComponent from '@stock_barcode/components/grouped_line';
|
|
import LineComponent from '@stock_barcode/components/line';
|
|
import PackageLineComponent from '@stock_barcode/components/package_line';
|
|
import { registry } from "@web/core/registry";
|
|
import { useService } from "@web/core/utils/hooks";
|
|
import * as BarcodeScanner from '@web/webclient/barcode/barcode_scanner';
|
|
import { ConfirmationDialog } from "@web/core/confirmation_dialog/confirmation_dialog";
|
|
import { View } from "@web/views/view";
|
|
|
|
const { Component, onMounted, onPatched, onWillStart, onWillUnmount, useSubEnv } = owl;
|
|
|
|
/**
|
|
* Main Component
|
|
* Gather the line information.
|
|
* Manage the scan and save process.
|
|
*/
|
|
|
|
class MainComponent extends Component {
|
|
//--------------------------------------------------------------------------
|
|
// Lifecycle
|
|
//--------------------------------------------------------------------------
|
|
|
|
setup() {
|
|
this.rpc = useService('rpc');
|
|
this.orm = useService('orm');
|
|
this.notification = useService('notification');
|
|
this.props.model = this.props.action.res_model;
|
|
this.props.id = this.props.action.context.active_id;
|
|
const model = this._getModel(this.props);
|
|
useSubEnv({model});
|
|
this._scrollBehavior = 'smooth';
|
|
this.isMobile = config.device.isMobile;
|
|
|
|
onWillStart(async () => {
|
|
const barcodeData = await this.rpc(
|
|
'/stock_barcode/get_barcode_data',
|
|
{
|
|
model: this.props.model,
|
|
res_id: this.props.id || false,
|
|
}
|
|
);
|
|
this.groups = barcodeData.groups;
|
|
this.env.model.setData(barcodeData);
|
|
this.env.model.on('process-action', this, this._onDoAction);
|
|
this.env.model.on('notification', this, this._onNotification);
|
|
this.env.model.on('refresh', this, this._onRefreshState);
|
|
this.env.model.on('update', this, () => this.render(true));
|
|
this.env.model.on('do-action', this, args => bus.trigger('do-action', args));
|
|
this.env.model.on('history-back', this, () => this.env.config.historyBack());
|
|
});
|
|
|
|
onMounted(() => {
|
|
bus.on('barcode_scanned', this, this._onBarcodeScanned);
|
|
bus.on('edit-line', this, this._onEditLine);
|
|
bus.on('exit', this, this.exit);
|
|
bus.on('open-package', this, this._onOpenPackage);
|
|
bus.on('refresh', this, this._onRefreshState);
|
|
bus.on('warning', this, this._onWarning);
|
|
});
|
|
|
|
onWillUnmount(() => {
|
|
bus.off('barcode_scanned', this, this._onBarcodeScanned);
|
|
bus.off('edit-line', this, this._onEditLine);
|
|
bus.off('exit', this, this.exit);
|
|
bus.off('open-package', this, this._onOpenPackage);
|
|
bus.off('refresh', this, this._onRefreshState);
|
|
bus.off('warning', this, this._onWarning);
|
|
});
|
|
|
|
onPatched(() => {
|
|
this._scrollToSelectedLine();
|
|
});
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// Public
|
|
//--------------------------------------------------------------------------
|
|
|
|
get displayHeaderInfoAsColumn() {
|
|
return this.env.model.isDone || this.env.model.isCancelled;
|
|
}
|
|
|
|
get displayBarcodeApplication() {
|
|
return this.env.model.view === 'barcodeLines';
|
|
}
|
|
|
|
get displayBarcodeActions() {
|
|
return this.env.model.view === 'actionsView';
|
|
}
|
|
|
|
get displayBarcodeLines() {
|
|
return this.displayBarcodeApplication && this.env.model.canBeProcessed;
|
|
}
|
|
|
|
get displayInformation() {
|
|
return this.env.model.view === 'infoFormView';
|
|
}
|
|
|
|
get displayNote() {
|
|
return !this._hideNote && this.env.model.record.note;
|
|
}
|
|
|
|
get displayPackageContent() {
|
|
return this.env.model.view === 'packagePage';
|
|
}
|
|
|
|
get displayProductPage() {
|
|
return this.env.model.view === 'productPage';
|
|
}
|
|
|
|
get lineFormViewData() {
|
|
const data = this.env.model.viewsWidgetData;
|
|
data.context = data.additionalContext;
|
|
data.resId = this._editedLineParams && this._editedLineParams.currentId;
|
|
return data;
|
|
}
|
|
|
|
get highlightValidateButton() {
|
|
return this.env.model.highlightValidateButton;
|
|
}
|
|
|
|
get info() {
|
|
return this.env.model.barcodeInfo;
|
|
}
|
|
|
|
get isTransfer() {
|
|
return this.currentSourceLocation && this.currentDestinationLocation;
|
|
}
|
|
|
|
get lines() {
|
|
return this.env.model.groupedLines;
|
|
}
|
|
|
|
get mobileScanner() {
|
|
return BarcodeScanner.isBarcodeScannerSupported();
|
|
}
|
|
|
|
get packageLines() {
|
|
return this.env.model.packageLines;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// Private
|
|
//--------------------------------------------------------------------------
|
|
|
|
_getModel(params) {
|
|
const { rpc, orm, notification } = this;
|
|
if (params.model === 'stock.picking') {
|
|
return new BarcodePickingModel(params, { rpc, orm, notification });
|
|
} else if (params.model === 'stock.quant') {
|
|
return new BarcodeQuantModel(params, { rpc, orm, notification });
|
|
} else {
|
|
throw new Error('No JS model define');
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// Handlers
|
|
//--------------------------------------------------------------------------
|
|
|
|
async cancel() {
|
|
await this.env.model.save();
|
|
const action = await this.orm.call(
|
|
this.props.model,
|
|
'action_cancel_from_barcode',
|
|
[[this.props.id]]
|
|
);
|
|
const onClose = res => {
|
|
if (res && res.cancelled) {
|
|
this.env.model._cancelNotification();
|
|
this.env.config.historyBack();
|
|
}
|
|
};
|
|
bus.trigger('do-action', {
|
|
action,
|
|
options: {
|
|
on_close: onClose.bind(this),
|
|
},
|
|
});
|
|
}
|
|
|
|
async openMobileScanner() {
|
|
const barcode = await BarcodeScanner.scanBarcode();
|
|
if (barcode) {
|
|
this.env.model.processBarcode(barcode);
|
|
if ('vibrate' in window.navigator) {
|
|
window.navigator.vibrate(100);
|
|
}
|
|
} else {
|
|
this.env.services.notification.notify({
|
|
type: 'warning',
|
|
message: this.env._t("Please, Scan again !"),
|
|
});
|
|
}
|
|
}
|
|
|
|
async exit(ev) {
|
|
if (this.displayBarcodeApplication) {
|
|
await this.env.model.save();
|
|
this.env.config.historyBack();
|
|
} else {
|
|
this.toggleBarcodeLines();
|
|
}
|
|
}
|
|
|
|
hideNote(ev) {
|
|
this._hideNote = true;
|
|
this.render();
|
|
}
|
|
|
|
async openProductPage() {
|
|
if (!this._editedLineParams) {
|
|
await this.env.model.save();
|
|
}
|
|
this.env.model.displayProductPage();
|
|
}
|
|
|
|
async print(action, method) {
|
|
await this.env.model.save();
|
|
const options = this.env.model._getPrintOptions();
|
|
if (options.warning) {
|
|
return this.env.model.notification.add(options.warning, { type: 'warning' });
|
|
}
|
|
if (!action && method) {
|
|
action = await this.orm.call(
|
|
this.props.model,
|
|
method,
|
|
[[this.props.id]]
|
|
);
|
|
}
|
|
bus.trigger('do-action', { action, options });
|
|
}
|
|
|
|
putInPack(ev) {
|
|
ev.stopPropagation();
|
|
this.env.model._putInPack();
|
|
}
|
|
|
|
saveFormView(lineRecord) {
|
|
const lineId = (lineRecord && lineRecord.data.id) || (this._editedLineParams && this._editedLineParams.currentId);
|
|
const recordId = (lineRecord.resModel === this.props.model) ? lineId : undefined
|
|
this._onRefreshState({ recordId, lineId });
|
|
}
|
|
|
|
toggleBarcodeActions(ev) {
|
|
ev.stopPropagation();
|
|
this.env.model.displayBarcodeActions();
|
|
}
|
|
|
|
async toggleBarcodeLines(lineId) {
|
|
this._editedLineParams = undefined;
|
|
await this.env.model.displayBarcodeLines(lineId);
|
|
}
|
|
|
|
async toggleInformation() {
|
|
await this.env.model.save();
|
|
this.env.model.displayInformation();
|
|
}
|
|
|
|
/**
|
|
* Calls `validate` on the model and then triggers up the action because OWL
|
|
* components don't seem able to manage wizard without doing custom things.
|
|
*
|
|
* @param {OdooEvent} ev
|
|
*/
|
|
async validate(ev) {
|
|
ev.stopPropagation();
|
|
await this.env.model.validate();
|
|
}
|
|
|
|
/**
|
|
* Handler called when a barcode is scanned.
|
|
*
|
|
* @private
|
|
* @param {string} barcode
|
|
*/
|
|
_onBarcodeScanned(barcode) {
|
|
if (this.displayBarcodeApplication) {
|
|
this.env.model.processBarcode(barcode);
|
|
}
|
|
}
|
|
|
|
_scrollToSelectedLine() {
|
|
if (!this.displayBarcodeLines) {
|
|
this._scrollBehavior = 'auto';
|
|
return;
|
|
}
|
|
let selectedLine = document.querySelector('.o_sublines .o_barcode_line.o_highlight');
|
|
const isSubline = Boolean(selectedLine);
|
|
if (!selectedLine) {
|
|
selectedLine = document.querySelector('.o_barcode_line.o_highlight');
|
|
}
|
|
if (!selectedLine) {
|
|
const matchingLine = this.env.model.findLineForCurrentLocation();
|
|
if (matchingLine) {
|
|
selectedLine = document.querySelector(`.o_barcode_line[data-virtual-id="${matchingLine.virtual_id}"]`);
|
|
}
|
|
}
|
|
if (selectedLine) {
|
|
// If a line is selected, checks if this line is on the top of the
|
|
// page, and if it's not, scrolls until the line is on top.
|
|
const header = document.querySelector('.o_barcode_header');
|
|
const lineRect = selectedLine.getBoundingClientRect();
|
|
const navbar = document.querySelector('.o_main_navbar');
|
|
const page = document.querySelector('.o_barcode_lines');
|
|
// Computes the real header's height (the navbar is present if the page was refreshed).
|
|
const headerHeight = navbar ? navbar.offsetHeight + header.offsetHeight : header.offsetHeight;
|
|
if (lineRect.top < headerHeight || lineRect.bottom > (headerHeight + lineRect.height)) {
|
|
let top = lineRect.top - headerHeight + page.scrollTop;
|
|
if (isSubline) {
|
|
const parentLine = selectedLine.closest('.o_barcode_lines > .o_barcode_line');
|
|
const parentSummary = parentLine.querySelector('.o_barcode_line_summary');
|
|
top -= parentSummary.getBoundingClientRect().height;
|
|
}
|
|
page.scroll({ left: 0, top, behavior: this._scrollBehavior });
|
|
this._scrollBehavior = 'smooth';
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
async _onDoAction(ev) {
|
|
bus.trigger('do-action', {
|
|
action: ev,
|
|
options: {
|
|
on_close: this._onRefreshState.bind(this),
|
|
},
|
|
});
|
|
}
|
|
|
|
async _onEditLine(ev) {
|
|
let { line } = ev;
|
|
const virtualId = line.virtual_id;
|
|
await this.env.model.save();
|
|
// Updates the line id if it's missing, in order to open the line form view.
|
|
if (!line.id && virtualId) {
|
|
line = this.env.model.pageLines.find(l => Number(l.dummy_id) === virtualId);
|
|
}
|
|
this._editedLineParams = this.env.model.getEditedLineParams(line);
|
|
await this.openProductPage();
|
|
}
|
|
|
|
_onNotification(notifParams) {
|
|
const { message } = notifParams;
|
|
delete notifParams.message;
|
|
this.env.services.notification.add(message, notifParams);
|
|
}
|
|
|
|
_onOpenPackage(packageId) {
|
|
this._inspectedPackageId = packageId;
|
|
this.env.model.displayPackagePage();
|
|
}
|
|
|
|
async _onRefreshState(paramsRefresh) {
|
|
const { recordId, lineId } = paramsRefresh || {}
|
|
const { route, params } = this.env.model.getActionRefresh(recordId);
|
|
const result = await this.rpc(route, params);
|
|
await this.env.model.refreshCache(result.data.records);
|
|
await this.toggleBarcodeLines(lineId);
|
|
}
|
|
|
|
/**
|
|
* Handles triggered warnings. It can happen from an onchange for example.
|
|
*
|
|
* @param {CustomEvent} ev
|
|
*/
|
|
_onWarning(ev) {
|
|
const { title, message } = ev.detail;
|
|
this.env.services.dialog.add(ConfirmationDialog, { title, body: message });
|
|
}
|
|
}
|
|
MainComponent.template = 'stock_barcode.MainComponent';
|
|
MainComponent.components = {
|
|
View,
|
|
GroupedLineComponent,
|
|
LineComponent,
|
|
PackageLineComponent,
|
|
ChatterContainer,
|
|
};
|
|
|
|
registry.category("actions").add("stock_barcode_client_action", MainComponent);
|
|
|
|
export default MainComponent;
|