Browse Source

- Make search_query a property

- Fix search clear button

- Refactor load_order and actions

This refactors load_data into two functions: load_order_data and load_order_from_data.
Also, when executing actions, instead of sending the order as an argument, only the order_data will be sent.
The action method is responsible for generating the order data (if that's what it needs)

All the refund logic has been extracted from the various methods, and it's now in action_return.

Now, returns have a specific 'Refund XXXX' name.

- IMP Allow to duplicate done orders from the POS

- IMP filter by customer name, too, when searching
pull/416/head
Iván Todorovich 6 years ago
committed by Roberto Fichera
parent
commit
1a81fecb28
  1. 3
      pos_order_mgmt/README.rst
  2. 8
      pos_order_mgmt/models/pos_config.py
  3. 7
      pos_order_mgmt/models/pos_order.py
  4. 1
      pos_order_mgmt/readme/CONTRIBUTORS.rst
  5. 190
      pos_order_mgmt/static/src/js/widgets.js
  6. 3
      pos_order_mgmt/static/src/xml/pos.xml
  7. 11
      pos_order_mgmt/views/view_pos_config.xml

3
pos_order_mgmt/README.rst

@ -106,7 +106,10 @@ Contributors
* David Vidal <david.vidal@tecnativa.com> * David Vidal <david.vidal@tecnativa.com>
* Sylvain LE GAL (https://twitter.com/legalsylvain) * Sylvain LE GAL (https://twitter.com/legalsylvain)
* Carlos Martínez <carlos@domatix.com>
* Pierrick Brun <pierrick.brun@akretion.com> * Pierrick Brun <pierrick.brun@akretion.com>
* Iván Todorovich <ivan.todorovich@druidoo.io>
Maintainers Maintainers
~~~~~~~~~~~ ~~~~~~~~~~~

8
pos_order_mgmt/models/pos_config.py

@ -16,12 +16,20 @@ class PosConfig(models.Model):
help='Allows to reprint already done orders in the frontend', help='Allows to reprint already done orders in the frontend',
oldname='iface_load_done_order', oldname='iface_load_done_order',
) )
iface_return_done_order = fields.Boolean( iface_return_done_order = fields.Boolean(
string='Return Done Orders', string='Return Done Orders',
default=True, default=True,
help='Allows to return already done orders in the frontend', help='Allows to return already done orders in the frontend',
oldname='iface_load_done_order', oldname='iface_load_done_order',
) )
iface_copy_done_order = fields.Boolean(
string='Duplicate Done Orders',
default=True,
help='Allows to duplicate already done orders in the frontend',
)
iface_load_done_order_max_qty = fields.Integer( iface_load_done_order_max_qty = fields.Integer(
string='Max. Done Orders Quantity To Load', string='Max. Done Orders Quantity To Load',
default=10, default=10,

7
pos_order_mgmt/models/pos_order.py

@ -33,9 +33,10 @@ class PosOrder(models.Model):
@api.model @api.model
def _prepare_filter_query_for_pos(self, pos_session_id, query): def _prepare_filter_query_for_pos(self, pos_session_id, query):
return [ return [
'|',
'|', '|',
('name', 'ilike', query), ('name', 'ilike', query),
('pos_reference', 'ilike', query), ('pos_reference', 'ilike', query),
('partner_id.display_name', 'ilike', query),
] ]
@api.model @api.model
@ -57,8 +58,8 @@ class PosOrder(models.Model):
condition += [('config_id', '=', config.id)] condition += [('config_id', '=', config.id)]
else: else:
# Search globally by criteria # Search globally by criteria
condition += self._prepare_filter_query_for_pos(pos_session_id,
query)
condition += self._prepare_filter_query_for_pos(
pos_session_id, query)
field_names = self._prepare_fields_for_pos_list() field_names = self._prepare_fields_for_pos_list()
return self.search_read( return self.search_read(
condition, field_names, limit=config.iface_load_done_order_max_qty) condition, field_names, limit=config.iface_load_done_order_max_qty)

1
pos_order_mgmt/readme/CONTRIBUTORS.rst

@ -2,3 +2,4 @@
* Sylvain LE GAL (https://twitter.com/legalsylvain) * Sylvain LE GAL (https://twitter.com/legalsylvain)
* Carlos Martínez <carlos@domatix.com> * Carlos Martínez <carlos@domatix.com>
* Pierrick Brun <pierrick.brun@akretion.com> * Pierrick Brun <pierrick.brun@akretion.com>
* Iván Todorovich <ivan.todorovich@druidoo.io>

190
pos_order_mgmt/static/src/js/widgets.js

@ -1,5 +1,6 @@
/* Copyright 2018 GRAP - Sylvain LE GAL /* Copyright 2018 GRAP - Sylvain LE GAL
Copyright 2018 Tecnativa - David Vidal Copyright 2018 Tecnativa - David Vidal
Copyright 2019 Druidoo - Ivan Todorovich
License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl). */ License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl). */
odoo.define('pos_order_mgmt.widgets', function (require) { odoo.define('pos_order_mgmt.widgets', function (require) {
"use strict"; "use strict";
@ -56,7 +57,8 @@ odoo.define('pos_order_mgmt.widgets', function (require) {
this.order_cache = new DomCache(); this.order_cache = new DomCache();
this.orders = []; this.orders = [];
this.unknown_products = []; this.unknown_products = [];
this.search_done_orders();
this.search_query = false;
this.perform_search();
}, },
auto_back: true, auto_back: true,
@ -77,24 +79,25 @@ odoo.define('pos_order_mgmt.widgets', function (require) {
this.$('.back').click(function () { this.$('.back').click(function () {
return self.gui.show_screen(self.gui.startup_screen); return self.gui.show_screen(self.gui.startup_screen);
}); });
if (self.orders.length === 0) {
this.search_done_orders();
}
this.render_list();
var search_timeout = null;
if (this.pos.config.iface_vkeyboard && this.chrome.widget.keyboard) { if (this.pos.config.iface_vkeyboard && this.chrome.widget.keyboard) {
this.chrome.widget.keyboard.connect(this.$('.searchbox input')); this.chrome.widget.keyboard.connect(this.$('.searchbox input'));
} }
var search_timeout = null;
this.$('.searchbox input').on('keyup', function (event) { this.$('.searchbox input').on('keyup', function (event) {
self.search_query = this.value;
clearTimeout(search_timeout); clearTimeout(search_timeout);
var query = this.value;
search_timeout = setTimeout(function () { search_timeout = setTimeout(function () {
self.perform_search(query, event.which === 13);
self.perform_search();
}, 70); }, 70);
}); });
this.$('.searchbox .search-clear').click(function () { this.$('.searchbox .search-clear').click(function () {
self.clear_search(); self.clear_search();
}); });
this.perform_search();
}, },
render_list: function () { render_list: function () {
@ -126,34 +129,46 @@ odoo.define('pos_order_mgmt.widgets', function (require) {
// button events. // button events.
this.$('.order-list-return').off('click'); this.$('.order-list-return').off('click');
this.$('.order-list-reprint').off('click'); this.$('.order-list-reprint').off('click');
this.$('.order-list-copy').off('click');
this.$('.order-list-return').click(function (event) { this.$('.order-list-return').click(function (event) {
self.order_list_actions(event, 'return'); self.order_list_actions(event, 'return');
}); });
this.$('.order-list-reprint').click(function (event) { this.$('.order-list-reprint').click(function (event) {
self.order_list_actions(event, 'print'); self.order_list_actions(event, 'print');
}); });
this.$('.order-list-copy').click(function(event) {
self.order_list_actions(event, 'copy');
});
}, },
order_list_actions: function (event, action) { order_list_actions: function (event, action) {
var dataset = event.target.parentNode.dataset;
var self = this; var self = this;
var order_data;
var dataset = event.target.parentNode.dataset;
if (dataset.orderId) { if (dataset.orderId) {
this.load_order(parseInt(dataset.orderId, 10), action);
self.load_order_data(parseInt(dataset.orderId, 10))
.then(function(order_data) {
self.order_action(order_data, action);
})
} else { } else {
var local_order = '';
_.each(this.orders, function (order) {
_.each(this.orders, function (order_data) {
if (order.uid === dataset.uid) { if (order.uid === dataset.uid) {
order.return = action === 'return';
local_order = self._prepare_order_from_order_data(order);
self.order_action(order_data, action);
} }
}); });
if (local_order) {
this['action_' + action](local_order);
}
} }
}, },
action_print: function (order) {
order_action: function(order_data, action) {
this.gui.back();
this['action_' + action](order_data);
},
action_print: function (order_data) {
var order = this.load_order_from_data(order_data, true);
if (!order) return false;
// Restore the previous name
order.name = order_data.pos_reference || order_data.name;
var receipt = order.export_for_printing(); var receipt = order.export_for_printing();
// We store temporarily the current order so we can safely compute // We store temporarily the current order so we can safely compute
// taxes based on fiscal position // taxes based on fiscal position
@ -178,26 +193,56 @@ odoo.define('pos_order_mgmt.widgets', function (require) {
} }
}, },
action_return: function (order) {
action_copy: function(order_data) {
// Remove payments from original data
order_data.statement_ids = false;
var order = this.load_order_from_data(order_data, true);
if (!order) { return false; }
// If previous order was invoiced, we need a refund too
order.set_to_invoice(order_data.origin_invoice_id);
order.formatted_validation_date = false;
order.trigger('change');
this.pos.get('orders').add(order); this.pos.get('orders').add(order);
this.pos.set('selectedOrder', order); this.pos.set('selectedOrder', order);
return order; return order;
}, },
_prepare_order_from_order_data: function (order_data) {
action_return: function (order_data) {
//var order_data = _.cloneDeep(order_data);
// Remove payments from original data
order_data.statement_ids = false;
// Invert line quantities
var order_lines = order_data.line_ids || order_data.lines || [];
_.each(order_lines, function(line) { line.qty = -1 * line.qty })
// Load from new data and change some stuff
var order = this.load_order_from_data(order_data);
if (!order) { return false; }
order.return = true;
order.name = _t("Refund ") + order.uid;;
order.origin_name = order_data.pos_reference || order.returned_order_id;
// If previous order was invoiced, we need a refund too
order.set_to_invoice(order_data.origin_invoice_id);
order.formatted_validation_date = false;
order.trigger('change');
// Add to pos
this.pos.get('orders').add(order);
this.pos.set('selectedOrder', order);
return order;
},
_prepare_order_from_order_data: function (order_data, temporary) {
var self = this; var self = this;
var order = new pos.Order({}, { var order = new pos.Order({}, {
pos: this.pos, pos: this.pos,
temporary: !order_data.return,
temporary: !!temporary,
}); });
// Set Generic Info
if (!order_data.return) {
order.name = order_data.pos_reference || order_data.name;
}
if (order_data.partner_id.length) { if (order_data.partner_id.length) {
order_data.partner_id = order_data.partner_id[0]; order_data.partner_id = order_data.partner_id[0];
} }
order.set_client(this.pos.db.get_partner_by_id(order_data.partner_id)); order.set_client(this.pos.db.get_partner_by_id(order_data.partner_id));
// Set fiscal position // Set fiscal position
if (order_data.fiscal_position && this.pos.fiscal_positions) { if (order_data.fiscal_position && this.pos.fiscal_positions) {
var fiscal_positions = this.pos.fiscal_positions; var fiscal_positions = this.pos.fiscal_positions;
@ -206,23 +251,17 @@ odoo.define('pos_order_mgmt.widgets', function (require) {
})[0]; })[0];
order.trigger('change'); order.trigger('change');
} }
// Set order lines // Set order lines
var orderLines = order_data.line_ids || order_data.lines || []; var orderLines = order_data.line_ids || order_data.lines || [];
self._prepare_orderlines_from_order_data(order, order_data, orderLines); self._prepare_orderlines_from_order_data(order, order_data, orderLines);
if (order_data.return) {
order.return = true;
// A credit note should be emited if there was an invoice
order.set_to_invoice(order_data.origin_invoice_id);
// We'll refunded orders once they are synced
order.returned_order_id = order_data.id || order_data.name;
order.origin_name = order_data.pos_reference || order.returned_order_id;
order.trigger('change');
return order;
}
if (order_data.returned_order_id) { if (order_data.returned_order_id) {
order.origin_name = order_data.returned_order_id; order.origin_name = order_data.returned_order_id;
} }
order.formatted_validation_date = moment(order_data.date_order).format('YYYY-MM-DD HH:mm:ss'); order.formatted_validation_date = moment(order_data.date_order).format('YYYY-MM-DD HH:mm:ss');
// Set Payment lines // Set Payment lines
var paymentLines = order_data.statement_ids || []; var paymentLines = order_data.statement_ids || [];
_.each(paymentLines, function (paymentLine) { _.each(paymentLines, function (paymentLine) {
@ -243,6 +282,7 @@ odoo.define('pos_order_mgmt.widgets', function (require) {
}); });
return order; return order;
}, },
_prepare_orderlines_from_order_data: function(order, order_data, orderLines) { _prepare_orderlines_from_order_data: function(order, order_data, orderLines) {
var self = this; var self = this;
_.each(orderLines, function(line) { _.each(orderLines, function(line) {
@ -258,7 +298,7 @@ odoo.define('pos_order_mgmt.widgets', function (require) {
// Create a new order line // Create a new order line
order.add_product(product, { order.add_product(product, {
price: line.price_unit, price: line.price_unit,
quantity: order_data.return ? line.qty * -1 : line.qty,
quantity: line.qty,
discount: line.discount, discount: line.discount,
merge: false, merge: false,
}); });
@ -266,36 +306,12 @@ odoo.define('pos_order_mgmt.widgets', function (require) {
}); });
}, },
load_order: function (order_id, action) {
this.unknown_products = [];
load_order_data: function(order_id) {
var self = this; var self = this;
return this._rpc({ return this._rpc({
model: 'pos.order', model: 'pos.order',
method: 'load_done_order_for_pos', method: 'load_done_order_for_pos',
args: [order_id], args: [order_id],
}).then(function (order_data) {
self.gui.back();
var correct_order_print = true;
if (action === 'return') {
order_data.return = true;
}
var order = self._prepare_order_from_order_data(order_data);
// Forbid POS Order loading if some products are unknown
if (self.unknown_products.length > 0) {
self.gui.show_popup('error-traceback', {
'title': _t('Unknown Products'),
'body': _t('Unable to load some order lines because the ' +
'products are not available in the POS cache.\n\n' +
'Please check that lines :\n\n * ') + self.unknown_products.join("; \n *"),
});
correct_order_print = false;
}
if (correct_order_print && action === 'print') {
self.action_print(order);
}
if (correct_order_print && action === 'return') {
self.action_return(order);
}
}).fail(function (error, event) { }).fail(function (error, event) {
if (parseInt(error.code, 10) === 200) { if (parseInt(error.code, 10) === 200) {
// Business Logic Error, not a connection problem // Business Logic Error, not a connection problem
@ -310,10 +326,42 @@ odoo.define('pos_order_mgmt.widgets', function (require) {
'body': _t('Can not execute this action because the POS is currently offline'), 'body': _t('Can not execute this action because the POS is currently offline'),
}); });
} }
event.preventDefault();
}); });
}, },
load_order_from_data: function(order_data) {
var self = this;
this.unknown_products = [];
var order = self._prepare_order_from_order_data(order_data);
// Forbid POS Order loading if some products are unknown
if (self.unknown_products.length > 0) {
self.gui.show_popup('error-traceback', {
'title': _t('Unknown Products'),
'body': _t('Unable to load some order lines because the ' +
'products are not available in the POS cache.\n\n' +
'Please check that lines :\n\n * ') + self.unknown_products.join("; \n *"),
});
return false;
}
return order;
},
/* deprecated */
load_order: function (order_id) {
var self = this;
var done = new $.Deferred();
this.load_order_data(order_id)
.then(function(order_data) {
self.gui.show_screen('orderlist');
var order = self.load_order_from_data(order_data);
if (!order) { return done.fail(); }
done.resolve(order);
}).fail(function() {
done.fail();
});
return done;
},
// Search Part // Search Part
search_done_orders: function (query) { search_done_orders: function (query) {
var self = this; var self = this;
@ -342,22 +390,18 @@ odoo.define('pos_order_mgmt.widgets', function (require) {
}); });
}, },
perform_search: function (query) {
perform_search: function() {
var self = this; var self = this;
this.search_done_orders(query)
.done(function () {
self.render_list();
});
return this.search_done_orders(self.search_query)
.done(function () { self.render_list(); });
}, },
clear_search: function () { clear_search: function () {
var self = this; var self = this;
this.search_done_orders()
.done(function () {
self.$('.searchbox input')[0].value = '';
self.$('.searchbox input').focus();
self.render_list();
});
self.$('.searchbox input')[0].value = '';
self.$('.searchbox input').focus();
self.search_query = false;
self.perform_search();
}, },
}); });

3
pos_order_mgmt/static/src/xml/pos.xml

@ -58,6 +58,9 @@
<span t-if="widget.pos.config.iface_reprint_done_order" class="button order-list-reprint" t-att-data-order-id="order.id" t-att-data-Uid='order.uid'> <span t-if="widget.pos.config.iface_reprint_done_order" class="button order-list-reprint" t-att-data-order-id="order.id" t-att-data-Uid='order.uid'>
<i class='fa fa-fw fa-print'/> <i class='fa fa-fw fa-print'/>
</span> </span>
<span t-if="widget.pos.config.iface_copy_done_order" class="button order-list-copy" t-att-data-order-id="order.id" t-att-data-Uid='order.uid'>
<i class='fa fa-fw fa-copy'/>
</span>
<span t-if="widget.pos.config.iface_return_done_order and order.amount_total >= 0" class="button order-list-return" t-att-data-order-id="order.id" t-att-data-Uid='order.uid'> <span t-if="widget.pos.config.iface_return_done_order and order.amount_total >= 0" class="button order-list-return" t-att-data-order-id="order.id" t-att-data-Uid='order.uid'>
<i class='fa fa-fw fa-undo'/> <i class='fa fa-fw fa-undo'/>
</span> </span>

11
pos_order_mgmt/views/view_pos_config.xml

@ -33,6 +33,17 @@
</div> </div>
</div> </div>
</div> </div>
<div class="col-xs-12 col-md-6 o_setting_box" id="copy_done_order">
<div class="o_setting_left_pane">
<field name="iface_copy_done_order"/>
</div>
<div class="o_setting_right_pane">
<label string="Duplicate Done Orders" for="iface_copy_done_order"/>
<div class="text-muted">
Allow to duplicate done orders in this POS
</div>
</div>
</div>
<div class="col-xs-12 col-md-6 o_setting_box" id="load_done_order_max_qty" <div class="col-xs-12 col-md-6 o_setting_box" id="load_done_order_max_qty"
attrs="{'invisible': [('iface_reprint_done_order', '=', False), attrs="{'invisible': [('iface_reprint_done_order', '=', False),
('iface_return_done_order', '=', False)]}"> ('iface_return_done_order', '=', False)]}">

Loading…
Cancel
Save