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