[FIX] Stock allow barcode input on stock.picking o2m kanban

This commit is contained in:
Antony Lesuisse
2016-03-17 09:40:11 +01:00
3 changed files with 199 additions and 6 deletions
+23 -2
View File
@@ -143,11 +143,18 @@ msgstr ""
#. module: barcodes
#. openerp-web
#: code:addons/barcodes/static/src/js/form_view_barcode_handler.js:51
#: code:addons/barcodes/static/src/js/form_view_barcode_handler.js:122
#, python-format
msgid "Error : Document not editable"
msgstr ""
#. module: barcodes
#. openerp-web
#: code:addons/barcodes/static/src/js/form_view_barcode_handler.js:56
#, python-format
msgid "Error : No last scanned barcode"
msgstr ""
#. module: barcodes
#: model:ir.model.fields,field_description:barcodes.field_barcode_nomenclature_id
#: model:ir.model.fields,field_description:barcodes.field_barcode_rule_id
@@ -209,6 +216,13 @@ msgstr ""
msgid "Sequence"
msgstr ""
#. module: barcodes
#. openerp-web
#: code:addons/barcodes/static/src/js/form_view_barcode_handler.js:68
#, python-format
msgid "Set quantity"
msgstr ""
#. module: barcodes
#: model:ir.ui.view,arch_db:barcodes.view_barcode_nomenclature_form
msgid "Tables"
@@ -244,11 +258,18 @@ msgstr ""
#. module: barcodes
#. openerp-web
#: code:addons/barcodes/static/src/js/form_view_barcode_handler.js:51
#: code:addons/barcodes/static/src/js/form_view_barcode_handler.js:122
#, python-format
msgid "To modify this document, please first start edition."
msgstr ""
#. module: barcodes
#. openerp-web
#: code:addons/barcodes/static/src/js/form_view_barcode_handler.js:56
#, python-format
msgid "To set the quantity please scan a barcode first."
msgstr ""
#. module: barcodes
#: model:ir.model.fields,field_description:barcodes.field_barcode_rule_type
msgid "Type"
@@ -6,10 +6,30 @@ var utils = require('web.utils');
var common = require('web.form_common');
var BarcodeEvents = require('barcodes.BarcodeEvents');
var BarcodeHandlerMixin = require('barcodes.BarcodeHandlerMixin');
var KanbanRecord = require('web_kanban.Record');
var _t = core._t;
// web_kanban.Record and web.list_common.Record do not implement the
// same interface and are thus inherently incompatible with each
// other. Luckily barcodes keeps things pretty simple when it comes to
// the records it wants to use. So if we give the KanbanRecord a get()
// function that behaves like the one of web.list.Record, everything
// is fine.
KanbanRecord.include({
get: function (key) {
return this.values[key] && this.values[key].value;
},
});
var FormViewBarcodeHandler = common.AbstractField.extend(BarcodeHandlerMixin, {
init: function(parent, context) {
this.__quantity_listener = _.bind(this._set_quantity_listener, this);
BarcodeHandlerMixin.init.apply(this, arguments);
return this._super.apply(this, arguments);
},
start: function() {
this._super();
this.form_view = this.field_manager;
@@ -31,7 +51,64 @@ var FormViewBarcodeHandler = common.AbstractField.extend(BarcodeHandlerMixin, {
this.map_barcode_method['O-CMD.PAGER-NEXT'] = _.bind(this.form_view.pager.next, this.form_view.pager);
}
},
_display_no_edit_mode_warning: function() {
this.do_warn(_t('Error : Document not editable'), _t('To modify this document, please first start edition.'));
},
_display_no_last_scanned_warning: function() {
this.do_warn(_t('Error : No last scanned barcode'), _t('To set the quantity please scan a barcode first.'));
},
_set_quantity_listener: function(event) {
var self = this;
var character = String.fromCharCode(event.which);
if (this.form_view.get('actual_mode') === 'view') {
this._display_no_edit_mode_warning();
} else if ($(event.target).is('body') && /[0-9]/.test(character)) { // only catch the event if we're not focused in
// another field and it's a number
var field = this.form_view.fields[this.m2x_field];
var view = field.viewmanager.active_view;
if (this.last_scanned_barcode) {
var new_qty = window.prompt(_t('Set quantity'), character) || "0";
new_qty = new_qty.replace(',', '.');
var record = this._get_records(field).find(function(record) {
return record.get('product_barcode') === self.last_scanned_barcode;
});
if (record) {
var values = {};
values[this.quantity_field] = parseFloat(new_qty);
field.data_update(record.get('id'), values).then(function () {
view.controller.reload_record(record);
});
} else {
this._display_no_last_scanned_warning();
}
} else {
this._display_no_last_scanned_warning();
}
}
},
start_listening: function() {
if (this.quantity_field && ! this.is_listening) {
core.bus.on('keypress', this, this.__quantity_listener);
}
BarcodeHandlerMixin.start_listening.call(this);
},
stop_listening: function() {
if (this.quantity_field && this.is_listening) {
core.bus.off('keypress', this, this.__quantity_listener);
delete this.last_scanned_barcode;
}
BarcodeHandlerMixin.stop_listening.call(this);
},
// Let subclasses add custom behaviour before onchange. Must return a deferred.
// Resolve the deferred with true proceed with the onchange, false to prevent it.
pre_onchange_hook: function(barcode) {
@@ -40,6 +117,7 @@ var FormViewBarcodeHandler = common.AbstractField.extend(BarcodeHandlerMixin, {
on_barcode_scanned: function(barcode) {
var self = this;
self.last_scanned_barcode = barcode;
// Execute a harcoded action
var action = this.map_barcode_method[barcode];
if (typeof action === "function")
@@ -48,7 +126,7 @@ var FormViewBarcodeHandler = common.AbstractField.extend(BarcodeHandlerMixin, {
return;
// Warn the user if form view is not editable
else if (this.form_view.get('actual_mode') === 'view')
this.do_warn(_t('Error : Document not editable'), _t('To modify this document, please first start edition.'));
this._display_no_edit_mode_warning();
else {
// Call hook method possibly implemented by subclass
this.pre_onchange_hook(barcode).then(function(proceed) {
@@ -71,6 +149,11 @@ var FormViewBarcodeHandler = common.AbstractField.extend(BarcodeHandlerMixin, {
});
}
},
_get_records: function(field) {
return field.viewmanager.active_view.controller.records || // tree view
field.viewmanager.active_view.controller.widgets; // kanban view
},
});
core.form_widget_registry.add('barcode_handler', FormViewBarcodeHandler);
+91 -2
View File
@@ -114,7 +114,7 @@
<notebook attrs="{'invisible':[('state','=','draft')]}">
<page string="Inventory Details" >
<button name="reset_real_qty" states="confirm" string="⇒ Set quantities to 0" type="object" class="oe_link oe_right" groups="stock.group_stock_user"/>
<field name="line_ids" string="Inventory Details" context="{'default_location_id': location_id, 'default_product_id': product_id, 'default_prod_lot_id': lot_id, 'default_package_id': package_id, 'default_partner_id': partner_id}">
<field name="line_ids" string="Inventory Details" context="{'default_location_id': location_id, 'default_product_id': product_id, 'default_prod_lot_id': lot_id, 'default_package_id': package_id, 'default_partner_id': partner_id}" mode="tree,kanban">
<tree string="Inventory Details" editable="bottom" decoration-info="product_qty != theoretical_qty" decoration-danger="theoretical_qty &lt; 0">
<field name="product_id" domain="[('type','=','product')]" on_change="onchange_createline(location_id, product_id, product_uom_id, package_id, prod_lot_id, partner_id)"/>
<field name="product_uom_id" groups="product.group_uom" string="UoM" on_change="onchange_createline(location_id, product_id, product_uom_id, package_id, prod_lot_id, partner_id)"/>
@@ -126,6 +126,48 @@
<field name="product_qty" string="Real Quantity"/>
<field name="state" invisible="True"/>
</tree>
<kanban class="o_kanban_mobile">
<field name="product_id" domain="[('type','=','product')]" on_change="onchange_createline(location_id, product_id, product_uom_id, package_id, prod_lot_id, partner_id)"/>
<field name="product_uom_id" groups="product.group_uom" string="UoM" on_change="onchange_createline(location_id, product_id, product_uom_id, package_id, prod_lot_id, partner_id)"/>
<field name="location_id" domain="[('id', 'child_of', parent.location_id)]" groups="stock.group_locations" on_change="onchange_createline(location_id, product_id, product_uom_id, package_id, prod_lot_id, partner_id)"/>
<field name="prod_lot_id" on_change="onchange_createline(location_id, product_id, product_uom_id, package_id, prod_lot_id, partner_id)" domain="[('product_id', '=', product_id)]" context="{'default_product_id': product_id}" groups="stock.group_production_lot"/>
<field name="package_id" domain="['|', ('location_id','=', False), ('location_id', '=', location_id)]" on_change="onchange_createline(location_id, product_id, product_uom_id, package_id, prod_lot_id, partner_id)" groups="stock.group_tracking_lot"/>
<field name="partner_id" on_change="onchange_createline(location_id, product_id, product_uom_id, package_id, prod_lot_id, partner_id)" groups="stock.group_tracking_owner"/>
<field name="theoretical_qty" readonly="1"/>
<field name="product_qty" string="Real Quantity"/>
<field name="state" invisible="True"/>
<templates>
<t t-name="kanban-box">
<div t-attf-class="oe_kanban_global_click_edit
#{record.product_qty.raw_value!=record.theoretical_qty.raw_value ? 'oe_kanban_color_6' : ''}
#{record.theoretical_qty.raw_value&lt;0 ? 'oe_kanban_color_2' : ''}">
<div class="row">
<div class="col-xs-12">
<strong>
<field name='product_id'/>
</strong>
</div>
</div>
<div class="row">
<div class="col-xs-12 text-muted">
<span><t t-esc='record.location_id.string'/>: <t t-esc='record.location_id.value'/></span>
</div>
</div>
<div class="row">
<div class="col-xs-12 text-muted">
<span><t t-esc='record.theoretical_qty.string'/>: <t t-esc='record.theoretical_qty.value'/></span>
</div>
</div>
<div class="row">
<div class="col-xs-12 text-muted">
<span><t t-esc='record.product_qty.string'/>: <t t-esc='record.product_qty.value'/></span>
</div>
</div>
</div>
</t>
</templates>
</kanban>
</field>
<p></p>
<h3 class="oe_grey">Notes</h3>
@@ -840,7 +882,7 @@
<button name="do_prepare_partial" type="object" string="Recompute" attrs="{'invisible': [('recompute_pack_op','=', False)]}"/>
<field name="recompute_pack_op" invisible="1"/>
<field name="pack_operation_ids" invisible="1"/>
<field name="pack_operation_product_ids" options="{'reload_on_button': True}" context="{'default_picking_id': id, 'default_location_id': location_id, 'default_location_dest_id': location_dest_id}">
<field name="pack_operation_product_ids" options="{'reload_on_button': True}" context="{'default_picking_id': id, 'default_location_id': location_id, 'default_location_dest_id': location_dest_id}" mode="tree,kanban">
<tree editable="bottom" decoration-muted="result_package_id" decoration-danger="qty_done&gt;product_qty" decoration-success="qty_done==product_qty and state!='done' and not result_package_id">
<field name="package_id" groups="stock.group_tracking_lot" invisible="1"/>
<field name="product_id" on_change="product_id_change(product_id, product_uom_id, product_qty)" required="1" attrs="{'readonly': [('fresh_record', '=', False)]}"/>
@@ -861,6 +903,53 @@
<button name="show_details" string="Modify" type="object" icon="fa-pencil" groups="stock.group_locations"
states="confirmed,assigned,waiting,partially_available"/>
</tree>
<kanban class="o_kanban_mobile">
<field name="package_id" groups="stock.group_tracking_lot" invisible="1"/>
<field name="product_id" on_change="product_id_change(product_id, product_uom_id, product_qty)" required="1" attrs="{'readonly': [('fresh_record', '=', False)]}"/>
<field name="fresh_record" invisible="1"/>
<field name="product_uom_id" attrs="{'readonly': [('fresh_record', '=', False)]}" groups="product.group_uom"/>
<field name="lots_visible" invisible="1"/>
<field name="owner_id" groups="stock.group_tracking_owner"/>
<field name="location_id" domain="[('id', 'child_of', parent.location_id)]" invisible="1"/>
<field name="location_dest_id" domain="[('id', 'child_of', parent.location_dest_id)]" invisible="1"/>
<field name="from_loc" groups="stock.group_locations,stock.group_tracking_lot"/>
<field name="to_loc" groups="stock.group_locations,stock.group_tracking_lot"/>
<field name="result_package_id" groups="stock.group_tracking_lot" context="{'location_id': location_dest_id}" invisible="1"/>
<field name="state" invisible="1"/>
<field name="product_qty" readonly="1" attrs="{'required': [('product_id', '!=', False)]}"/>
<field name="qty_done" attrs="{'readonly': [('lots_visible', '=', True)]}"/>
<templates>
<t t-name="kanban-box">
<div t-attf-class="oe_kanban_card oe_kanban_global_click
#{record.qty_done.raw_value&gt;record.product_qty.raw_value ? 'oe_kanban_color_2' : ''}
#{record.qty_done.raw_value==record.product_qty.raw_value &amp;&amp; record.state.raw_value!='done' &amp;&amp; ! record.result_package_id.raw_value ? 'oe_kanban_color_3' : ''}">
<div class="row">
<div class="col-xs-12">
<strong><span><t t-esc="record.product_id.value"/></span></strong>
</div>
</div>
<div class="row">
<div class="col-xs-6 text-muted">
<span><t t-esc="record.from_loc.string"/>: <t t-esc="record.from_loc.value"/></span>
</div>
<div class="col-xs-6 text-muted">
<span><t t-esc="record.to_loc.string"/>: <t t-esc="record.to_loc.value"/></span>
</div>
</div>
<div class="row">
<div class="col-xs-6 text-muted">
<span><t t-esc="record.product_qty.string"/>: <t t-esc="record.product_qty.value"/></span>
</div>
<div class="col-xs-6 text-muted">
<span>
<t t-esc="record.qty_done.string"/>: <t t-esc="record.qty_done.value"/>
</span>
</div>
</div>
</div>
</t>
</templates>
</kanban>
</field>
<field name="picking_type_entire_packs" invisible="1"/>
<field name="pack_operation_pack_ids" options="{'reload_on_button': True}" attrs="{'invisible': [('pack_operation_pack_ids', '=', []), ('picking_type_entire_packs', '=', False)]}" context="{'default_picking_id': id, 'default_location_id': location_id, 'default_location_dest_id': location_dest_id, 'default_picking_id': id}" groups="stock.group_tracking_lot">