Sylvain Calador
10 years ago
7 changed files with 469 additions and 0 deletions
-
1pos_order_load/__init__.py
-
51pos_order_load/__openerp__.py
-
42pos_order_load/point_of_sale.py
-
51pos_order_load/static/src/css/pos_order_load.css
-
238pos_order_load/static/src/js/pos_order_load.js
-
68pos_order_load/static/src/xml/pos_order_load.xml
-
18pos_order_load/view/pos_order_load.xml
@ -0,0 +1 @@ |
|||||
|
import point_of_sale |
@ -0,0 +1,51 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################## |
||||
|
# |
||||
|
# OpenERP, Open Source Management Solution |
||||
|
# Copyright (C) 2014-TODAY Akretion (<http://www.akretion.com>). |
||||
|
# Copyright (C) 2014 Sylvain Calador (sylvain.calador@akretion.com). |
||||
|
# |
||||
|
# This program is free software: you can redistribute it and/or modify |
||||
|
# it under the terms of the GNU Affero General Public License as |
||||
|
# published by the Free Software Foundation, either version 3 of the |
||||
|
# License, or (at your option) any later version. |
||||
|
# |
||||
|
# This program is distributed in the hope that it will be useful, |
||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
|
# GNU Affero General Public License for more details. |
||||
|
# |
||||
|
# You should have received a copy of the GNU Affero General Public License |
||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||
|
# |
||||
|
############################################################################## |
||||
|
|
||||
|
{ |
||||
|
'name': 'POS Order Load', |
||||
|
'version': '0.1', |
||||
|
'author': 'Akretion', |
||||
|
'category': 'Sales Management', |
||||
|
'depends': [ |
||||
|
'base', |
||||
|
'decimal_precision', |
||||
|
'point_of_sale', |
||||
|
'product', |
||||
|
'web', |
||||
|
], |
||||
|
'demo': [], |
||||
|
'website': 'https://www.akretion.com', |
||||
|
'description': """ |
||||
|
This module allows to load existing POS order |
||||
|
""", |
||||
|
'data': [ |
||||
|
'view/pos_order_load.xml', |
||||
|
], |
||||
|
'qweb': [ |
||||
|
'static/src/xml/pos_order_load.xml', |
||||
|
], |
||||
|
'test': [], |
||||
|
'installable': True, |
||||
|
'auto_install': False, |
||||
|
} |
||||
|
|
||||
|
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: |
@ -0,0 +1,42 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################## |
||||
|
# |
||||
|
# OpenERP, Open Source Management Solution |
||||
|
# Copyright (C) 2014 Akretion (<http://www.akretion.com>). |
||||
|
# |
||||
|
# This program is free software: you can redistribute it and/or modify |
||||
|
# it under the terms of the GNU Affero General Public License as |
||||
|
# published by the Free Software Foundation, either version 3 of the |
||||
|
# License, or (at your option) any later version. |
||||
|
# |
||||
|
# This program is distributed in the hope that it will be useful, |
||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
|
# GNU Affero General Public License for more details. |
||||
|
# |
||||
|
# You should have received a copy of the GNU Affero General Public License |
||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||
|
# |
||||
|
############################################################################## |
||||
|
|
||||
|
from openerp import models, api |
||||
|
|
||||
|
|
||||
|
class PosOrder(models.Model): |
||||
|
_inherit = 'pos.order' |
||||
|
|
||||
|
@api.model |
||||
|
def search_read_orders(self, query): |
||||
|
condition = [('pos_reference', 'ilike', query)] |
||||
|
fields = ['pos_reference', 'partner_id'] |
||||
|
return self.search_read(condition, fields, limit=10) |
||||
|
|
||||
|
@api.one |
||||
|
def load_order(self): |
||||
|
condition = [('order_id', '=', self.id)] |
||||
|
fields = ['product_id', 'price_unit', 'qty', 'discount'] |
||||
|
orderlines = self.lines.search_read(condition, fields) |
||||
|
return { |
||||
|
'partner_id': self.partner_id and self.partner_id.id or False, |
||||
|
'orderlines': orderlines |
||||
|
} |
@ -0,0 +1,51 @@ |
|||||
|
.screen .top-content .button.validate { |
||||
|
left: 0px; |
||||
|
margin-left: 150px; |
||||
|
} |
||||
|
|
||||
|
.pos .orderlist-screen .order-list{ |
||||
|
font-size: 16px; |
||||
|
width: 100%; |
||||
|
line-height: 40px; |
||||
|
} |
||||
|
.pos .orderlist-screen .order-list th, |
||||
|
.pos .orderlist-screen .order-list td { |
||||
|
padding: 0px 8px; |
||||
|
} |
||||
|
.pos .orderlist-screen .order-list tr{ |
||||
|
transition: all 150ms linear; |
||||
|
background: rgb(230,230,230); |
||||
|
cursor: pointer; |
||||
|
} |
||||
|
.pos .orderlist-screen .order-list thead > tr, |
||||
|
.pos .orderlist-screen .order-list tr:nth-child(even) { |
||||
|
background: rgb(247,247,247); |
||||
|
} |
||||
|
.pos .orderlist-screen .order-list tr.highlight{ |
||||
|
transition: all 150ms linear; |
||||
|
background: rgb(110,200,155) !important; |
||||
|
color: white; |
||||
|
} |
||||
|
.pos .orderlist-screen .order-list tr.lowlight{ |
||||
|
transition: all 150ms linear; |
||||
|
background: rgb(216, 238, 227); |
||||
|
} |
||||
|
.pos .orderlist-screen .order-list tr.lowlight:nth-child(even){ |
||||
|
transition: all 150ms linear; |
||||
|
background: rgb(227, 246, 237); |
||||
|
} |
||||
|
.pos .orderlist-screen .order-details{ |
||||
|
padding: 16px; |
||||
|
border-bottom: solid 5px rgb(110,200,155); |
||||
|
} |
||||
|
|
||||
|
.pos .orderlist-screen .searchbox{ |
||||
|
right: auto; |
||||
|
margin-left: -90px; |
||||
|
margin-top:8px; |
||||
|
left: 50%; |
||||
|
} |
||||
|
.pos .orderlist-screen .searchbox input{ |
||||
|
width: 120px; |
||||
|
} |
||||
|
|
@ -0,0 +1,238 @@ |
|||||
|
/****************************************************************************** |
||||
|
* Point Of Sale - Product Template module for Odoo |
||||
|
* Copyright (C) 2014-Today Akretion (http://www.akretion.com)
|
||||
|
* @author Sylvain Calador (sylvain.calador@akretion.com) |
||||
|
* This program is free software: you can redistribute it and/or modify |
||||
|
* it under the terms of the GNU Affero General Public License as |
||||
|
* published by the Free Software Foundation, either version 3 of the |
||||
|
* License, or (at your option) any later version. |
||||
|
* This program is distributed in the hope that it will be useful, |
||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
|
* GNU Affero General Public License for more details. |
||||
|
* You should have received a copy of the GNU Affero General Public License |
||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
*****************************************************************************/ |
||||
|
|
||||
|
openerp.pos_order_load = function(instance, local) { |
||||
|
module = instance.point_of_sale; |
||||
|
var QWeb = instance.web.qweb; |
||||
|
var _t = instance.web._t; |
||||
|
var round_pr = instance.web.round_precision; |
||||
|
|
||||
|
module.LoadButtonWidget = module.PosBaseWidget.extend({ |
||||
|
template: 'LoadButtonWidget', |
||||
|
init: function(parent, options){ |
||||
|
options = options || {}; |
||||
|
this._super(parent, options); |
||||
|
}, |
||||
|
start: function() { |
||||
|
var self = this; |
||||
|
this.$el.click(function(){ |
||||
|
var ss = self.pos.pos_widget.screen_selector; |
||||
|
ss.set_current_screen('orderlist'); |
||||
|
}); |
||||
|
}, |
||||
|
show: function(){ |
||||
|
this.$el.removeClass('oe_hidden'); |
||||
|
}, |
||||
|
hide: function(){ |
||||
|
this.$el.addClass('oe_hidden'); |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
module.PosWidget = module.PosWidget.extend({ |
||||
|
build_widgets: function() { |
||||
|
this._super(); |
||||
|
|
||||
|
this.orderlist_screen = new module.OrderListScreenWidget(this, {}); |
||||
|
this.orderlist_screen.appendTo(this.$('.screens')); |
||||
|
this.orderlist_screen.hide(); |
||||
|
|
||||
|
this.load_button = new module.LoadButtonWidget(this); |
||||
|
this.load_button.appendTo(this.$('li.orderline.empty')); |
||||
|
|
||||
|
this.screen_selector.screen_set['orderlist'] = |
||||
|
this.orderlist_screen; |
||||
|
}, |
||||
|
}); |
||||
|
|
||||
|
module.OrderWidget = module.OrderWidget.extend({ |
||||
|
renderElement: function(scrollbottom){ |
||||
|
this._super(scrollbottom); |
||||
|
if (this.pos_widget.load_button) { |
||||
|
this.pos_widget.load_button.appendTo( |
||||
|
this.pos_widget.$('li.orderline.empty') |
||||
|
); |
||||
|
} |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
module.OrderListScreenWidget = module.ScreenWidget.extend({ |
||||
|
template: 'OrderListScreenWidget', |
||||
|
show_leftpane: true, |
||||
|
|
||||
|
init: function(parent, options){ |
||||
|
this._super(parent, options); |
||||
|
}, |
||||
|
|
||||
|
start: function() { |
||||
|
var self = this; |
||||
|
this._super(); |
||||
|
this.$el.find('span.button.back').click(function(){ |
||||
|
order = self.pos.get('selectedOrder'); |
||||
|
order.set_client(undefined); |
||||
|
order.get('orderLines').reset(); |
||||
|
self.pos_widget.order_widget.change_selected_order(); |
||||
|
var ss = self.pos.pos_widget.screen_selector; |
||||
|
|
||||
|
ss.set_current_screen('products'); |
||||
|
}); |
||||
|
this.$el.find('span.button.validate').click(function(){ |
||||
|
var ss = self.pos.pos_widget.screen_selector; |
||||
|
ss.set_current_screen('products'); |
||||
|
}); |
||||
|
|
||||
|
var search_timeout = null; |
||||
|
|
||||
|
this.$('.searchbox input').on('keyup',function(event){ |
||||
|
clearTimeout(search_timeout); |
||||
|
|
||||
|
var query = this.value; |
||||
|
|
||||
|
search_timeout = setTimeout(function(){ |
||||
|
self.perform_search(query); |
||||
|
},70); |
||||
|
|
||||
|
}); |
||||
|
|
||||
|
this.$('.searchbox .search-clear').click(function(){ |
||||
|
self.clear_search(); |
||||
|
}); |
||||
|
|
||||
|
}, |
||||
|
|
||||
|
// to override if necessary
|
||||
|
add_product_attribute: function(product, key, orderline){ |
||||
|
return product; |
||||
|
}, |
||||
|
|
||||
|
load_order: function(order_id) { |
||||
|
var self = this; |
||||
|
var posOrderModel = new instance.web.Model('pos.order'); |
||||
|
return posOrderModel.call('load_order', [order_id]) |
||||
|
.then(function (result) { |
||||
|
var order = self.pos.get('selectedOrder'); |
||||
|
order.get('orderLines').reset(); |
||||
|
|
||||
|
var partner = self.pos.db.get_partner_by_id( |
||||
|
result[0].partner_id); |
||||
|
order.set_client(partner || undefined); |
||||
|
|
||||
|
var orderlines = result[0].orderlines || []; |
||||
|
for (var i=0, len=orderlines.length; i<len; i++) { |
||||
|
var orderline = orderlines[i]; |
||||
|
var product_id = orderline.product_id[0]; |
||||
|
var product = self.pos.db.get_product_by_id(product_id); |
||||
|
var options = { |
||||
|
quantity: orderline.qty, |
||||
|
price: orderline.price_unit, |
||||
|
discount: orderline.discount, |
||||
|
} |
||||
|
|
||||
|
for (key in orderline) { |
||||
|
if (!key.indexOf('product__')) { |
||||
|
product = self.add_product_attribute( |
||||
|
product, key, orderline |
||||
|
); |
||||
|
} |
||||
|
} |
||||
|
order.addProduct(product, options); |
||||
|
last_orderline = order.getLastOrderline(); |
||||
|
last_orderline = jQuery.extend(last_orderline, orderline); |
||||
|
} |
||||
|
|
||||
|
}).fail(function (error, event){ |
||||
|
if (error.code === 200) { |
||||
|
// Business Logic Error, not a connection problem
|
||||
|
self.pos_widget.screen_selector.show_popup( |
||||
|
'error-traceback', { |
||||
|
message: error.data.message, |
||||
|
comment: error.data.debug |
||||
|
}); |
||||
|
} |
||||
|
console.error('Failed to load order:', order_id); |
||||
|
self.pos_widget.screen_selector.show_popup('error',{ |
||||
|
message: 'Connection error', |
||||
|
comment: 'Can not execute this action because the POS \ |
||||
|
is currently offline', |
||||
|
}); |
||||
|
event.preventDefault(); |
||||
|
}); |
||||
|
}, |
||||
|
|
||||
|
load_orders: function(query) { |
||||
|
var self = this; |
||||
|
var posOrderModel = new instance.web.Model('pos.order'); |
||||
|
return posOrderModel.call('search_read_orders', [query || '']) |
||||
|
.then(function (result) { |
||||
|
self.render_list(result); |
||||
|
}).fail(function (error, event){ |
||||
|
if (error.code === 200) { |
||||
|
// Business Logic Error, not a connection problem
|
||||
|
self.pos_widget.screen_selector.show_popup( |
||||
|
'error-traceback', { |
||||
|
message: error.data.message, |
||||
|
comment: error.data.debug |
||||
|
} |
||||
|
); |
||||
|
} |
||||
|
console.error('Failed to load orders:', query); |
||||
|
self.pos_widget.screen_selector.show_popup('error',{ |
||||
|
message: 'Connection error', |
||||
|
comment: 'Can not execute this action because the POS \ |
||||
|
is currently offline', |
||||
|
}); |
||||
|
event.preventDefault(); |
||||
|
}); |
||||
|
}, |
||||
|
|
||||
|
show: function() { |
||||
|
this._super(); |
||||
|
var ss = this.pos.pos_widget.screen_selector; |
||||
|
if (ss.get_current_screen() == 'orderlist') { |
||||
|
this.load_orders(); |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
render_list: function(orders){ |
||||
|
var self = this; |
||||
|
var contents = this.$el[0].querySelector('.order-list-contents'); |
||||
|
contents.innerHTML = ""; |
||||
|
for (var i = 0, len = orders.length; i < len; i++){ |
||||
|
var order = orders[i]; |
||||
|
var orderline_html = QWeb.render('LoadOrderLine', |
||||
|
{widget: this, order:orders[i]}); |
||||
|
var orderline = document.createElement('tbody'); |
||||
|
orderline.innerHTML = orderline_html; |
||||
|
orderline = orderline.childNodes[1]; |
||||
|
orderline.addEventListener('click', function() { |
||||
|
self.load_order(parseInt(this.dataset['orderId'])); |
||||
|
}); |
||||
|
contents.appendChild(orderline); |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
perform_search: function(query){ |
||||
|
this.load_orders(query) |
||||
|
}, |
||||
|
|
||||
|
clear_search: function(){ |
||||
|
this.load_orders(); |
||||
|
this.$('.searchbox input')[0].value = ''; |
||||
|
this.$('.searchbox input').focus(); |
||||
|
}, |
||||
|
|
||||
|
}); |
||||
|
|
||||
|
} |
@ -0,0 +1,68 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<template> |
||||
|
|
||||
|
<t t-name="LoadButtonWidget"> |
||||
|
<button class="order-load">Load</button> |
||||
|
</t> |
||||
|
|
||||
|
<t t-name="LoadOrderLine"> |
||||
|
<tr class="order-line" t-att-data-order-id="order.id"> |
||||
|
<td><t t-esc="order.pos_reference"/></td> |
||||
|
<td t-if="order.partner_id"><t t-esc="order.partner_id[1]"/></td> |
||||
|
<td t-if="!order.partner_id"></td> |
||||
|
</tr> |
||||
|
</t> |
||||
|
|
||||
|
<t t-name="OrderListScreenWidget"> |
||||
|
<div class="orderlist-screen screen"> |
||||
|
<div class="screen-content"> |
||||
|
<section class="top-content"> |
||||
|
<span class="button back"> |
||||
|
<i class="fa fa-angle-double-left"></i> |
||||
|
Cancel |
||||
|
</span> |
||||
|
<span class="button validate"> |
||||
|
<i class="fa fa-angle-double-left"></i> |
||||
|
Validate |
||||
|
</span> |
||||
|
<span class="searchbox"> |
||||
|
<input placeholder="Search Orders" /> |
||||
|
<span class="search-clear"></span> |
||||
|
</span> |
||||
|
<span class="searchbox"></span> |
||||
|
<span class="button next oe_hidden highlight"> |
||||
|
Select Order |
||||
|
<i class="fa fa-angle-double-right"></i> |
||||
|
</span> |
||||
|
</section> |
||||
|
<section class="full-content"> |
||||
|
<div class="window"> |
||||
|
<section class="subwindow collapsed"> |
||||
|
<div class="subwindow-container"> |
||||
|
<div class="subwindow-container-fix order-details-contents"> |
||||
|
</div> |
||||
|
</div> |
||||
|
</section> |
||||
|
<section class="subwindow"> |
||||
|
<div class="subwindow-container"> |
||||
|
<div class="subwindow-container-fix touch-scrollable scrollable-y"> |
||||
|
<table class="order-list"> |
||||
|
<thead> |
||||
|
<tr> |
||||
|
<th>Order</th> |
||||
|
<th>Customer</th> |
||||
|
</tr> |
||||
|
</thead> |
||||
|
<tbody class="order-list-contents"> |
||||
|
</tbody> |
||||
|
</table> |
||||
|
</div> |
||||
|
</div> |
||||
|
</section> |
||||
|
</div> |
||||
|
</section> |
||||
|
</div> |
||||
|
</div> |
||||
|
</t> |
||||
|
|
||||
|
</template> |
@ -0,0 +1,18 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<!-- vim:fdn=3: |
||||
|
--> |
||||
|
<openerp> |
||||
|
<data> |
||||
|
<template id="assets_backend" name="pos_order_load assets" inherit_id="web.assets_backend"> |
||||
|
<xpath expr="." position="inside"> |
||||
|
<script type="text/javascript" src="/pos_order_load/static/src/js/pos_order_load.js"></script> |
||||
|
</xpath> |
||||
|
</template> |
||||
|
<template id="index_pos_order_load" name="POS Index Order Load" inherit_id="point_of_sale.index"> |
||||
|
<xpath expr="//head" position="inside"> |
||||
|
<link rel="stylesheet" href="/pos_order_load/static/src/css/pos_order_load.css"/> |
||||
|
</xpath> |
||||
|
</template> |
||||
|
</data> |
||||
|
</openerp> |
||||
|
|
Write
Preview
Loading…
Cancel
Save
Reference in new issue