质量模块和库存扫码
This commit is contained in:
111
stock_barcode/static/src/widgets/digipad.js
Normal file
111
stock_barcode/static/src/widgets/digipad.js
Normal file
@@ -0,0 +1,111 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import { registry } from "@web/core/registry";
|
||||
import { useService } from "@web/core/utils/hooks";
|
||||
|
||||
const { Component, onWillStart } = owl;
|
||||
|
||||
export class Digipad extends Component {
|
||||
setup() {
|
||||
this.orm = useService('orm');
|
||||
const user = useService('user');
|
||||
this.buttons = [7, 8, 9, 4, 5, 6, 1, 2, 3, '.', '0', 'erase'].map((value, index) => {
|
||||
return { index, value };
|
||||
});
|
||||
this.value = String(this.props.record.data[this.props.quantityField]);
|
||||
onWillStart(async () => {
|
||||
this.displayUOM = await user.hasGroup('uom.group_uom');
|
||||
await this._fetchPackagingButtons();
|
||||
});
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Private
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Copies the input value if digipad value is not set yet, or overrides it if there is a
|
||||
* difference between the two values (in case user has manualy edited the input value).
|
||||
* @private
|
||||
*/
|
||||
_checkInputValue() {
|
||||
const input = document.querySelector(`div[name="${this.props.quantityField}"] input`);
|
||||
const inputValue = input.value;
|
||||
if (Number(this.value) != Number(inputValue)) {
|
||||
console.warn(`-- Change widget value: ${this.value} -> ${inputValue}`);
|
||||
this.value = inputValue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Increments the field value by the interval amount (1 by default).
|
||||
* @private
|
||||
* @param {integer} [interval=1]
|
||||
*/
|
||||
async _increment(interval=1) {
|
||||
this._checkInputValue();
|
||||
const numberValue = Number(this.value || 0);
|
||||
this.value = String(numberValue + interval);
|
||||
await this._notifyChanges();
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies changes on the field to mark the record as dirty.
|
||||
* @private
|
||||
*/
|
||||
async _notifyChanges() {
|
||||
const changes = { [this.props.quantityField]: Number(this.value) };
|
||||
await this.props.record.update(changes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for the product's packaging buttons.
|
||||
* @private
|
||||
* @returns {Promise}
|
||||
*/
|
||||
async _fetchPackagingButtons() {
|
||||
const record = this.props.record.data;
|
||||
const demandQty = record.reserved_uom_qty;
|
||||
const domain = [['product_id', '=', record.product_id[0]]];
|
||||
if (demandQty) { // Doesn't fetch packaging with a too high quantity.
|
||||
domain.push(['qty', '<=', demandQty]);
|
||||
}
|
||||
this.packageButtons = await this.orm.searchRead(
|
||||
'product.packaging',
|
||||
domain,
|
||||
['name', 'product_uom_id', 'qty'],
|
||||
{ limit: 3 },
|
||||
);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Handlers
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Handles the click on one of the digipad's button and updates the value..
|
||||
* @private
|
||||
* @param {String} button
|
||||
*/
|
||||
_onCLickButton(button) {
|
||||
this._checkInputValue();
|
||||
if (button === 'erase') {
|
||||
this.value = this.value.substr(0, this.value.length - 1);
|
||||
} else {
|
||||
if (button === '.' && this.value.indexOf('.') != -1) {
|
||||
// Avoids to add a decimal separator multiple time.
|
||||
return;
|
||||
}
|
||||
this.value += button;
|
||||
}
|
||||
this._notifyChanges();
|
||||
}
|
||||
}
|
||||
|
||||
Digipad.template = 'stock_barcode.DigipadTemplate';
|
||||
Digipad.extractProps = ({ attrs }) => {
|
||||
return {
|
||||
quantityField: attrs.quantity_field,
|
||||
};
|
||||
};
|
||||
registry.category('view_widgets').add('digipad', Digipad);
|
||||
46
stock_barcode/static/src/widgets/digipad.scss
Normal file
46
stock_barcode/static/src/widgets/digipad.scss
Normal file
@@ -0,0 +1,46 @@
|
||||
.o_digipad_widget {
|
||||
display: flex;
|
||||
|
||||
.btn {
|
||||
padding: 0;
|
||||
border-radius: 4px;
|
||||
font-size: 2em;
|
||||
|
||||
.o_web_client.o_touch_device & {
|
||||
border-radius: 4px;
|
||||
font-size: 1em;
|
||||
padding: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.o_digipad_digit_buttons, .o_digipad_special_buttons {
|
||||
display: grid;
|
||||
grid-template-rows: repeat(4, 1fr);
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.o_digipad_digit_buttons {
|
||||
width: 75%;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
}
|
||||
|
||||
.o_digipad_special_buttons {
|
||||
width: 25%;
|
||||
grid-template-columns: repeat(1, 1fr);
|
||||
}
|
||||
|
||||
.o_packaging_button {
|
||||
font-size: 1em;
|
||||
overflow: hidden;
|
||||
|
||||
div[name=packaging_name] {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
|
||||
.small-text {
|
||||
font-size: 0.66em;
|
||||
}
|
||||
}
|
||||
36
stock_barcode/static/src/widgets/digipad.xml
Normal file
36
stock_barcode/static/src/widgets/digipad.xml
Normal file
@@ -0,0 +1,36 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates xml:space="preserve">
|
||||
|
||||
<div t-name="stock_barcode.DigipadTemplate" class="o_digipad_widget" owl="1">
|
||||
<t t-set="commonCssClasses">o_digipad_button btn d-flex justify-content-center align-items-center border w-100 py-2</t>
|
||||
<!-- Digits, erase and dot buttons -->
|
||||
<div class="o_digipad_digit_buttons me-2">
|
||||
<div t-foreach="buttons" t-as="button" t-key="button.index" t-att-data-button="button.value"
|
||||
class="btn-primary" t-att-class="commonCssClasses"
|
||||
t-on-click="() => this._onCLickButton(button.value)">
|
||||
<div t-if="button.value == 'erase'" class="fa fa-lg fa-long-arrow-left"/>
|
||||
<div t-else="" t-out="button.value"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="o_digipad_special_buttons">
|
||||
<!-- +1 / -1 buttons -->
|
||||
<div class="btn-secondary o_increase" t-att-class="commonCssClasses"
|
||||
t-on-click="() => this._increment()"
|
||||
t-att-data-button="increase">+1</div>
|
||||
<div class="btn-secondary o_decrease" t-att-class="commonCssClasses"
|
||||
t-on-click="() => this._increment(-1)"
|
||||
t-att-data-button="decrease">-1</div>
|
||||
<!-- Product packagings buttons -->
|
||||
<div t-foreach="packageButtons" t-as="button" t-key="button.id" t-att-data-qty="button.qty"
|
||||
t-on-click="() => this._increment(button.qty)"
|
||||
class="o_packaging_button btn btn-secondary border w-100 py-2">
|
||||
<div class="text-capitalize">
|
||||
<span t-out="'+' + button.qty"/>
|
||||
<span t-if="displayUOM" class="small-text ms-1" t-out="button.product_uom_id[1]"/>
|
||||
</div>
|
||||
<div name="packaging_name" class="small-text" t-out="button.name"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</templates>
|
||||
34
stock_barcode/static/src/widgets/set_reserved_qty_button.js
Normal file
34
stock_barcode/static/src/widgets/set_reserved_qty_button.js
Normal file
@@ -0,0 +1,34 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import { registry } from "@web/core/registry";
|
||||
import { useService } from "@web/core/utils/hooks";
|
||||
|
||||
const { Component, onWillStart } = owl;
|
||||
|
||||
export class SetReservedQuantityButton extends Component {
|
||||
setup() {
|
||||
const user = useService('user');
|
||||
onWillStart(async () => {
|
||||
this.displayUOM = await user.hasGroup('uom.group_uom');
|
||||
});
|
||||
}
|
||||
|
||||
get uom() {
|
||||
const [id, name] = this.props.record.data.product_uom_id || [];
|
||||
return { id, name };
|
||||
}
|
||||
|
||||
_setQuantity (ev) {
|
||||
ev.stopPropagation();
|
||||
this.props.record.update({ [this.props.fieldToSet]: this.props.value });
|
||||
}
|
||||
}
|
||||
|
||||
SetReservedQuantityButton.extractProps = ({ attrs }) => {
|
||||
if (attrs.field_to_set) {
|
||||
return { fieldToSet: attrs.field_to_set };
|
||||
}
|
||||
};
|
||||
|
||||
SetReservedQuantityButton.template = 'stock_barcode.SetReservedQuantityButtonTemplate';
|
||||
registry.category('fields').add('set_reserved_qty_button', SetReservedQuantityButton);
|
||||
@@ -0,0 +1,5 @@
|
||||
.o_web_client.o_touch_device .o_button_qty_done, .o_button_qty_done {
|
||||
padding-top: 8px;
|
||||
padding-bottom: 8px;
|
||||
font-size: 1em;
|
||||
}
|
||||
14
stock_barcode/static/src/widgets/set_reserved_qty_button.xml
Normal file
14
stock_barcode/static/src/widgets/set_reserved_qty_button.xml
Normal file
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates xml:space="preserve">
|
||||
|
||||
<button t-name="stock_barcode.SetReservedQuantityButtonTemplate" owl="1"
|
||||
class="o_button_qty_done d-flex btn"
|
||||
t-attf-class="{{props.value ? 'btn-primary' : 'btn-secondary'}}"
|
||||
t-on-click="_setQuantity" t-att-disabled="!props.value">
|
||||
<t t-if="props.value">
|
||||
/ <span name="product_uom_qty" class="ms-1" t-out="props.value"/>
|
||||
</t>
|
||||
<span t-if="displayUOM" name="product_uom_id" class="text-capitalize ms-1" t-out="uom.name"/>
|
||||
</button>
|
||||
|
||||
</templates>
|
||||
Reference in New Issue
Block a user