[MERGE] merge the branch of the point_of_sale

bzr revid: stw@openerp.com-20120822093223-sdf72z5vhw2cejac
This commit is contained in:
Stephane Wirtel 2012-08-22 11:32:23 +02:00
commit d0057766ab
45 changed files with 6272 additions and 15077 deletions

File diff suppressed because it is too large Load Diff

View File

@ -83,16 +83,16 @@ Main features:
'application': True,
# Web client
'js': [
'static/lib/backbone/backbone-0.9.2.js',
'static/lib/mousewheel/jquery.mousewheel-3.0.6.js',
'static/src/js/pos_models.js',
'static/src/js/pos_basewidget.js',
'static/src/js/pos_keyboard_widget.js',
'static/src/js/pos_scrollbar_widget.js',
'static/src/js/pos_widgets.js',
'static/src/js/pos_devices.js',
'static/src/js/pos_screens.js',
'static/src/js/pos_main.js'
'static/src/js/db.js',
'static/src/js/models.js',
'static/src/js/widget_base.js',
'static/src/js/widget_keyboard.js',
'static/src/js/widget_scrollbar.js',
'static/src/js/widgets.js',
'static/src/js/devices.js',
'static/src/js/screens.js',
'static/src/js/main.js'
],
'css': [
'static/src/css/pos.css',

View File

@ -34,8 +34,8 @@ class account_journal(osv.osv):
'self_checkout_payment_method' : fields.boolean('Self Checkout Payment Method'),
}
_defaults = {
'opening_control' : True,
'closing_control' : True,
'opening_control' : False,
'closing_control' : False,
'self_checkout_payment_method' : False,
}

View File

@ -0,0 +1,3 @@
import main
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -1,14 +1,71 @@
# -*- coding: utf-8 -*-
import logging
import simplejson
import os
import openerp
try:
import openerp.addons.web.common.http as openerpweb
from openerp.addons.web.controllers.main import manifest_list, module_boot, html_template
except ImportError:
import web.common.http as openerpweb
class PointOfSaleController(openerpweb.Controller):
_cp_path = '/pos'
@openerpweb.httprequest
def app(self, req, s_action=None, **kw):
js = "\n ".join('<script type="text/javascript" src="%s"></script>' % i for i in manifest_list(req, None, 'js'))
css = "\n ".join('<link rel="stylesheet" href="%s">' % i for i in manifest_list(req, None, 'css'))
cookie = req.httprequest.cookies.get("instance0|session_id")
session_id = cookie.replace("%22","")
template = html_template.replace('<html','<html manifest="/pos/manifest?session_id=%s"'%session_id)
r = template % {
'js': js,
'css': css,
'modules': simplejson.dumps(module_boot(req)),
'init': 'var wc = new s.web.WebClient();wc.appendTo($(document.body));'
}
return r
@openerpweb.httprequest
def manifest(self, req, **kwargs):
""" This generates a HTML5 cache manifest files that preloads the categories and products thumbnails
and other ressources necessary for the point of sale to work offline """
ml = ["CACHE MANIFEST"]
# loading all the images in the static/src/img/* directories
def load_css_img(srcdir,dstdir):
for f in os.listdir(srcdir):
path = os.path.join(srcdir,f)
dstpath = os.path.join(dstdir,f)
if os.path.isdir(path) :
load_css_img(path,dstpath)
elif f.endswith(('.png','.PNG','.jpg','.JPG','.jpeg','.JPEG','.gif','.GIF')):
ml.append(dstpath)
imgdir = openerp.modules.get_module_resource('point_of_sale','static/src/img');
load_css_img(imgdir,'/point_of_sale/static/src/img')
products = req.session.model('product.product')
for p in products.search_read([('pos_categ_id','!=',False)], ['name']):
product_id = p['id']
url = "/web/binary/image?session_id=%s&model=product.product&field=image&id=%s" % (req.session_id, product_id)
ml.append(url)
categories = req.session.model('pos.category')
for c in categories.search_read([],['name']):
category_id = c['id']
url = "/web/binary/image?session_id=%s&model=pos.category&field=image&id=%s" % (req.session_id, category_id)
ml.append(url)
ml += ["NETWORK:","*"]
m = "\n".join(ml)
return m
@openerpweb.jsonrequest
def dispatch(self, request, iface, **kwargs):
method = 'iface_%s' % iface
@ -109,3 +166,4 @@ class PointOfSaleController(openerpweb.Controller):
print 'print_receipt' + str(receipt)
return

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 75 KiB

View File

@ -0,0 +1,506 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="744.09448819"
height="1052.3622047"
id="svg2"
version="1.1"
inkscape:version="0.48.3.1 r9886"
sodipodi:docname="barcode_test_sheet.svg">
<defs
id="defs4" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="1"
inkscape:pageshadow="2"
inkscape:zoom="0.86706515"
inkscape:cx="381.55817"
inkscape:cy="597.57819"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="1301"
inkscape:window-height="744"
inkscape:window-x="1985"
inkscape:window-y="24"
inkscape:window-maximized="1" />
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<g
id="g4158"
transform="matrix(0.92675002,0,0,0.92675002,27.597551,38.360312)">
<text
sodipodi:linespacing="125%"
id="text2985"
y="72.12545"
x="140.86679"
style="font-size:46.51613998px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
xml:space="preserve"><tspan
style="font-style:normal;font-variant:normal;font-weight:500;font-stretch:normal;font-family:Inconsolata;-inkscape-font-specification:Inconsolata Medium"
y="72.12545"
x="140.86679"
id="tspan2987"
sodipodi:role="line">BARCODE TEST SHEET</tspan></text>
<g
transform="translate(0,-5.3405762e-5)"
id="g3347">
<image
y="124.14642"
x="388.17426"
id="image3083"
xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAOYAAACYCAIAAAB/IAJdAAAAA3NCSVQICAjb4U/gAAADqUlEQVR4 nO3a226jMABFUYjy/7/MPCChDBfHARM4zVpP1dQY4u66JEw/DEO3S9/3XdeNh79+vRwz2hr5OmZr 5JH5t0YuX0X51W0dW3PNNa9xOWf5au8wsrxu9Ud95HHkYPg+yRJGsoSRLGEkSxjJEkayhJEsYSRL GMkSRrKEkSxhJEsYyRJGsoSRLGEkSxjJEkayhJEsYSRLGMkSRrKEkSxhJEsYyRJGsoSRLGEkSxjJ EkayhJEsYSRLGMkSRrKEkSxhJEsYyRJGsoSRLGEkSxjJEkayhJEsYSRLGMkSRrKEkSxhJEsYyRJG soSRLGEkSxjJEkayhJEsYSRLGMkSRrKEkSxhJEsYyRJGsoSRLGEkSxjJEkayhJEsYSRLGMkSRrKE kSxhJEsYyRJGsoSRLGEkSxjJEkayhJEsYSRLGMkSRrKEkSxhJEsYyRJGsoSRLGEkSxjJEkayhJEs YSRLGMkSRrKEkSxhJEsYyRJGsoSRLGEkSxjJEkayhJEsYSRLGMkSRrKEkSxhJEsYyRJGsoSRLGEk SxjJEkayhJEsYSRLGMkSRrKEkSxhJEsYyRJGsoSRLGEkSxjJEkayhJEsYSRLGMkSRrKEkSxh+mEY dh7Z913XjYe/fv3LrMlS8zWxyxJGsjuNGwbf91z911Z/1GY/18oJ689+cOTb7LZmPtJr/ZrcfOTu 1SucseaQlV221f6xnKdm5vqznzHy1Nn6vq9fk5SRrYzzry7RzHyXbd7r9HszXVP9b3bN/EdGlq/k 0y250uu0W2tSv3oXjvxo9d4ahmE6b/mP58ou2/BNbuUr3Hf2M0aOzthRVn8M5QurX71rR87sXr1h GGa/z6tT/Zds2y12+TpfP+nYOqp+/rYjZ4esXvzk0zkLlmtSv3rXjlx1/F3QMtzZgMfse1d9jvi1 t1zND2HScPUK2+2j+cl2uEmvl/Bh2Zat+4TH64gLruvzs599C3vSOqz+Yf1LvZ60estwn93ehdt9 ez5zk1vYJmrW5C9lWqNVJ5OnW4IjR31kudFWvqe5v/NWb/lE47n6jbOvo3xZhbOfMfLL7n97fRNb j9/WH9j+mgtTtsUW5hzNN6PVMx2/iMLO93ba798tfPp6d398tjyqfqFu+I9vv7XD1uY6Ofd/ci0/ o/5ZhR/2lvrVu3ZkK7PPX7ee2px1YzA9Mn77S/Nrlj/+1UdNlat37ciGCncCMyfuslsP/e7mazey y22j8Pi3fvWuHdlq9cYZap6H/wMqFm5yx9XfMwAAAABJRU5ErkJggg== "
height="107.96206"
width="163.36363" />
<rect
style="fill:none;stroke:#c3c3c3;stroke-width:1.07903957;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
id="rect3086"
width="417.54495"
height="126.4052"
x="141.08452"
y="114.51707"
rx="2.0839903"
ry="2.0839903" />
<text
xml:space="preserve"
style="font-size:27.53050041px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
x="201.46712"
y="147.73848"
id="text3856"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan3858"
x="201.46712"
y="147.73848"
style="font-style:normal;font-variant:normal;font-weight:500;font-stretch:normal;font-family:Inconsolata;-inkscape-font-specification:Inconsolata Medium">CASHIER</tspan></text>
<text
sodipodi:linespacing="125%"
id="text3860"
y="182.10147"
x="154.5717"
style="font-size:17.41555405px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
xml:space="preserve"><tspan
style="font-style:normal;font-variant:normal;font-weight:500;font-stretch:normal;font-family:Inconsolata;-inkscape-font-specification:Inconsolata Medium"
y="182.10147"
x="154.5717"
id="tspan3862"
sodipodi:role="line">Prefix: 40</tspan></text>
<text
xml:space="preserve"
style="font-size:17.41555405px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
x="154.5717"
y="200.85837"
id="text3149"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan3151"
x="154.5717"
y="200.85837"
style="font-style:normal;font-variant:normal;font-weight:500;font-stretch:normal;font-family:Inconsolata;-inkscape-font-specification:Inconsolata Medium">ID: 4447190000</tspan></text>
</g>
<g
transform="translate(0,0.97855377)"
id="g3335">
<g
id="g3173"
transform="translate(0,-3.2620699)">
<text
sodipodi:linespacing="125%"
id="text3153"
y="341.12738"
x="154.5717"
style="font-size:17.41555405px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
xml:space="preserve"><tspan
style="font-style:normal;font-variant:normal;font-weight:500;font-stretch:normal;font-family:Inconsolata;-inkscape-font-specification:Inconsolata Medium"
y="341.12738"
x="154.5717"
id="tspan3155"
sodipodi:role="line">Prefix: 42</tspan></text>
<text
xml:space="preserve"
style="font-size:17.41555405px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
x="154.5717"
y="359.88428"
id="text3157"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan3159"
x="154.5717"
y="359.88428"
style="font-style:normal;font-variant:normal;font-weight:500;font-stretch:normal;font-family:Inconsolata;-inkscape-font-specification:Inconsolata Medium">ID: 1182180000</tspan></text>
</g>
<g
id="g3954"
transform="translate(0,-18.756902)">
<rect
style="fill:none;stroke:#c3c3c3;stroke-width:1.07903957;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
id="rect3876"
width="417.54495"
height="126.4052"
x="141.08452"
y="284.96024"
rx="2.0839903"
ry="2.0839903" />
<text
xml:space="preserve"
style="font-size:27.53050041px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
x="209.6223"
y="318.18164"
id="text3878"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan3880"
x="209.6223"
y="318.18164"
style="font-style:normal;font-variant:normal;font-weight:500;font-stretch:normal;font-family:Inconsolata;-inkscape-font-specification:Inconsolata Medium">CLIENT</tspan></text>
<image
width="162.31204"
height="107.26709"
xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAOYAAACYCAIAAAB/IAJdAAAAA3NCSVQICAjb4U/gAAADyUlEQVR4 nO3a3Y6qShSFUTC+/yvXuSAxbCgQsPiZp8e46mgJq/ETxe6+lNId0vd913XDw8c/z+8d3zIYP2p8 y9LKLbcvbXN8y5Zt/jLn0jHZcnzm29z+m961cmn+vY/a5fXLg+F6kiWMZAkjWcJIljCSJYxkCSNZ wkiWMJIljGQJI1nCSJYwkiWMZAkjWcJIljCSJYxkCSNZwkiWMJIljGQJI1nCSJYwkiWMZAkjWcJI ljCSJYxkCSNZwkiWMJIljGQJI1nCSJYwkiWMZAkjWcJIljCSJYxkCSNZwkiWMJIljGQJI1nCSJYw kiWMZAkjWcJIljCSJYxkCSNZwkiWMJIljGQJI1nCSJYwkiWMZAkjWcJIljCSJYxkCSNZwkiWMJIl jGQJI1nCSJYwkiWMZAkjWcJIljCSJYxkCSNZwkiWMJIljGQJI1nCSJYwkiWMZAkjWcJIljCSJYxk CSNZwkiWMJIljGQJI1nCSJYwkiWMZAkjWcJIljCSJYxkCSNZwkiWMJIljGQJI1nCSJYwkiWMZAkj WcJIljCSJYxkCSNZwkiWMJIljGQJI1nCSJYwkiWMZAkjWcJIljCSJYxkCSNZwkiWMJIljGQJI1nC 9KWUg4/s+67rhoePf/7LHJO55sfEWZYwj0t2eCG2XXmGe/f+l72rt7Z6U5s8r1832LbX+ZqlAc6b 85qpbln59SDs7afvN31MrZxlm5w/+r6fb6d644H9Huu1euOpcx6eqnrj0n7vXdnKsP31Iz+YnmWb TzZ+3Wx/wqoOrJzvfeml3HDO36ear1wa/saV62fovafYUspnv+tv8pWzbMPPA5NNjS8bL1Dd+9gt c36dqnrX+pNy78qJw8etlDJ53VY39U+ybZ+kjb9nGWm1cpfmc/5o6RwzfyHdu3LX8NvNw50seE3u a/J8XPC8bjH5bavXPdfP+XWqXG0TGm92fJTezXf2BJ8PRtsvzy/wzKkea3Je/yT6mqw41e0vjI27 vnjO/0evJx20+eeEd3f0vWnXx/OlC52TVA/f8P6yPsCPc64fk8NTRTt8GbfkffYZ5eJYu9ULiJU+ zp7z2FQpzqtocj4tpbyrdzSZY7zNJz8fKXP+NfNYhx/qf7BtuL+HR5Ay55OdcYpdOY+8Ss1n6Y9z pHTwqDmXvgGdl3HvypNMvtKa7/GU/+S695uB7Qf9yjkPpDD/hn/JvStbmcdaPSxnfTDoVn/PK783 +GXZBdcTS/utfoO7dPV218qGtl9RPO7/ZZuovkBv/5vcrqmqK5c2e+PKVu9Uwxa2PEf/AXnknj+O sxSLAAAAAElFTkSuQmCC "
id="image3951"
x="388.17426"
y="294.58957" />
</g>
</g>
<g
transform="translate(0,0.3261795)"
id="g3322">
<rect
style="fill:none;stroke:#c3c3c3;stroke-width:1.07903957;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
id="rect3964"
width="417.54495"
height="126.4052"
x="141.08452"
y="419.52063"
rx="2.0839903"
ry="2.0839903" />
<text
xml:space="preserve"
style="font-size:27.53050041px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
x="209.6223"
y="452.74203"
id="text3966"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan3968"
x="209.6223"
y="452.74203"
style="font-style:normal;font-variant:normal;font-weight:500;font-stretch:normal;font-family:Inconsolata;-inkscape-font-specification:Inconsolata Medium">WEIGHT</tspan></text>
<image
width="162.12962"
height="107.14654"
xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAOYAAACYCAIAAAB/IAJdAAAAA3NCSVQICAjb4U/gAAAERElEQVR4 nO3a0ZKaQBRFUbX8/182D1QZInRzadDxTNZ6mjgNNrhBYHJ9PB6XIdfr9XK5TIvPf57/drJ8/ayR rVdaG9Wfc+Vd+vPc+3N/hv3XK68st2i5Xa09XNnS+sjKthfdjiwMnydZwkiWMJIljGQJI1nCSJYw kiWMZAkjWcJIljCSJYxkCSNZwkiWMJIljGQJI1nCSJYwkiWMZAkjWcJIljCSJYxkCSNZwkiWMJIl jGQJI1nCSJYwkiWMZAkjWcJIljCSJYxkCSNZwkiWMJIljGQJI1nCSJYwkiWMZAkjWcJIljCSJYxk CSNZwkiWMJIljGQJI1nCSJYwkiWMZAkjWcJIljCSJYxkCSNZwkiWMJIljGQJI1nCSJYwkiWMZAkj WcJIljCSJYxkCSNZwkiWMJIljGQJI1nCSJYwkiWMZAkjWcJIljCSJYxkCSNZwkiWMJIljGQJI1nC SJYwkiWMZAkjWcJIljCSJYxkCSNZwkiWMJIljGQJI1nCSJYwkiWMZAkjWcJIljCSJYxkCSNZwkiW MJIljGQJI1nCSJYwkiWMZAkjWcJIljCSJYxkCSNZwkiWMJIljGQJI1nCSJYwkiWMZAkjWcJIljCS Jcz18XgMLnm9Xi6XafH5z/8z+2Tp9H3iLEuYDyU7HV7njuT/dH/+9NLKid9o5/Zan+fYFnW+vFrT 27uv+hPb3AnLt1su0prSO/beWeVcr6XL1Ns0dLnNqy+OzeOskfV5Dm9RZ8BZe2N1YgfXWXlx11u/ Y2TftFTlM7rP/zFv/B378eDIp+I8V89enUO5MpMjXz7P9S/nP59V/3zWOiX317k6/9aw5TqPj9z0 eDyea+vfpd1Wt/ny7y3eN6jP88jX9wdu8Jfzn2wuWD84W1cOm8Navz1rZN/LfmidcW/H36nz9pUP oz7yUs6uuLa5dx+cB5/vvOmRWeuAb52ex0bunVL/W/Se8uDw+Dw3r1MHTnVjs5qvpP6m9QuG7/lu HNa5Trg3F/oVKrfSR05ge5dt3awMH5DPj7b+0KA/n+/xcrZ+7qhmsm/6JjpdfZ6dMZuLd+5yBuxa ydgHsbxsnS4N407Gy3BXkm3d6KyuqzLyTSrzXH5Cex+Cdt5ifoabBlT2SSWmutZGtVb4+UxP7+Sf v34979EGbl8+aWye9RuIuoMnv039Ge66AVruqy//lCcvzw3+XhjsvSH4Ke+YZ+dR/DfvijFBW9S6 071fylcCP644z19T2wc25GsvZzvnpnvrF1/rxHl2rvZWL3lbTxuK77V6ifmZaPpH+8vlZmeeYyPH ptqa8C3lnPTjj5OW6xz7dlr9Q93YrFoP7Vfz6s+kOM+DI/uWl63r095c0VnP8A8+M9/cF8XHN8Xn WfUT6nCvm2uo7Lf6lu6af32eu7aoo36X8jv/i/fqAXrwBrm1zr0rKa6heJzXt3TXE4P6PI/vk/lS lc/oD6RX6TSeNJuDAAAAAElFTkSuQmCC "
id="image4065"
x="387.35876"
y="429.14993" />
<g
transform="translate(0,140.26901)"
id="g3179">
<text
xml:space="preserve"
style="font-size:17.41555405px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
x="154.5717"
y="341.12738"
id="text3181"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan3183"
x="154.5717"
y="341.12738"
style="font-style:normal;font-variant:normal;font-weight:500;font-stretch:normal;font-family:Inconsolata;-inkscape-font-specification:Inconsolata Medium">Prefix: 21</tspan></text>
<text
sodipodi:linespacing="125%"
id="text3185"
y="359.88428"
x="154.5717"
style="font-size:17.41555405px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
xml:space="preserve"><tspan
style="font-style:normal;font-variant:normal;font-weight:500;font-stretch:normal;font-family:Inconsolata;-inkscape-font-specification:Inconsolata Medium"
y="359.88428"
x="154.5717"
id="tspan3187"
sodipodi:role="line">ID: 12345</tspan></text>
<text
xml:space="preserve"
style="font-size:17.41555405px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
x="154.5717"
y="379.4567"
id="text3189"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan3191"
x="154.5717"
y="379.4567"
style="font-style:normal;font-variant:normal;font-weight:500;font-stretch:normal;font-family:Inconsolata;-inkscape-font-specification:Inconsolata Medium">Value: 67.89Kg</tspan></text>
</g>
</g>
<g
transform="translate(0,-1.9572678)"
id="g3308">
<rect
ry="2.0839903"
rx="2.0839903"
y="574.46893"
x="141.08452"
height="126.4052"
width="417.54495"
id="rect4081"
style="fill:none;stroke:#c3c3c3;stroke-width:1.07903957;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
<g
id="g3296">
<text
xml:space="preserve"
style="font-size:27.53050041px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
x="195.7585"
y="607.69037"
id="text4083"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan4085"
x="195.7585"
y="607.69037"
style="font-style:normal;font-variant:normal;font-weight:500;font-stretch:normal;font-family:Inconsolata;-inkscape-font-specification:Inconsolata Medium">DISCOUNT</tspan></text>
<image
y="584.09827"
x="385.72772"
id="image4202"
xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAOYAAACYCAIAAAB/IAJdAAAAA3NCSVQICAjb4U/gAAADsElEQVR4 nO3aXXOiMACGUej4//8ye+EMZYHECBF96zlXHTd8iE8jYTtO0zQcMo7jMAz3zUs/b8ffLUcuXymN bHn92ZHtR3l2ZOnnpfazOvauS3toP3rLyPr5tGx1wM+ZjeF6kiWMZAkjWcJIljCSJYxkCSNZwkiW MJIljGQJI1nCSJYwkiWMZAkjWcJIljCSJYxkCSNZwkiWMJIljGQJI1nCSJYwkiWMZAkjWcJIljCS JYxkCSNZwkiWMJIljGQJI1nCSJYwkiWMZAkjWcJIljCSJYxkCSNZwkiWMJIljGQJI1nCSJYwkiWM ZAkjWcJIljCSJYxkCSNZwkiWMJIljGQJI1nCSJYwkiWMZAkjWcJIljCSJYxkCSNZwkiWMJIljGQJ I1nCSJYwkiWMZAkjWcJIljCSJYxkCSNZwkiWMJIljGQJI1nCSJYwkiWMZAkjWcJIljCSJYxkCSNZ wkiWMJIljGQJI1nCSJYwkiWMZAkjWcJIljCSJYxkCSNZwkiWMJIljGQJI1nCSJYwkiWMZAkjWcJI ljCSJYxkCSNZwkiWMJIljGQJI1nCSJYwkiWMZAkjWcJIljCSJYxkCSNZwkiWMJIljGQJI1nCjNM0 HdxyHIdhuG++/PmbuSZb3a+JWZYwH5fs/RfxL42kr9vuq6/4UmvZZ0qFZ3pdbVu5II0jSyezO777 0XdP4Fg549h0m7ozy75i/mjZZ0qFHXst7W0cx92RjZufPPrJkc+e1XKT3fe4sp5lr+81pb/zV2be wzyXzJ9TaXZZvl45gZbJqf3oZ0ZWBtdN0zTvrf6FvDPLvmKRa+E8W16K+nf36l+Xi+7XHf3kyIeD 6/tZpb/7Zv+bZd9yS9A4kQSN3FWaOe6zy3ZOumYhsT36s+fZ3eo3c3vE32TfteRi5cDlal8qvcKq qi4TX+U+4bb7ahd6fZfLrvwcVq+HBts9bMO9rUb0pdeOtiFWVj/v1fFz34Z7G46+yfrt+SdcuOu1 L1meUl+Qbc/h2Jr98IltHyMsD939mvy4Jfhk86p5tZquu+bKVxZqQ785a/Xc4PfGoPJA+OTfzfTd 5/dYfU5vPJN3Ka0p9//Dlvcq3Ql8j8pv7K3yNPvMM+Hu+0xXeq5ZuSwPr1Vp2931e+PRD5xnXw8f 2H3cX3L9ecuPpMu902on9Rn64dHbR5buWQ/Hvb1t3d2JG4PrlJ5itvzJ1e74px6Lth/9qfOsn2q7 9nt3s+ylSuvrMzts32f3kaWjH3hT901atv0Hm+wpnHOLKX0AAAAASUVORK5CYII= "
height="108.34499"
width="163.94308"
inkscape:transform-center-x="-24.4145"
inkscape:transform-center-y="6.3942733" />
<g
transform="translate(0,296.03285)"
id="g3210">
<text
xml:space="preserve"
style="font-size:17.41555405px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
x="154.5717"
y="341.12738"
id="text3212"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan3214"
x="154.5717"
y="341.12738"
style="font-style:normal;font-variant:normal;font-weight:500;font-stretch:normal;font-family:Inconsolata;-inkscape-font-specification:Inconsolata Medium">Prefix: 44</tspan></text>
<text
sodipodi:linespacing="125%"
id="text3216"
y="359.88428"
x="154.5717"
style="font-size:17.41555405px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
xml:space="preserve"><tspan
style="font-style:normal;font-variant:normal;font-weight:500;font-stretch:normal;font-family:Inconsolata;-inkscape-font-specification:Inconsolata Medium"
y="359.88428"
x="154.5717"
id="tspan3218"
sodipodi:role="line">ID: 11111</tspan></text>
<text
xml:space="preserve"
style="font-size:17.41555405px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
x="154.5717"
y="379.4567"
id="text3220"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan3222"
x="154.5717"
y="379.4567"
style="font-style:normal;font-variant:normal;font-weight:500;font-stretch:normal;font-family:Inconsolata;-inkscape-font-specification:Inconsolata Medium">Value: 25%</tspan></text>
</g>
</g>
</g>
<g
transform="translate(0,-0.97866058)"
id="g3283">
<rect
style="fill:none;stroke:#c3c3c3;stroke-width:1.07903957;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
id="rect4115"
width="417.54495"
height="126.4052"
x="141.08452"
y="726.15521"
rx="2.0839903"
ry="2.0839903" />
<text
xml:space="preserve"
style="font-size:27.53050041px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
x="212.88437"
y="759.37659"
id="text4117"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan4119"
x="212.88437"
y="759.37659"
style="font-style:normal;font-variant:normal;font-weight:500;font-stretch:normal;font-family:Inconsolata;-inkscape-font-specification:Inconsolata Medium">PRICE</tspan></text>
<image
width="163.36363"
height="107.96206"
xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAOYAAACYCAIAAAB/IAJdAAAAA3NCSVQICAjb4U/gAAAD70lEQVR4 nO3Z3XKbOhiGUezJ/d8yPfBsbwaB+Phz/aZrHaWukDB+goE8xnEcDnk8HsMwvDaf/tz+7/SVl3bR drbEkWvHZG3O/gytyvxrq6yt265emWfvyMq7K3qe2Rg+T7KEkSxhJEsYyRJGsoSRLGEkSxjJEkay hJEsYSRLGMkSRrKEkSxhJEsYyRJGsoSRLGEkSxjJEkayhJEsYSRLGMkSRrKEkSxhJEsYyRJGsoSR LGEkSxjJEkayhJEsYSRLGMkSRrKEkSxhJEsYyRJGsoSRLGEkSxjJEkayhJEsYSRLGMkSRrKEkSxh JEsYyRJGsoSRLGEkSxjJEkayhJEsYSRLGMkSRrKEkSxhJEsYyRJGsoSRLGEkSxjJEkayhJEsYSRL GMkSRrKEkSxhJEsYyRJGsoSRLGEkSxjJEkayhJEsYSRLGMkSRrKEkSxhJEsYyRJGsoSRLGEkSxjJ EkayhJEsYSRLGMkSRrKEkSxhJEsYyRJGsoSRLGEkSxjJEkayhJEsYSRLGMkSRrKEkSxhJEsYyRJG soSRLGEkSxjJEkayhJEsYSRLGMkSRrKEkSxhJEsYyRJGsoSRLGEkSxjJEkayhJEsYSRLmMc4jge3 fDyGYXhtPv35X+aYtC4/Js6yhJHsX/M65bDXz/un2RG85ButPufvG9lX6fXYWp0v37VFK4M/c0wq 2z4XV118Ze/yi3PWX6zP+W0jN21uVT9Q9Zl37Wr9nV57TCrv8We6xrvx9/bnz7XTGTp70w5bWz1l ZGvvZzmbdnO5yvyVXa33cGE54zi+Z+vfpT2n2yz+fED7Tqb/nB7ZxZ3rfE99/8jzdn19HxhTVO/h qnLGcZydERYPxXOt6DavA3twctv6aeP7Rw7/fSSzD6YzrDLn21U3c/UebiqnDXc24Ge4gYeRd9i8 Tt31pf/ytZ9U5zrhlmTPq/+Opow8pp2/jaze69r83xnu7Gz93tWPPpdtD9Dil8hiBykj79O5Wths bnp3cezC429prxOOn2V3XXSv3ZC1AzZ988gLb2HbSRbPiPXL6MUXL3w0VFn6klVuP8u+7/vWfrPb 19NH3qR+A7R3zm82e24wjuON17KzlfqD68cuZeTHrP3hY/jKva1bu1O8K9nNK4Hi5r9mZN0vqO28 zvnu53VBs/aHjZMH7uQ17uLzuS8f+UmL63YuedeeNszmLPZwUzmbj+Gei0NPnjzqO9056KEjr9JO fv4ksvbEY/Pd9d/pVeW0l63LO7b4+Oa9zeG1+wMWT2Cbq6eMrOj31zmAxS+uk/t/x294X/3O5/+z +oV/F9mVbLvJxh6HjOyrnDKPfSK7fhk6c94xsq/4rO0Pc0/7XuEpWE8AAAAASUVORK5CYII= "
id="image4264"
x="386.54324"
y="734.96906" />
<g
transform="translate(0,442.82599)"
id="g3224">
<text
xml:space="preserve"
style="font-size:17.41555405px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
x="154.5717"
y="341.12738"
id="text3226"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan3228"
x="154.5717"
y="341.12738"
style="font-style:normal;font-variant:normal;font-weight:500;font-stretch:normal;font-family:Inconsolata;-inkscape-font-specification:Inconsolata Medium">Prefix: 02</tspan></text>
<text
sodipodi:linespacing="125%"
id="text3230"
y="359.88428"
x="154.5717"
style="font-size:17.41555405px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
xml:space="preserve"><tspan
style="font-style:normal;font-variant:normal;font-weight:500;font-stretch:normal;font-family:Inconsolata;-inkscape-font-specification:Inconsolata Medium"
y="359.88428"
x="154.5717"
id="tspan3232"
sodipodi:role="line">ID: 99999</tspan></text>
<text
xml:space="preserve"
style="font-size:17.41555405px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
x="154.5717"
y="379.4567"
id="text3234"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan3236"
x="154.5717"
y="379.4567"
style="font-style:normal;font-variant:normal;font-weight:500;font-stretch:normal;font-family:Inconsolata;-inkscape-font-specification:Inconsolata Medium">Value: 134.50€</tspan></text>
</g>
</g>
<g
id="g3269">
<g
id="g3061"
transform="translate(0,284.6156)">
<g
id="g3063"
transform="translate(0,154.94832)">
<rect
ry="2.0839903"
rx="2.0839903"
y="438.27753"
x="141.08452"
height="126.4052"
width="417.54495"
id="rect3065"
style="fill:none;stroke:#c3c3c3;stroke-width:1.07903957;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
<text
sodipodi:linespacing="125%"
id="text3067"
y="471.49893"
x="216.61778"
style="font-size:27.53050041px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
xml:space="preserve"><tspan
style="font-style:normal;font-variant:normal;font-weight:500;font-stretch:normal;font-family:Inconsolata;-inkscape-font-specification:Inconsolata Medium"
y="471.49893"
x="216.61778"
sodipodi:role="line"
id="tspan3083">UNIT</tspan></text>
<g
transform="translate(0,156.57936)"
id="g3071" />
</g>
</g>
<image
y="887.47076"
x="386.54324"
id="image3146"
xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAOYAAACYCAIAAAB/IAJdAAAAA3NCSVQICAjb4U/gAAADz0lEQVR4 nO3Z0ZKaMACGUdnx/V+ZXjBjGWKyEQjyt+fcdIsBY/ZTgZ3meX7sMk3T4/FYdq/9XI5frB9db18/ Wh6nfJZyr/Yz9s+t/Yp65tw/22+N3Pe6asevhdQ/stPPkZ3hepIljGQJI1nCSJYwkiWMZAkjWcJI ljCSJYxkCSNZwkiWMJIljGQJI1nCSJYwkiWMZAkjWcJIljCSJYxkCSNZwkiWMJIljGQJI1nCSJYw kiWMZAkjWcJIljCSJYxkCSNZwkiWMJIljGQJI1nCSJYwkiWMZAkjWcJIljCSJYxkCSNZwkiWMJIl jGQJI1nCSJYwkiWMZAkjWcJIljCSJYxkCSNZwkiWMJIljGQJI1nCSJYwkiWMZAkjWcJIljCSJYxk CSNZwkiWMJIljGQJI1nCSJYwkiWMZAkjWcJIljCSJYxkCSNZwkiWMJIljGQJI1nCSJYwkiWMZAkj WcJIljCSJYxkCSNZwkiWMJIljGQJI1nCSJYwkiWMZAkjWcJIljCSJYxkCSNZwkiWMJIljGQJI1nC SJYwkiWMZAkjWcJIljCSJYxkCSNZwkiWMJIljGQJI1nCSJYwkiWMZAkjWcJIljCSJYxkCSNZwkzz PO/cc5oej8ey+/rn/5k1KZ2+Jj5lCSNZwjyXf5ZP7NKJ32v9XwrtkZupNg743ZH3nNWgV3p8WZaD 9Oz4Uz7fCP1P0Rg5TVP5aG38d0fWfHFW/av36TofX5bXLm+PtvF8/TTuQuGUXl/W83y9zs3kX8d5 bb9yZM0dZtWzev0jy2f/9bA18zy/jtb+mr3oXLZ/9u3zgc2j7cOuH71+5K1m1b96B9d5+e+isUvt OJv0336EDU/23I/Y0vruyfo45ZJdM7LmnrM6MnLQXbwy3M2AZ+2xU6Zy1iUXN7eO55RfYuM84Vnb 53hD1/R6wbXjP+z4d2Dt2ut4uJvP9ddhf9Zn0LvPQtrPesrIt19heu3Uv3r71nlEPOsjr2fyrF0t /nrd1z7lH3cKe+dMj1+cjTbi91Jefi1XTsv209ekevl1w1OC8k084m39r+pfvY/WefT6b+4bzPNc PZc95clqGzevs3/k2y30O/FUbbTa/YCByV7jzucJ9zf6FuRujVsQb/56sdnnrHfbwbOF2nwag7+1 ccfrGr2xf/XOWudye6dfb7b+vB13eq/HvZ1J+61f3ve+cuStZtW/emet845yytPW95NZ37PdOLfX 49dkH83zst/cR6v0xVn1r96gdW7r/2PE33tYI/76Vc7p+G2ED15b9ysaMfKes9q3emetc1vjjura Hwkp3Xa7HcbMAAAAAElFTkSuQmCC "
height="108.34499"
width="163.94308" />
<g
id="g3255"
transform="translate(0,598.58983)">
<text
xml:space="preserve"
style="font-size:17.41555405px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
x="154.5717"
y="351.81107"
id="text3261"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan3263"
x="154.5717"
y="351.81107"
style="font-style:normal;font-variant:normal;font-weight:500;font-stretch:normal;font-family:Inconsolata;-inkscape-font-specification:Inconsolata Medium">ID: 5449000000996</tspan></text>
</g>
</g>
<g
transform="matrix(0,-1,1,0,261.82237,1622.2884)"
id="g4150">
<text
xml:space="preserve"
style="font-size:8px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Droid Sans;-inkscape-font-specification:Droid Sans"
x="618.97778"
y="320.0275"
id="text3357"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan3359"
x="618.97778"
y="320.0275">This is a test sheet intended to help you test the codebar aquisition in OpenERP's Point of Sale module. </tspan><tspan
sodipodi:role="line"
x="618.97778"
y="330.0275"
id="tspan3363">The codes provided in this list are randomly chosen and are encoded in the EAN13 format. Their codes</tspan><tspan
sodipodi:role="line"
x="618.97778"
y="340.0275"
id="tspan3367">and prefixes are not intended to match any specified standard. </tspan></text>
<text
sodipodi:linespacing="125%"
id="text3369"
y="349.54001"
x="618.99207"
style="font-size:6.53564215px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#b3b3b3;fill-opacity:1;stroke:none;font-family:Droid Sans;-inkscape-font-specification:Droid Sans"
xml:space="preserve"><tspan
id="tspan3375"
y="349.54001"
x="618.99207"
sodipodi:role="line"
style="fill:#b3b3b3;fill-opacity:1">This document is licensed by OpenERP S.A. under the Creative Commons CC BY 3.0 license htttp://www.creativecommons.org</tspan></text>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 31 KiB

View File

@ -22,6 +22,7 @@
import pdb
import openerp
import addons
import openerp.addons.product.product
import time
from datetime import datetime
@ -59,16 +60,9 @@ class pos_config(osv.osv):
help="Accounting journal used to post sales entries."),
'iface_self_checkout' : fields.boolean('Self Checkout Mode',
help="Check this if this point of sale should open by default in a self checkout mode. If unchecked, OpenERP uses the normal cashier mode by default."),
'iface_websql' : fields.boolean('WebSQL (Faster but Chrome Only)',
help="If have more than 200 products, it's highly suggested to use WebSQL "\
"to store the data in the browser, instead of localStore mechanism. "\
"It's more efficient but works on the Chrome browser only."
),
'iface_led' : fields.boolean('Help Notification'),
'iface_cashdrawer' : fields.boolean('Cashdrawer Interface'),
'iface_payment_terminal' : fields.boolean('Payment Terminal Interface'),
'iface_electronic_scale' : fields.boolean('Electronic Scale Interface'),
'iface_barscan' : fields.boolean('BarScan Interface'),
'iface_vkeyboard' : fields.boolean('Virtual KeyBoard Interface'),
'iface_print_via_proxy' : fields.boolean('Print via Proxy'),
@ -77,7 +71,7 @@ class pos_config(osv.osv):
help="This sequence is automatically created by OpenERP but you can change it "\
"to customize the reference numbers of your orders."),
'session_ids': fields.one2many('pos.session', 'config_id', 'Sessions'),
'group_by' : fields.boolean('Group By', help="Check this if you want to group the Journal Items by Product while closing a Session"),
'group_by' : fields.boolean('Group Journal Items', help="Check this if you want to group the Journal Items by Product while closing a Session"),
}
def name_get(self, cr, uid, ids, context=None):
@ -93,7 +87,7 @@ class pos_config(osv.osv):
result.append((record.id, record.name+' ('+_('not used')+')'))
continue
session = record.session_ids[0]
result.append((record.id, record.name + ' ('+session.user_id.name+', '+states[session.state]+')'))
result.append((record.id, record.name + ' ('+session.user_id.name+')')) #, '+states[session.state]+')'))
return result
@ -143,8 +137,6 @@ class pos_config(osv.osv):
obj.sequence_id.unlink()
return super(pos_config, self).unlink(cr, uid, ids, context=context)
pos_config()
class pos_session(osv.osv):
_name = 'pos.session'
_order = 'id desc'
@ -459,8 +451,6 @@ class pos_session(osv.osv):
'context' : context,
}
pos_session()
class pos_order(osv.osv):
_name = "pos.order"
_description = "Point of Sale"
@ -1079,8 +1069,6 @@ class pos_order(osv.osv):
self.create_account_move(cr, uid, ids, context=context)
return True
pos_order()
class account_bank_statement(osv.osv):
_inherit = 'account.bank.statement'
_columns= {
@ -1179,8 +1167,6 @@ class pos_order_line(osv.osv):
})
return super(pos_order_line, self).copy_data(cr, uid, id, default, context=context)
pos_order_line()
class pos_category(osv.osv):
_name = 'pos.category'
_description = "Point of Sale Category"
@ -1230,6 +1216,11 @@ class pos_category(osv.osv):
'parent_id': fields.many2one('pos.category','Parent Category', select=True),
'child_id': fields.one2many('pos.category', 'parent_id', string='Children Categories'),
'sequence': fields.integer('Sequence', help="Gives the sequence order when displaying a list of product categories."),
# NOTE: there is no 'default image', because by default we don't show thumbnails for categories. However if we have a thumbnail
# for at least one category, then we display a default image on the other, so that the buttons have consistent styling.
# In this case, the default image is set by the js code.
'image': fields.binary("Image",
help="This field holds the image used for the category. "\
"The image is base64 encoded, and PIL-supported. "\
@ -1252,21 +1243,40 @@ class pos_category(osv.osv):
"Use this field anywhere a small image is required."),
}
def _get_default_image(self, cr, uid, context=None):
image_path = openerp.modules.get_module_resource('point_of_sale', 'images', 'default_category_photo.png')
return tools.image_resize_image_big(open(image_path, 'rb').read().encode('base64'))
_defaults = {
'image': _get_default_image,
}
pos_category()
import io, StringIO
class ean_wizard(osv.osv_memory):
_name = 'pos.ean_wizard'
_columns = {
'ean13_pattern': fields.char('Ean13 Pattern', size=32, required=True, translate=True),
}
def sanitize_ean13(self, cr, uid, ids, context):
for r in self.browse(cr,uid,ids):
ean13 = openerp.addons.product.product.sanitize_ean13(r.ean13_pattern)
m = context.get('active_model')
m_id = context.get('active_id')
self.pool.get(m).write(cr,uid,[m_id],{'ean13':ean13})
return { 'type' : 'ir.actions.act_window_close' }
class product_product(osv.osv):
_inherit = 'product.product'
#def _get_small_image(self, cr, uid, ids, prop, unknow_none, context=None):
# result = {}
# for obj in self.browse(cr, uid, ids, context=context):
# if not obj.product_image:
# result[obj.id] = False
# continue
# image_stream = io.BytesIO(obj.product_image.decode('base64'))
# img = Image.open(image_stream)
# img.thumbnail((120, 100), Image.ANTIALIAS)
# img_stream = StringIO.StringIO()
# img.save(img_stream, "JPEG")
# result[obj.id] = img_stream.getvalue().encode('base64')
# return result
_columns = {
'income_pdt': fields.boolean('Point of Sale Cash In', help="This is a product you can use to put cash into a statement for the point of sale backend."),
'expense_pdt': fields.boolean('Point of Sale Cash Out', help="This is a product you can use to take cash from a statement for the point of sale backend, exemple: money lost, transfer to bank, etc."),
@ -1278,7 +1288,16 @@ class product_product(osv.osv):
'to_weight' : False,
}
product_product()
def edit_ean(self, cr, uid, ids, context):
return {
'name': "Edit Ean",
'type': 'ir.actions.act_window',
'view_type': 'form',
'view_mode': 'form',
'res_model': 'pos.ean_wizard',
'target' : 'new',
'view_id': False,
'context':context,
}
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -1,12 +1,40 @@
<?xml version="1.0"?>
<openerp>
<data>
<record id="base.user_root" model="res.users">
<field name="ean13">0410100000006</field>
</record>
<record id="base.user_demo" model="res.users">
<field name="groups_id" eval="[(4,ref('group_pos_user'))]"/>
<field name="ean13">0410200000005</field>
</record>
<record id="account.cash_journal" model="account.journal">
<record id="account.cash_journal" model="account.journal">
<field eval="True" name="journal_user"/>
</record>
<record id="base.user_jsmith" model="res.users">
<field name="name">John Smith (Cashier)</field>
<field name="login">jsmith</field>
<field name="ean13">0410300000004</field>
<field name="groups_id" eval="[(4,ref('group_pos_manager'))]"/>
</record>
<record id="base.user_jdoe" model="res.users">
<field name="name">John Doe (Cashier)</field>
<field name="login">jdoe</field>
<field name="ean13">0410400000003</field>
<field name="groups_id" eval="[(4,ref('group_pos_manager'))]"/>
</record>
<record id="base.user_jbloggs" model="res.users">
<field name="name">Joe Bloggs (Client)</field>
<field name="login">jbloggs</field>
<field name="ean13">0420100000005</field>
<field name="groups_id" eval="[(4,ref('group_pos_user'))]"/>
</record>
<record id="base.user_bsoap" model="res.users">
<field name="name">Bob Soap (Client)</field>
<field name="login">bsoap</field>
<field name="ean13">0420200000004</field>
<field name="groups_id" eval="[(4,ref('group_pos_user'))]"/>
</record>
<!-- Resource: pos.category -->
@ -154,6 +182,7 @@
<field name="list_price">1.98</field>
<field name="name">Boni Oranges</field>
<field name="to_weight">True</field>
<field name="ean13">2100002000003</field>
<field name="pos_categ_id" ref="oranges"/>
<field name="uom_id" ref="product.product_uom_kgm" />
<field name="uom_po_id" ref="product.product_uom_kgm" />
@ -248,6 +277,7 @@
<field name="list_price">5.10</field>
<field name="name">Fishing</field>
<field name="to_weight">True</field>
<field name="ean13">2300001000008</field>
<field name="pos_categ_id" ref="rouges_noyau_fruits"/>
<field name="uom_id" ref="product.product_uom_kgm" />
<field name="uom_po_id" ref="product.product_uom_kgm" />
@ -316,6 +346,7 @@
<field name="list_price">1.28</field>
<field name="name">Onions</field>
<field name="to_weight">True</field>
<field name="ean13">2100001000004</field>
<field name="pos_categ_id" ref="oignons_ail_echalotes"/>
<field name="uom_id" ref="product.product_uom_kgm" />
<field name="uom_po_id" ref="product.product_uom_kgm" />
@ -389,6 +420,7 @@
<record id="coca_regular_33cl" model="product.product">
<field name="list_price">0.51</field>
<field name="name">Coca-Cola Regular 33cl</field>
<field name="ean13">5449000000996</field>
<field name="pos_categ_id" ref="coke"/>
<field name="image">/9j/4AAQSkZJRgABAAEBLAEsAAD/2wCEAAoHBwgHBgoICAgLCgoLDhgQDg0NDh0VFhEYIx8lJCIfIiEmKzcvJik0KSEiMEExNDk7Pj4+JS5ESUM8SDc9PjsBCwoKDg0OHBYWHCgoKCgoOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O//AABEIAGQAOAMBEQACEQEDEQH/xAAfAAABBQEBAQEBAQAAAAAAAAAAAQIDBAUGBwgJCgv/xAC1EAACAQMDAgQDBQUEBAAAAX0BAgMABBEFEiExQQYTUWEHInEUMoGRoQgjQrHBFVLR8CQzYnKCCQoWFxgZGiUmJygpKjQ1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5eoOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4eLj5OXm5+jp6vHy8/T19vf4+fr/xAAfAQADAQEBAQEBAQEBAAAAAAAAAQIDBAUGBwgJCgv/xAC1EQACAQIEBAMEBwUEBAABAncAAQIDEQQFITEGEkFRB2FxEyIygQgUQpGhscEJIzNS8BVictEKFiQ04SXxFxgZGiYnKCkqNTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqCg4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2dri4+Tl5ufo6ery8/T19vf4+fr/2gAMAwEAAhEDEQA/APZSQoJJwByTQBCt1HIxVOSOvtQBBe36WkDyySLGqAse5wKB2M1fEqHDYkZCu7ds4xQFi/Zaql7brPCVdGzjBweDjvQFix9ujVgsmULdM9/oelAiwrB1DKcgjINAFTVL6DT9PluLggIqnI9amUlFXZrQoyrTUY7s5OLWpJbINIs1ozjO6ICRfy4IrGOIT3R3VsslGTUJJ/g/8jPkngaXzZ9Wif2nDrn8wRWqq031OZ4LEL7D+Wo0ahp6uP8AiYaf5f8Ac81cflt/pT9pDuifqtf+SX3MVby2kuPMg1aBPRYSzlfYYGKTq0+5SwWJ/kf3WNy4muLTTUkJkvGlQspYCNcYHXqT1/8A1UqlTlV0h4fC+1m1J2szY8NanHquiQTLtWRB5cqD+Fhwf8fxopT543FjsK8PWcenT0OU8dXUlx4gtNK8wJF5aytlsbiWI/p+PHpXPipNySPZyWlGGHnVtd3t+A6bakaqvQDArJhC7d2Ub1AkJ3gcrke4qZaG9F3locBfMr3blVGAalI+joxtBHVeFirWwIUD6CnHc8fMk1I76JkudJj3n/Uttb6dv6V2RtKHofNyTp13bqZWgXMVp4xbT7NgIZomeRAcjI6GppSSqWR242nKpg/aT3TsjG+IJ2+NLY562i/+hNWeJ+M7sj1wUv8AF+iOTGq3trd7VuJDGH5TOQRmsEe08NTnDZXsdHrtzLcaLbm2eJmMzCBVJHmxMMgjnOQwYEZJycdxW9SKcVY8bBJQry5r7a+TWmvk1Zp9vmZFjoNlqGhwXEt6LK7mkdUeUZicg8KT/C1KMFKCd7M66+ZVqOIlFR5opLbdefmixPpuqeH7Ge4VZ4kDKoUhXVSe+7HzAjoeOeo6ZU6TimzOljKOMqRi2np5p/d0f9INK1u9udL1qKe6dybVWUdMfOAcY/3qVOXuy9C8ThKcK1Fxj9r9H/kaPw+H/FXRe1q9PC/GYZ4/9jf+JFj4h/8AI52h/wCnVf8A0JqrE/GjPIv9yn/i/RHGSQNPfCNMbnfaM+5rnWrse4pqFO76I39X1TSdL1A6GbQ3OnQoI5+fmEo6uno3r64ronOMXy20PHw2GxFel7fm5Zt3Xa3Z9127Fy2tZlsprvSpIdas3wZrdx88o/2l7SD+8OuOmetxi7Nx1X9ficdWrH2ihVTpyWz6L0f8r7dPTavqlzYf8Ii8dn/aUQFwgNtOTtgP905/h7jryO1TUcfZO1zbCU6v11OfI/deq6+fr38jn9PJR7he0tuy/lhv/Za5oPf0PZrK6i+0l/l+p1Xw7GfFg9rVv5itsL8Z5We/7p/28S/Ec7PF1m3/AE7L/wChNV4n40TkCvhJr+9+hyDTJFeq8jFVWQEkdRzXNH4j2nFyptLsXte0uOXVJwkPkSPKQGVyyFzyA2c7d2chuhyOnONqsLs4MFipRoxu7pL52XVd7dVuvuvL4UiSVEax2LqFqWeSA5WS4XqArZGPQjp3welOgrrTdGGaScW1P4JWs+i9VZ+qf5Brl/4hutFf7S9wLRZQk0U8IV0bquWAG4e/HPXtkqSqOOuw8DQwdOuuVLmtdNO6a66X0fkZNm25tw/55t/6Ca51uepU0XzX5nWfDZt/it/a1b+a1vhfj+R5Gfq2FX+L/MsfEqPzPFOnrnG63A/8eNaYle+jPh+XLhaj8/0PPrknz5Of4j/Oudn0kPhXodLpUyeIrKKx+1my1i2TZbXAcqJ0/uMR3Hatov2itezPExcHg6jny81OTu1vZ90NstMaCJbGY2+n6rBIzYu0AW4B6bZOoI6cGiELK2zM6+JU5c6vODX2Xt6r/NEGq6vq2paNKj3Mnk28qpc28gBKNztO7GSuR3OQcdampOco7muEwuHo101FXabT799Nr+n4GXo5LX8cQ/jyv5g1lBXaPSxNlSb7HW/C47vFEv8A16v/AOhLW2G+P5HlcR6YVf4v0ZpfEhf+Km0pvWIj/wAerTEfGjkyF/7NV9Tzi5/4+JM/3j/OuZn1EPhXoafhq1mu5b6KGW3jLWpXNwPlyWULhv4WyQQfUVdKLd/Q87M6saag2m/e6b7O+nVW3RuJrerafbNZ+INMj1JIRl0mH7xF/vZIIYe4/E1sqk0rSVzyZYPD1Z8+HqOF+23p3T8vuMzVvEdpe6VJp+kaQllA7LJOwxlsHgce+KzqVU42irHbg8vqU6yqVajk9kUfC8XneI7KPrmT+hqKKvNHdmMuXCzfkdP8K+fFEx7C0b/0JK0w3x/I83iP/do/4v0Zp/EzP9u6QV67G7e4q8T8SOTh+3sK1/L9Tzi9QR3cqhg2G6iuaW59PSleCZo+GEuPt8skLwCOOL9+lxnZIhIBBwOnIOe2M1VK/McGaSh7NJp3b0tunvp/l12Oy0W4t5fM+yalA1rEGVrS+Ic2zYI+V8/Mvb0wetddNp7PTz6HzuMhONueD5nb3o6c3quj/U5e401IfAQvFaJXmvizYP3lG5QF9ccn8a55w/dX8z2KWIcsx5NbKH52eongGEzeLrQ44jDuf++SP5kVOGV6iNs7ny4Kfnb8zc+FA/4qS79rVv8A0Na0wy/efI5OJH/s8P8AF+jND4mNjxBpABx8jc/jV4j4kc+QL9xV+R59er5upugO3e4GW7Z71yvVn0dJ8tFPyNa10uXStUeay1FWktUdnjERMnysEZWjz05z16c1pGDjK6Z5lbExxFFKcNJNa301V00/61NaaDwta3gv5RcWnygSwxLuhcsuSmMZXr0OOlbctNO+x50amPqQ5FaXZvRqz38/VXOYvLoz+HrKGMkRW9xMAp9DtIz+ZrCbvBep69Cny4mbe7jH9U/0Oj+GNsDf318w4ghCA+7HP/staYRe82cHEdT91CHd/l/w5rfC3TZba/kvJAdtzbNs9MB1zVYaOt/Iw4hxMZpQX2WvyY74lRyHxLpUgU7PKOGxnkNk/pijEfGgyGUfq1VeZ57qY238w56965pbn0lB3pI6W1jm1+KLWNIl8vXLJQJ4sj9+AMBxnjJHBHf+eyTn70d0eHWccJJ0aqvSls+3l/kEGseG2upptV0y4srxlZLiGIHy5SeuVPQ559jzmqjOm3qrEVMJjVFKlNSj0b3XzOdtoTNpGpOisIYXikBPY5Kgfkx/KsbXiz1JS5a9NPdpr8L/AKHoHhi0/sXwBc3kg2yTwyTn6bcKP6/jXVQjyUmz57Mav1nMowWyaX46mv4Jkid7ZIoyPL06Pe/GCSE/wP5GrpWtH0OLMIy9rVb61P8AP/gHTappNnrFqba8i3qDlWBwyH1B7GrnBSVmcmHxFShLmgzzTxJ8OprWRri3vg6N084Yx9SOK5pYZ9GfQ4XiNRjacPu/yf8Amc1BpuoaTdJdW9zGssZyro+B/wCPYzUKjUi7o6amc4HEQcZ3s+6/yN3/AISa11CMJ4g0S2uZFGPNhljyfwzkfnW2sviieW1Sou9DEWXZ3/y/QjnuLXVUh0uysU0zS/NEk77wWkx9M/5xSlTcvdSsi6eMp0G6sqvPO1lo7I7prBPEejvp8BNvauFQyDBwoI4XqO2K3lFSjY8nD4mVGsqlrtfmbOi6HYaDZi1sItinlmJyzn1JpU6cYKyFisXVxM+abuaB6mrZziEBhggEHsaAM668PaPekm406Bye+3H8qVkO7M5vAPhlm3HTh9PMb/GiwcxetPDGh2JBt9MgUjoSu4/rmiyC7NQAKAFAAHQCmIUUIAAA/9k=</field>
</record>
@ -1022,3 +1054,4 @@
</data>
</openerp>

View File

@ -55,10 +55,14 @@
</group>
</form>
</field>
<group class="oe_subtotal_footer">
<group class="oe_subtotal_footer oe_right" colspan="2" name="order_total">
<field name="amount_tax"/>
<field name="amount_total"/>
<button name="button_dummy" string="Update" class="oe_link" states="draft"/>
<div class="oe_subtotal_footer_separator oe_inline">
<label for="amount_total" />
<button name="button_dummy"
states="draft" string="(update)" type="object" class="oe_edit_only oe_link"/>
</div>
<field name="amount_total" nolabel="1" class="oe_subtotal_footer_separator"/>
</group>
</page>
<page string="Payments">
@ -630,6 +634,9 @@
<field name="expense_pdt"/>
</group>
</group>
<field name="ean13" position="after">
<button name="edit_ean" type="object" string="Edit" />
</field>
</field>
</record>
@ -773,10 +780,10 @@
<sheet>
<group>
<field name="name"/>
<field name="shop_id" widget="selection" />
<field name="shop_id" widget="selection" groups="stock.group_locations" />
<field name="journal_id" widget="selection" />
<field name="sequence_id" readonly="1"/>
<field name="group_by" />
<field name="sequence_id" readonly="1" groups="base.group_no_one" />
<field name="group_by" groups="account.group_account_user" />
</group>
<separator string="Available Payment Methods" colspan="4"/>
<field name="journal_ids" colspan="4" nolabel="1">
@ -791,14 +798,11 @@
<group string="Material Interfaces" >
<group>
<field name="iface_self_checkout" />
<field name="iface_websql" />
<field name="iface_led" />
<field name="iface_cashdrawer" />
<field name="iface_payment_terminal" />
</group>
<group>
<field name="iface_electronic_scale" />
<field name="iface_barscan" />
<field name="iface_vkeyboard" />
<field name="iface_print_via_proxy" />
</group>
@ -877,7 +881,7 @@
class="oe_highlight"/>
<button name="close" type="workflow" string="Validate Closing &amp; Post Entries" states="closing_control"
class="oe_highlight"/>
<button name="open_frontend_cb" type="object" string="Start Selling" states="opened"/>
<button name="open_frontend_cb" type="object" string="Continue Selling" states="opened"/>
<div class="oe_right">
<field name="state" widget="statusbar" statusbar_visible="opening_control,opened,closing_control,closed" nolabel="1"/>
</div>
@ -888,7 +892,7 @@
<button name="%(action_pos_box_out)d" string="Take Money Out" type="action" states="opened,closing_control"/>
</div>
<h1 class="oe_title">
Point of Sale Session:
Session:
<field name="name" attrs="{'invisible': [('name','=','/')]}" class="oe_inline"/>
</h1>
<field name="has_opening_control" invisible="1" />
@ -1057,5 +1061,20 @@
parent="menu_point_of_sale"
id="menu_pos_session_opening" sequence="0"/>
<record model="ir.ui.view" id="pos_ean13_generator">
<field name="name">pos.ean_wizard</field>
<field name="model">pos.ean_wizard</field>
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Ean13 Generator" version="7.0">
<field name="ean13_pattern"/>
<footer>
<button name="sanitize_ean13" type="object" string="Apply"/>
</footer>
</form>
</field>
</record>
</data>
</openerp>

View File

@ -37,14 +37,14 @@ class pos_sales_user(report_sxw.rml_parse):
dt1 = form['date_start'] + ' 00:00:00'
dt2 = form['date_end'] + ' 23:59:59'
data={}
self.cr.execute("select po.name as pos,po.date_order,ru.name as user,po.state,rc.name " \
"from pos_order as po,res_users as ru,res_company as rc " \
self.cr.execute("select po.name as pos,po.date_order,rp.name as user,po.state,rc.name " \
"from pos_order as po,res_users as ru,res_company as rc, res_partner as rp " \
"where po.date_order >= %s and po.date_order <= %s " \
"and po.company_id=rc.id and po.user_id=ru.id and po.user_id IN %s " \
"and po.company_id=rc.id and po.user_id=ru.id and po.user_id IN %s and ru.partner_id = rp.id" \
,(dt1,dt2,tuple(form['user_id'])))
return self.cr.dictfetchall()
report_sxw.report_sxw('report.pos.sales.user', 'pos.order', 'addons/point_of_sale/report/pos_sales_user.rml', parser=pos_sales_user,header='internal')
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -1,38 +1,8 @@
#!/usr/bin/env python
from osv import osv, fields
import math
import openerp.addons.product.product
def is_pair(x):
return not x%2
# This code is a duplicate of product#check_ean function
def check_ean(eancode):
if not eancode:
return True
if len(eancode) <> 13:
return False
try:
int(eancode)
except:
return False
oddsum=0
evensum=0
total=0
eanvalue=eancode
reversevalue = eanvalue[::-1]
finalean=reversevalue[1:]
for i in range(len(finalean)):
if is_pair(i):
oddsum += int(finalean[i])
else:
evensum += int(finalean[i])
total=(oddsum * 3) + evensum
check = int(10 - math.ceil(total % 10.0)) %10
if check != int(eancode[-1]):
return False
return True
class res_users(osv.osv):
_inherit = 'res.users'
@ -43,10 +13,22 @@ class res_users(osv.osv):
def _check_ean(self, cr, uid, ids, context=None):
return all(
check_ean(user.ean13) == True
openerp.addons.product.product.check_ean(user.ean13) == True
for user in self.browse(cr, uid, ids, context=context)
)
def edit_ean(self, cr, uid, ids, context):
return {
'name': "Edit Ean",
'type': 'ir.actions.act_window',
'view_type': 'form',
'view_mode': 'form',
'res_model': 'pos.ean_wizard',
'target' : 'new',
'view_id': False,
'context':context,
}
_constraints = [
(_check_ean, "Error: Invalid ean code", ['ean13'],),
]

View File

@ -20,8 +20,11 @@
<field name="arch" type="xml">
<notebook position="inside">
<page string="Point Of Sale">
<field name="ean13" />
<field name="pos_config" />
<group>
<field name="pos_config" />
<field name="ean13" />
<button name="edit_ean" type="object" string="Edit" />
</group>
</page>
</notebook>
</field>

View File

@ -60,7 +60,8 @@ access_product_uom_manager,product.uom manager,product.model_product_uom,group_p
access_res_partner_manager,res.partner manager,base.model_res_partner,group_pos_manager,1,0,0,0
access_product_category_manager,product.category manager,product.model_product_category,group_pos_manager,1,1,1,1
access_product_pricelist_manager,product.pricelist manager,product.model_product_pricelist,group_pos_manager,1,0,0,0
access_product_category_pos_manager,pos.category manager,model_pos_category,group_pos_manager,1,1,1,"1"""
access_product_category_pos_user,pos.category user,model_pos_category,group_pos_user,1,0,0,"0"""
access_product_category_pos_manager,pos.category manager,model_pos_category,group_pos_manager,1,1,1,1
access_product_category_pos_user,pos.category user,model_pos_category,group_pos_user,1,0,0,0
access_pos_session_user,pos.session user,model_pos_session,group_pos_user,1,1,1,0
access_pos_config_user,pos.config user,model_pos_config,group_pos_user,1,1,1,0
access_ir_sequence_manager,ir.sequence manager,base.model_ir_sequence,group_pos_manager,1,1,1,1

1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
60 access_res_partner_manager res.partner manager base.model_res_partner group_pos_manager 1 0 0 0
61 access_product_category_manager product.category manager product.model_product_category group_pos_manager 1 1 1 1
62 access_product_pricelist_manager product.pricelist manager product.model_product_pricelist group_pos_manager 1 0 0 0
63 access_product_category_pos_manager pos.category manager model_pos_category group_pos_manager 1 1 1 1" 1
64 access_product_category_pos_user pos.category user model_pos_category group_pos_user 1 0 0 0" 0
65 access_pos_session_user pos.session user model_pos_session group_pos_user 1 1 1 0
66 access_pos_config_user pos.config user model_pos_config group_pos_user 1 1 1 0
67 access_ir_sequence_manager ir.sequence manager base.model_ir_sequence group_pos_manager 1 1 1 1

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -31,6 +31,7 @@
background: -ms-linear-gradient(top,rgba(0,0,0,0.09),rgba(0,0,0,0));
background: linear-gradient(top,rgba(0,0,0,0.09),rgba(0,0,0,0));
}
.point-of-sale .darker-shadow-top{
position: absolute;
top:0;
@ -42,6 +43,7 @@
background: -ms-linear-gradient(top,rgba(0,0,0,0.15),rgba(0,0,0,0));
background: linear-gradient(top,rgba(0,0,0,0.15),rgba(0,0,0,0));
}
/* ********* The black loading screen ********* */
.point-of-sale .loader{
@ -156,11 +158,11 @@
}
.point-of-sale #rightheader button {
color: black;
height:29px;
margin:2px;
color: #273072;
height:27px;
margin:3px;
margin-right:0px;
border: 1px solid black;
border: 1px solid #353A7E;
background: #7f82ac;
background: -webkit-gradient(linear, left top, left bottom, from(#b2b3d7), to(#7f82ac));
background: -moz-linear-gradient(#b2b3d7, #7f82ac);
@ -187,6 +189,33 @@
font-weight: 900;
}
/* c) The session buttons */
.point-of-sale #rightheader .header-button{
float:right;
height:32px;
padding-left:10px;
padding-right:10px;
border-right: 1px solid #3a3a3a;
border-left: 1px solid #3a3a3a;
color:#DDD;
line-height:32px;
text-align:center;
cursor: pointer;
-webkit-transition-property: background;
-webkit-transition-duration: 0.2s;
-webkit-transition-timing-function: ease-out;
}
.point-of-sale #rightheader .header-button:last-child{
border-left: 1px solid #3a3a3a;
}
.point-of-sale #rightheader .header-button:hover{
background: rgba(0,0,0,0.2);
text-shadow: #000 0px 0px 3px;
color:#EEE;
}
/* c) The notifications indicator */
.point-of-sale .oe_pos_synch-notification{
@ -234,7 +263,8 @@
left:0;
width:440px;
top:0px;
bottom:105px;
/*bottom:105px;*/
bottom:0;
border-right: solid 1px #CECBCB;
background-color: white;
}
@ -318,7 +348,8 @@
.point-of-sale #rightpane {
position: absolute;
top: 0;
bottom: 105px;
/*bottom: 105px;*/
bottom:0;
left: 440px;
right: 0;
vertical-align: top;
@ -454,6 +485,42 @@
cursor: pointer;
}
.point-of-sale .category-simple-button{
position: relative;
display: inline-block;
font-size: 14px;
margin-right:10px;
padding:5px;
cursor: pointer;
border: 1px solid #cacaca;
border-radius: 4px;
background: #e2e2e2;
background: -webkit-linear-gradient(#f0f0f0, #e2e2e2);
background: -moz-linear-gradient(#f0f0f0, #e2e2e2);
background: -ms-linear-gradient(#f0f0f0, #e2e2e2);
background: linear-gradient(#f0f0f0, #e2e2e2);
-webkit-box-shadow: 0px 2px 2px rgba(0,0,0, 0.1);
-moz-box-shadow: 0px 2px 2px rgba(0,0,0, 0.1);
box-shadow: 0px 2px 2px rgba(0,0,0, 0.1);
}
.point-of-sale .category-simple-button:hover {
color: white;
background: #7f82ac;
border: 1px solid #7f82ac;
background: -webkit-linear-gradient(#9d9fc5, #7f82ac);
background: -moz-linear-gradient(#9d9fc5, #7f82ac);
background: -ms-linear-gradient(#9d9fc5, #7f82ac);
background: linear-gradient(#9d9fc5, #7f82ac);
-webkit-transition-property: background, border;
-webkit-transition-duration: 0.2s;
-webkit-transition-timing-function: ease-out;
}
.point-of-sale .category-button .category-img {
position: relative;
width: 120px;
@ -463,7 +530,8 @@
}
.point-of-sale .category-button .category-img img {
height: 100px;
max-height: 100px;
max-width: 120px;
}
.point-of-sale .category-button .category-name {
@ -490,6 +558,7 @@
position:relative;
vertical-align: top;
display: inline-block;
line-height: 100px;
font-size: 11px;
margin: 5px;
width: 120px;
@ -497,9 +566,9 @@
background:#fff;
border: 1px solid #fff;
border-radius: 3px;
-webkit-box-shadow: 0px 1px 8px rgba(0,0,0,0.2);
-moz-box-shadow: 0px 1px 8px rgba(0,0,0,0.2);
box-shadow: 0px 1px 8px rgba(0,0,0,0.2);
-webkit-box-shadow: 0px 1px 8px rgba(127,130,172,0.4);
-moz-box-shadow: 0px 1px 8px rgba(127,130,172,0.4);
box-shadow: 0px 1px 8px rgba(127,130,172,0.4);
}
.point-of-sale .product .product-img {
@ -511,7 +580,8 @@
}
.point-of-sale .product .product-img img {
height: 100px;
max-height: 100px;
max-width: 120px;
}
.point-of-sale .product .price-tag {
@ -520,6 +590,7 @@
right: 2px;
vertical-align: top;
color: white;
line-height: 14px;
background: #7f82ac;
padding: 2px 5px;
border-radius: 3px;
@ -533,6 +604,7 @@
box-sizing: border-box;
bottom:0;
top:auto;
line-height: 14px;
width:100%;
background: -webkit-linear-gradient(-90deg,rgba(255,255,255,0),rgba(255,255,255,1), rgba(255,255,255,1));
background: -moz-linear-gradient(-90deg,rgba(255,255,255,0),rgba(255,255,255,1), rgba(255,255,255,1));
@ -598,8 +670,16 @@
display: inline-block;
font-size: 1.5em;
}
.point-of-sale .greyed-out{
color: #AAA;
}
.point-of-sale .pos-step-container input{
font-size: 1em;
}
.point-of-sale .pos-payment-container {
text-align: left;
min-width: 500px;
}
.point-of-sale .pos-payment-container .left-block{
display: inline-block;
@ -648,8 +728,17 @@
width: 300px;
background-color: white;
margin: 20px;
padding: 10px;
padding: 15px;
padding-bottom:30px;
display: inline-block;
font-family: "Inconsolata";
-webkit-box-shadow: 0px 5px 16px rgba(0,0,0, 0.3);
-moz-box-shadow: 0px 5px 16px rgba(0,0,0, 0.3);
box-shadow: 0px 5px 16px rgba(0,0,0, 0.3);
}
.point-of-sale .pos-sale-ticket .emph{
font-size: 20px;
margin:5px;
}
.point-of-sale .pos-sale-ticket table {
width: 100%;
@ -660,18 +749,18 @@
}
@media print {
#oe_header, #oe_menu, .point-of-sale #topheader, .point-of-sale #leftpane {
display: none;
.point-of-sale #topheader, .point-of-sale #leftpane {
display: none !important;
}
.point-of-sale #content {
top: 0px;
top: 0px !important;
}
.point-of-sale #rightpane {
left: 0px;
left: 0px !important;
background-color: white;
}
#receipt-screen header {
display: none;
display: none !important;
}
#receipt-screen {
text-align: left;
@ -761,6 +850,28 @@
font-family: "Inconsolata";
}
/* e) The Welcome Screen */
.point-of-sale .goodbye-message{
position: absolute;
left:50%;
top:30%;
width:500px;
height:400px;
margin-left: -250px;
margin-top: -200px;
padding:10px;
padding-top:20px;
text-align:center;
font-size:20px;
font-weight:bold;
background-color: #F0EEEE;
border: 1px solid #E0DDDD;
-webkit-box-shadow: 0px 10px 20px rgba(0,0,0, 0.3);
-moz-box-shadow: 0px 10px 20px rgba(0,0,0, 0.3);
-ms-box-shadow: 0px 10px 20px rgba(0,0,0, 0.3);
z-index:1150;
}
/* ********* The OrderWidget ********* */
.point-of-sale .order-container{
@ -846,6 +957,10 @@
-moz-transition: background 50ms ease-in-out;
transition: background 50ms ease-in-out;
}
.point-of-sale .order .orderline.empty:hover{
background: transparent;
cursor: default;
}
.point-of-sale .order .orderline.selected{
background: rgba(140,143,183,0.2);
@ -933,9 +1048,9 @@
background: -moz-linear-gradient(#f0f0f0, #e2e2e2);
background: -ms-linear-gradient(#f0f0f0, #e2e2e2);
background: linear-gradient(#f0f0f0, #e2e2e2);
-webkit-box-shadow: 0px 2px 2px rgba(0,0,0, 0.3);
-moz-box-shadow: 0px 2px 2px rgba(0,0,0, 0.3);
box-shadow: 0px 2px 2px rgba(0,0,0, 0.3);
-webkit-box-shadow: 0px 2px 2px rgba(0,0,0, 0.1);
-moz-box-shadow: 0px 2px 2px rgba(0,0,0, 0.1);
box-shadow: 0px 2px 2px rgba(0,0,0, 0.1);
}
.point-of-sale .pos-actionbar .button .label{
margin-top: 37px;
@ -957,6 +1072,23 @@
-webkit-transition-timing-function: ease-out;
}
.point-of-sale .pos-actionbar .button.disabled{
color:#AAA;
}
.point-of-sale .pos-actionbar .button.disabled:hover{
border: 1px solid #cacaca;
border-radius: 4px;
background: #e2e2e2;
background: -webkit-linear-gradient(#f0f0f0, #e2e2e2);
background: -moz-linear-gradient(#f0f0f0, #e2e2e2);
background: -ms-linear-gradient(#f0f0f0, #e2e2e2);
background: linear-gradient(#f0f0f0, #e2e2e2);
-webkit-box-shadow: 0px 2px 2px rgba(0,0,0, 0.1);
-moz-box-shadow: 0px 2px 2px rgba(0,0,0, 0.1);
box-shadow: 0px 2px 2px rgba(0,0,0, 0.1);
}
.point-of-sale .pos-actionbar .button.rightalign{
float:right;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 46 KiB

View File

@ -0,0 +1,984 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="48"
height="48"
overflow="visible"
enable-background="new 0 0 128 129.396"
xml:space="preserve"
id="svg2"
sodipodi:version="0.32"
inkscape:version="0.48.3.1 r9886"
sodipodi:docname="scale.svg"
version="1.0"
inkscape:export-filename="/home/fva/Code/openerp/src/addons/trunk-pos-fva/point_of_sale/static/src/img/scale.png"
inkscape:export-xdpi="600"
inkscape:export-ydpi="600"
inkscape:output_extension="org.inkscape.output.svg.inkscape"><metadata
id="metadata367"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><cc:license
rdf:resource="http://creativecommons.org/licenses/publicdomain/" /><dc:title></dc:title><dc:creator><cc:Agent><dc:title>Jakub Steiner</dc:title></cc:Agent></dc:creator><dc:source>http://jimmac.musichall.cz</dc:source><dc:subject><rdf:Bag><rdf:li>home</rdf:li><rdf:li>return</rdf:li><rdf:li>go</rdf:li><rdf:li>default</rdf:li><rdf:li>user</rdf:li><rdf:li>directory</rdf:li></rdf:Bag></dc:subject><dc:contributor><cc:Agent><dc:title>Tuomas Kuosmanen</dc:title></cc:Agent></dc:contributor></cc:Work><cc:License
rdf:about="http://creativecommons.org/licenses/publicdomain/"><cc:permits
rdf:resource="http://creativecommons.org/ns#Reproduction" /><cc:permits
rdf:resource="http://creativecommons.org/ns#Distribution" /><cc:permits
rdf:resource="http://creativecommons.org/ns#DerivativeWorks" /></cc:License></rdf:RDF></metadata><defs
id="defs365"><linearGradient
inkscape:collect="always"
id="linearGradient4977"><stop
style="stop-color:#ff0000;stop-opacity:1;"
offset="0"
id="stop4979" /><stop
style="stop-color:#ff0000;stop-opacity:0"
offset="1"
id="stop4981" /></linearGradient><inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="0 : 24 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_z="48 : 24 : 1"
inkscape:persp3d-origin="24 : 16 : 1"
id="perspective92" /><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient5060"
id="radialGradient5031"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(-2.774389,0,0,1.969706,112.7623,-872.8854)"
cx="605.71429"
cy="486.64789"
fx="605.71429"
fy="486.64789"
r="117.14286" /><linearGradient
inkscape:collect="always"
id="linearGradient5060"><stop
style="stop-color:black;stop-opacity:1;"
offset="0"
id="stop5062" /><stop
style="stop-color:black;stop-opacity:0;"
offset="1"
id="stop5064" /></linearGradient><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient5060"
id="radialGradient5029"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(2.774389,0,0,1.969706,-1891.633,-872.8854)"
cx="605.71429"
cy="486.64789"
fx="605.71429"
fy="486.64789"
r="117.14286" /><linearGradient
id="linearGradient5048"><stop
style="stop-color:black;stop-opacity:0;"
offset="0"
id="stop5050" /><stop
id="stop5056"
offset="0.5"
style="stop-color:black;stop-opacity:1;" /><stop
style="stop-color:black;stop-opacity:0;"
offset="1"
id="stop5052" /></linearGradient><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient5048"
id="linearGradient5027"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(2.774389,0,0,1.969706,-1892.179,-872.8854)"
x1="302.85715"
y1="366.64789"
x2="302.85715"
y2="609.50507" /><linearGradient
id="linearGradient2406"><stop
style="stop-color:#7c7e79;stop-opacity:1;"
offset="0"
id="stop2408" /><stop
id="stop2414"
offset="0.1724138"
style="stop-color:#848681;stop-opacity:1;" /><stop
style="stop-color:#898c86;stop-opacity:1;"
offset="1"
id="stop2410" /></linearGradient><linearGradient
inkscape:collect="always"
id="linearGradient2390"><stop
style="stop-color:#919191;stop-opacity:1;"
offset="0"
id="stop2392" /><stop
style="stop-color:#919191;stop-opacity:0;"
offset="1"
id="stop2394" /></linearGradient><linearGradient
inkscape:collect="always"
id="linearGradient2378"><stop
style="stop-color:#575757;stop-opacity:1;"
offset="0"
id="stop2380" /><stop
style="stop-color:#575757;stop-opacity:0;"
offset="1"
id="stop2382" /></linearGradient><linearGradient
inkscape:collect="always"
id="linearGradient2368"><stop
style="stop-color:#ffffff;stop-opacity:1;"
offset="0"
id="stop2370" /><stop
style="stop-color:#ffffff;stop-opacity:0;"
offset="1"
id="stop2372" /></linearGradient><linearGradient
inkscape:collect="always"
id="linearGradient2349"><stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop2351" /><stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop2353" /></linearGradient><linearGradient
id="linearGradient2341"><stop
id="stop2343"
offset="0"
style="stop-color:#000000;stop-opacity:1;" /><stop
id="stop2345"
offset="1"
style="stop-color:#000000;stop-opacity:0;" /></linearGradient><linearGradient
id="linearGradient2329"><stop
style="stop-color:#000000;stop-opacity:0.18556701;"
offset="0"
id="stop2331" /><stop
style="stop-color:#ffffff;stop-opacity:1;"
offset="1"
id="stop2333" /></linearGradient><linearGradient
inkscape:collect="always"
id="linearGradient2319"><stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop2321" /><stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop2323" /></linearGradient><linearGradient
id="linearGradient2307"><stop
style="stop-color:#edd400;stop-opacity:1;"
offset="0"
id="stop2309" /><stop
style="stop-color:#998800;stop-opacity:1;"
offset="1"
id="stop2311" /></linearGradient><linearGradient
inkscape:collect="always"
id="linearGradient2299"><stop
style="stop-color:#ffffff;stop-opacity:1;"
offset="0"
id="stop2301" /><stop
style="stop-color:#ffffff;stop-opacity:0;"
offset="1"
id="stop2303" /></linearGradient><linearGradient
id="XMLID_2_"
gradientUnits="userSpaceOnUse"
x1="80.223602"
y1="117.5205"
x2="48.046001"
y2="59.7995"
gradientTransform="matrix(0.314683,0.000000,0.000000,0.314683,4.128264,3.742874)">
<stop
offset="0"
style="stop-color:#CCCCCC"
id="stop17" />
<stop
offset="0.9831"
style="stop-color:#FFFFFF"
id="stop19" />
<midPointStop
offset="0"
style="stop-color:#CCCCCC"
id="midPointStop48" />
<midPointStop
offset="0.5"
style="stop-color:#CCCCCC"
id="midPointStop50" />
<midPointStop
offset="0.9831"
style="stop-color:#FFFFFF"
id="midPointStop52" />
</linearGradient><linearGradient
inkscape:collect="always"
xlink:href="#XMLID_2_"
id="linearGradient1514"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.336922,0,0,0.166888,143.49433,15.46151)"
x1="52.006104"
y1="166.1331"
x2="14.049017"
y2="-42.218513" /><linearGradient
id="XMLID_39_"
gradientUnits="userSpaceOnUse"
x1="64.387703"
y1="65.124001"
x2="64.387703"
y2="35.569"
gradientTransform="matrix(0.354101,0,0,0.354101,127.15013,-0.08364921)">
<stop
offset="0"
style="stop-color:#FFFFFF"
id="stop336" />
<stop
offset="0.8539"
style="stop-color:#FF6200"
id="stop338" />
<stop
offset="1"
style="stop-color:#F25D00"
id="stop340" />
<midPointStop
offset="0"
style="stop-color:#FFFFFF"
id="midPointStop335" />
<midPointStop
offset="0.5"
style="stop-color:#FFFFFF"
id="midPointStop337" />
<midPointStop
offset="0.8539"
style="stop-color:#FF6200"
id="midPointStop339" />
<midPointStop
offset="0.5"
style="stop-color:#FF6200"
id="midPointStop341" />
<midPointStop
offset="1"
style="stop-color:#F25D00"
id="midPointStop343" />
</linearGradient><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient2299"
id="radialGradient2305"
cx="7.5326638"
cy="24.202574"
fx="7.5326638"
fy="24.202574"
r="8.2452128"
gradientTransform="matrix(4.100086,0,0,4.201322,100.09639,-78.53967)"
gradientUnits="userSpaceOnUse" /><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient2307"
id="radialGradient2313"
cx="19.985598"
cy="36.77816"
fx="19.985598"
fy="36.77816"
r="1.0821035"
gradientTransform="matrix(1.125263,0,0,0.982744,122.08278,0.565787)"
gradientUnits="userSpaceOnUse" /><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient2319"
id="radialGradient2325"
cx="20.443665"
cy="37.425829"
fx="20.443665"
fy="37.425829"
r="1.0821035"
gradientTransform="matrix(1.125263,0,0,0.982744,122.08278,0.731106)"
gradientUnits="userSpaceOnUse" /><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient2329"
id="linearGradient2335"
x1="17.602522"
y1="26.057423"
x2="17.682528"
y2="32.654099"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.898789,0,0,1.071914,125.98948,-2.080838)" /><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient2341"
id="radialGradient2339"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(4.100086,0,0,-4.201322,120.31334,105.3535)"
cx="11.68129"
cy="19.554111"
fx="11.68129"
fy="19.554111"
r="8.2452126" /><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient2349"
id="radialGradient2355"
cx="24.023088"
cy="40.56913"
fx="24.023088"
fy="40.56913"
r="16.28684"
gradientTransform="matrix(1.000000,0.000000,0.000000,0.431250,1.157278e-15,23.07369)"
gradientUnits="userSpaceOnUse" /><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient2368"
id="radialGradient2374"
cx="29.913452"
cy="30.442923"
fx="29.913452"
fy="30.442923"
r="4.0018832"
gradientTransform="matrix(3.751495,0,0,3.147818,43.502383,-65.70704)"
gradientUnits="userSpaceOnUse" /><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient2378"
id="radialGradient2384"
cx="24.195112"
cy="10.577631"
fx="24.195112"
fy="10.577631"
r="15.242914"
gradientTransform="matrix(1.125263,-3.585417e-8,4.269819e-8,1.340059,122.50475,1.355395)"
gradientUnits="userSpaceOnUse" /><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient2390"
id="linearGradient2396"
x1="30.603519"
y1="37.337803"
x2="30.603519"
y2="36.112415"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1.263867,0,0,0.859794,-6.499556,8.390924)" /><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient2406"
id="linearGradient2412"
x1="17.850183"
y1="28.939463"
x2="19.040216"
y2="41.03223"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.888785,0,0,1.08932,127.92244,-1.524336)" /><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient5048"
id="linearGradient4689"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(2.774389,0,0,1.969706,-1892.179,-872.8854)"
x1="302.85715"
y1="366.64789"
x2="302.85715"
y2="609.50507" /><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient5060"
id="radialGradient4691"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(2.774389,0,0,1.969706,-1891.633,-872.8854)"
cx="605.71429"
cy="486.64789"
fx="605.71429"
fy="486.64789"
r="117.14286" /><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient5060"
id="radialGradient4693"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(-2.774389,0,0,1.969706,112.7623,-872.8854)"
cx="605.71429"
cy="486.64789"
fx="605.71429"
fy="486.64789"
r="117.14286" /><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient5048"
id="linearGradient4894"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(2.774389,0,0,1.969706,-1892.179,-872.8854)"
x1="302.85715"
y1="366.64789"
x2="302.85715"
y2="609.50507" /><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient5060"
id="radialGradient4896"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(2.774389,0,0,1.969706,-1891.633,-872.8854)"
cx="605.71429"
cy="486.64789"
fx="605.71429"
fy="486.64789"
r="117.14286" /><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient5060"
id="radialGradient4898"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(-2.774389,0,0,1.969706,112.7623,-872.8854)"
cx="605.71429"
cy="486.64789"
fx="605.71429"
fy="486.64789"
r="117.14286" /><filter
inkscape:collect="always"
id="filter4971"
x="-0.064853556"
width="1.1297071"
y="-1.9456067"
height="4.8912134"><feGaussianBlur
inkscape:collect="always"
stdDeviation="0.81066946"
id="feGaussianBlur4973" /></filter><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4977"
id="linearGradient4983"
x1="23.6875"
y1="32"
x2="23.75"
y2="45.25"
gradientUnits="userSpaceOnUse" /></defs><sodipodi:namedview
inkscape:cy="-2.746436"
inkscape:cx="31.130127"
inkscape:zoom="8"
inkscape:window-height="1176"
inkscape:window-width="1855"
inkscape:pageshadow="2"
inkscape:pageopacity="0.0"
borderopacity="0.21568627"
bordercolor="#666666"
pagecolor="#ffffff"
id="base"
inkscape:showpageshadow="false"
inkscape:window-x="65"
inkscape:window-y="24"
inkscape:current-layer="svg2"
fill="#555753"
showgrid="true"
stroke="#a40000"
showguides="true"
inkscape:guide-bbox="true"
inkscape:snap-global="false"
inkscape:window-maximized="1"><inkscape:grid
type="xygrid"
id="grid3818" /></sodipodi:namedview>
<g
style="display:inline"
id="g5022"
transform="matrix(0.02158196,0,0,0.01859457,168.63396,41.63767)"><rect
y="-150.69685"
x="-1559.2523"
height="478.35718"
width="1339.6335"
id="rect4173"
style="opacity:0.40206185;color:#000000;fill:url(#linearGradient5027);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible" /><path
sodipodi:nodetypes="cccc"
id="path5058"
d="m -219.61876,-150.68038 c 0,0 0,478.33079 0,478.33079 142.874166,0.90045 345.40022,-107.16966 345.40014,-239.196175 0,-132.026537 -159.436816,-239.134595 -345.40014,-239.134615 z"
style="opacity:0.40206185;color:#000000;fill:url(#radialGradient5029);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible"
inkscape:connector-curvature="0" /><path
style="opacity:0.40206185;color:#000000;fill:url(#radialGradient5031);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible"
d="m -1559.2523,-150.68038 c 0,0 0,478.33079 0,478.33079 -142.8742,0.90045 -345.4002,-107.16966 -345.4002,-239.196175 0,-132.026537 159.4368,-239.134595 345.4002,-239.134615 z"
id="path5018"
sodipodi:nodetypes="cccc"
inkscape:connector-curvature="0" /></g><path
style="color:#000000;fill:url(#linearGradient1514);fill-opacity:1;fill-rule:nonzero;stroke:#757575;stroke-width:1.0000006;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
d="m 147.13103,8.1833733 5.95746,0 c 0.83973,0 13.88647,15.4353277 13.88647,16.3406587 l -0.44352,18.496745 c 0,0.905333 -0.67603,1.634177 -1.51576,1.634177 l -31.45728,0 c -0.83973,0 -1.51576,-0.728844 -1.51576,-1.634177 l 0.0565,-18.496745 c 0,-0.905331 14.19218,-16.3406587 15.03191,-16.3406587 z"
id="rect1512"
sodipodi:nodetypes="ccccccccc"
inkscape:connector-curvature="0" /><path
style="fill:none"
id="path5"
d="m 172.47503,45.735573 -45.3249,0 0,-45.32489746 45.3249,0 0,45.32489746 z"
inkscape:connector-curvature="0" /><path
style="fill:url(#linearGradient2335);fill-opacity:1;fill-rule:evenodd"
id="path2327"
d="m 148.51145,29 -0.0457,15.090942 -11.84279,0 L 136.51145,29 l 12,0 z"
clip-rule="evenodd"
sodipodi:nodetypes="ccccc"
inkscape:connector-curvature="0" /><path
sodipodi:nodetypes="ccccccccc"
id="path2357"
d="m 147.29191,9.405584 5.5591,0 c 0.78358,0 13.00087,14.399588 13.00087,15.244172 l -0.34716,18.212311 c 0,0.459259 -0.14374,0.653465 -0.51237,0.653465 l -31.38721,0.01428 c -0.36863,0 -0.58396,-0.07992 -0.58396,-0.45355 l 0.21534,-18.426506 c 0,-0.844584 13.27181,-15.244172 14.05539,-15.244172 z"
style="opacity:0.3125;color:#000000;fill:none;stroke:#ffffff;stroke-width:1.00000012;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
inkscape:connector-curvature="0" /><path
clip-rule="evenodd"
d="m 132.71898,27.943053 -0.0543,2.595194 18.36817,-13.179254 15.28639,11.154428 0.0713,-0.311714 -16.37045,-15.904131 -17.30116,15.645477 z"
id="path23"
style="opacity:0.2;fill:url(#radialGradient2384);fill-opacity:1;fill-rule:evenodd"
sodipodi:nodetypes="ccccccc"
inkscape:connector-curvature="0" /><path
clip-rule="evenodd"
d="m 147.51145,30 0,14.090942 -9.81103,0 L 137.51145,30 l 10,0 z"
id="path188"
style="fill:url(#linearGradient2412);fill-opacity:1;fill-rule:evenodd"
sodipodi:nodetypes="ccccc"
inkscape:connector-curvature="0" /><path
style="opacity:0.40909089;fill:url(#radialGradient2325);fill-opacity:1;fill-rule:evenodd"
id="path2315"
d="m 145.08831,36.44767 c 0.67279,0 1.21662,0.474605 1.21662,1.058507 0,0.589811 -0.54383,1.068355 -1.21662,1.068355 -0.67227,0 -1.21869,-0.478544 -1.21869,-1.068355 5.2e-4,-0.583902 0.54642,-1.058507 1.21869,-1.058507 z"
clip-rule="evenodd"
inkscape:connector-curvature="0" /><path
clip-rule="evenodd"
d="m 144.97377,35.932229 c 0.67279,0 1.21661,0.474605 1.21661,1.058507 0,0.589809 -0.54382,1.068353 -1.21661,1.068353 -0.67228,0 -1.21869,-0.478544 -1.21869,-1.068353 5.2e-4,-0.583902 0.54641,-1.058507 1.21869,-1.058507 z"
id="path217"
style="fill:url(#radialGradient2313);fill-opacity:1;fill-rule:evenodd"
inkscape:connector-curvature="0" /><path
d="m 149.9592,11.559337 18.92706,17.169868 0.49468,0.391991 0.40368,-0.171385 -0.37287,-0.761673 -0.27762,-0.223436 -19.17493,-15.572306 -19.38951,15.743335 -0.23761,0.14412 -0.21671,0.706786 0.43342,0.129248 0.38456,-0.308423 19.02585,-17.248125 z"
id="path342"
style="fill:url(#XMLID_39_)"
sodipodi:nodetypes="ccccccccccccc"
inkscape:connector-curvature="0" /><path
style="fill:#ef2929;stroke:#a40000"
id="path362"
d="m 149.84162,2.2713382 -21.88174,18.1013368 -0.62473,7.165928 1.99994,2.064323 c 0,0 20.40738,-17.157285 20.62409,-17.327963 l 19.63254,17.54326 1.89843,-2.323997 -1.61579,-7.111374 -19.91518,-18.2159732 -0.11756,0.1044594 z"
sodipodi:nodetypes="cccccccccc"
inkscape:connector-curvature="0" />
<path
style="opacity:0.40909089;color:#000000;fill:url(#radialGradient2305);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible"
d="M 128.3528,20.613129 128.06124,27.236494 149.88067,8.980075 149.81034,3.0867443 128.3528,20.613129 z"
id="path1536"
sodipodi:nodetypes="ccccc"
inkscape:connector-curvature="0" /><path
sodipodi:nodetypes="ccccc"
id="path2337"
d="m 149.99522,8.7509884 0.0995,-5.8411017 19.32896,17.6519533 1.49181,6.500812 -20.92023,-18.3116636 z"
style="opacity:0.13636367;color:#000000;fill:url(#radialGradient2339);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible"
inkscape:connector-curvature="0" /><path
style="opacity:0.31818183;color:#000000;fill:none;stroke:#ffffff;stroke-width:0.99999934;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
d="m 152.61368,27.719824 9.04,0 c 0.77059,0 1.39096,0.62037 1.39096,1.390967 l -0.008,9.079221 c 0,0.770596 -0.59632,1.265969 -1.36691,1.265969 l -9.05609,0 c -0.77059,0 -1.39096,-0.620373 -1.39096,-1.390969 l 0,-8.954221 c 0,-0.770597 0.62037,-1.390967 1.39096,-1.390967 z"
id="rect2361"
sodipodi:nodetypes="ccccccccc"
inkscape:connector-curvature="0" /><rect
style="color:#000000;fill:#3465a4;fill-opacity:1;fill-rule:nonzero;stroke:#757575;stroke-width:0.9999994;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
id="rect3263"
width="10.001333"
height="9.9624557"
x="152.01923"
y="28.514256"
rx="0.38128215"
ry="0.38128215" /><path
style="opacity:0.39772728;color:#000000;fill:url(#radialGradient2374);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.99999958;marker:none;visibility:visible;display:inline;overflow:visible"
d="m 152.61857,34.408261 c 3.61798,0.331177 5.52773,-1.445704 8.86815,-1.55274 l 0.0247,-3.849491 -8.91161,-0.006 0.0187,5.408261 z"
id="rect2363"
sodipodi:nodetypes="ccccc"
inkscape:connector-curvature="0" /><g
transform="translate(65.937707,2.0108349)"
id="g4739"><path
sodipodi:type="arc"
style="fill:#6adb4d;fill-opacity:1;stroke:#34781d;stroke-width:1.05264175;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
id="path4741"
sodipodi:cx="25.367456"
sodipodi:cy="8.2694378"
sodipodi:rx="5.3033009"
sodipodi:ry="5.3033009"
d="m 30.670757,8.2694378 c 0,2.9289322 -2.374368,5.3033012 -5.303301,5.3033012 -2.928932,0 -5.3033,-2.374369 -5.3033,-5.3033012 0,-2.9289322 2.374368,-5.3033009 5.3033,-5.3033009 2.928933,0 5.303301,2.3743687 5.303301,5.3033009 z"
transform="matrix(0.95416667,0,0,0.94583333,1.405743,0.73519001)" /><path
transform="matrix(0.77083333,0,0,0.77916666,6.0564434,2.1576238)"
d="m 30.670757,8.2694378 c 0,2.9289322 -2.374368,5.3033012 -5.303301,5.3033012 -2.928932,0 -5.3033,-2.374369 -5.3033,-5.3033012 0,-2.9289322 2.374368,-5.3033009 5.3033,-5.3033009 2.928933,0 5.303301,2.3743687 5.303301,5.3033009 z"
sodipodi:ry="5.3033009"
sodipodi:rx="5.3033009"
sodipodi:cy="8.2694378"
sodipodi:cx="25.367456"
id="path4743"
style="fill:none;stroke:#b3ff9a;stroke-width:1.29034114;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
sodipodi:type="arc" /></g><g
id="g4733"
transform="translate(76.897863,5.0160386)"><path
transform="matrix(0.95416667,0,0,0.94583333,1.405743,0.73519001)"
d="m 30.670757,8.2694378 c 0,2.9289322 -2.374368,5.3033012 -5.303301,5.3033012 -2.928932,0 -5.3033,-2.374369 -5.3033,-5.3033012 0,-2.9289322 2.374368,-5.3033009 5.3033,-5.3033009 2.928933,0 5.303301,2.3743687 5.303301,5.3033009 z"
sodipodi:ry="5.3033009"
sodipodi:rx="5.3033009"
sodipodi:cy="8.2694378"
sodipodi:cx="25.367456"
id="path4735"
style="fill:#6adb4d;fill-opacity:1;stroke:#34781d;stroke-width:1.05264175;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
sodipodi:type="arc" /><path
sodipodi:type="arc"
style="fill:none;stroke:#b3ff9a;stroke-width:1.29034114;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
id="path4737"
sodipodi:cx="25.367456"
sodipodi:cy="8.2694378"
sodipodi:rx="5.3033009"
sodipodi:ry="5.3033009"
d="m 30.670757,8.2694378 c 0,2.9289322 -2.374368,5.3033012 -5.303301,5.3033012 -2.928932,0 -5.3033,-2.374369 -5.3033,-5.3033012 0,-2.9289322 2.374368,-5.3033009 5.3033,-5.3033009 2.928933,0 5.303301,2.3743687 5.303301,5.3033009 z"
transform="matrix(0.77083333,0,0,0.77916666,6.0564434,2.1576238)" /></g><g
transform="translate(70.887455,7.0268736)"
id="g4727"><path
sodipodi:type="arc"
style="fill:#6adb4d;fill-opacity:1;stroke:#34781d;stroke-width:1.05264175;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
id="path4729"
sodipodi:cx="25.367456"
sodipodi:cy="8.2694378"
sodipodi:rx="5.3033009"
sodipodi:ry="5.3033009"
d="m 30.670757,8.2694378 c 0,2.9289322 -2.374368,5.3033012 -5.303301,5.3033012 -2.928932,0 -5.3033,-2.374369 -5.3033,-5.3033012 0,-2.9289322 2.374368,-5.3033009 5.3033,-5.3033009 2.928933,0 5.303301,2.3743687 5.303301,5.3033009 z"
transform="matrix(0.95416667,0,0,0.94583333,1.405743,0.73519001)" /><path
transform="matrix(0.77083333,0,0,0.77916666,6.0564434,2.1576238)"
d="m 30.670757,8.2694378 c 0,2.9289322 -2.374368,5.3033012 -5.303301,5.3033012 -2.928932,0 -5.3033,-2.374369 -5.3033,-5.3033012 0,-2.9289322 2.374368,-5.3033009 5.3033,-5.3033009 2.928933,0 5.303301,2.3743687 5.303301,5.3033009 z"
sodipodi:ry="5.3033009"
sodipodi:rx="5.3033009"
sodipodi:cy="8.2694378"
sodipodi:cx="25.367456"
id="path4731"
style="fill:none;stroke:#b3ff9a;stroke-width:1.29034114;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
sodipodi:type="arc" /></g><g
id="g4723"
transform="translate(59.662134,5.9662134)"><path
transform="matrix(0.95416667,0,0,0.94583333,1.405743,0.73519001)"
d="m 30.670757,8.2694378 c 0,2.9289322 -2.374368,5.3033012 -5.303301,5.3033012 -2.928932,0 -5.3033,-2.374369 -5.3033,-5.3033012 0,-2.9289322 2.374368,-5.3033009 5.3033,-5.3033009 2.928933,0 5.303301,2.3743687 5.303301,5.3033009 z"
sodipodi:ry="5.3033009"
sodipodi:rx="5.3033009"
sodipodi:cy="8.2694378"
sodipodi:cx="25.367456"
id="path4719"
style="fill:#6adb4d;fill-opacity:1;stroke:#34781d;stroke-width:1.05264175;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
sodipodi:type="arc" /><path
sodipodi:type="arc"
style="fill:none;stroke:#b3ff9a;stroke-width:1.29034114;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
id="path4721"
sodipodi:cx="25.367456"
sodipodi:cy="8.2694378"
sodipodi:rx="5.3033009"
sodipodi:ry="5.3033009"
d="m 30.670757,8.2694378 c 0,2.9289322 -2.374368,5.3033012 -5.303301,5.3033012 -2.928932,0 -5.3033,-2.374369 -5.3033,-5.3033012 0,-2.9289322 2.374368,-5.3033009 5.3033,-5.3033009 2.928933,0 5.303301,2.3743687 5.303301,5.3033009 z"
transform="matrix(0.77083333,0,0,0.77916666,6.0564434,2.1576238)" /></g><g
id="g4695"
transform="matrix(1.1326331,0,0,0.44340824,65.580189,2.4215088)"><rect
style="fill:#8a8a8a;fill-opacity:1;stroke:none"
id="rect4697"
width="29.256544"
height="18.119614"
x="10.297243"
y="27.449707"
rx="1.7190553"
ry="4.3911204" /><g
id="g4699"><g
id="g4701"><rect
rx="1.7051755"
y="27.517153"
x="10.510214"
height="17.982044"
width="29.020325"
id="rect4703"
style="fill:none;stroke:#646464;stroke-width:1.41005611;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
ry="4.3556657" /></g><rect
rx="0.88575488"
y="29.624025"
x="11.337327"
height="13.83156"
width="27.319036"
id="rect4705"
style="fill:none;stroke:#b3b3b3;stroke-width:1.40048993;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
ry="2.2625546" /></g></g><g
transform="matrix(0.01936185,0,0,0.01859457,111.41659,40.621203)"
id="g4681"
style="display:inline"><rect
style="opacity:0.40206185;color:#000000;fill:url(#linearGradient4689);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible"
id="rect4683"
width="1339.6335"
height="478.35718"
x="-1559.2523"
y="-150.69685" /><path
inkscape:connector-curvature="0"
style="opacity:0.40206185;color:#000000;fill:url(#radialGradient4691);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible"
d="m -219.61876,-150.68038 c 0,0 0,478.33079 0,478.33079 142.874166,0.90045 345.40022,-107.16966 345.40014,-239.196175 0,-132.026537 -159.436816,-239.134595 -345.40014,-239.134615 z"
id="path4685"
sodipodi:nodetypes="cccc" /><path
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccc"
id="path4687"
d="m -1559.2523,-150.68038 c 0,0 0,478.33079 0,478.33079 -142.8742,0.90045 -345.4002,-107.16966 -345.4002,-239.196175 0,-132.026537 159.4368,-239.134595 345.4002,-239.134615 z"
style="opacity:0.40206185;color:#000000;fill:url(#radialGradient4693);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible" /></g><g
id="g4672"
transform="translate(68.942911,-2.9168155)"><rect
ry="1.9470588"
rx="1.9470588"
y="27.449707"
x="10.297243"
height="18.119614"
width="29.256544"
id="rect4658"
style="fill:#dddddd;fill-opacity:1;stroke:none" /><g
id="g4667"><g
id="g4664"><rect
ry="1.9313381"
style="fill:none;stroke:#646464;stroke-width:0.99927008;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
id="rect4660"
width="29.020325"
height="17.982044"
x="10.510214"
y="27.517153"
rx="1.9313381" /></g><rect
ry="1.0032353"
style="fill:none;stroke:#f6f6f6;stroke-width:0.99249077;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
id="rect4662"
width="26.967867"
height="16.005489"
x="11.532422"
y="28.489801"
rx="1.0032353" /></g></g><path
transform="matrix(1.0023678,0,0,0.95596394,68.883716,-0.0273148)"
d="m 35,33 a 10,10 0 1 1 -20,0 10,10 0 1 1 20,0 z"
sodipodi:ry="10"
sodipodi:rx="10"
sodipodi:cy="33"
sodipodi:cx="25"
id="path4679"
style="fill:#b8b8b8;fill-opacity:1;fill-rule:evenodd;stroke:none"
sodipodi:type="arc" /><path
sodipodi:type="arc"
style="fill:#b8b8b8;fill-opacity:1;fill-rule:evenodd;stroke:#646464;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
id="path3820"
sodipodi:cx="25"
sodipodi:cy="33"
sodipodi:rx="10"
sodipodi:ry="10"
d="m 35,33 a 10,10 0 1 1 -20,0 10,10 0 1 1 20,0 z"
transform="matrix(0.94933484,0,0,0.95596394,70.209541,-1.4857225)" /><path
transform="matrix(0.6928006,0,0,0.69727163,76.6008,8.0344445)"
d="m 35,33 a 10,10 0 1 1 -20,0 10,10 0 1 1 20,0 z"
sodipodi:ry="10"
sodipodi:rx="10"
sodipodi:cy="33"
sodipodi:cx="25"
id="path4590"
style="fill:#f4f4f4;fill-opacity:1;fill-rule:evenodd;stroke:none"
sodipodi:type="arc" /><g
id="g4644"
transform="matrix(0.70710678,0.70710678,-0.70710678,0.70710678,99.594592,-10.924363)"><rect
style="fill:#232323;fill-opacity:1;stroke:none"
id="rect4646"
width="0.68500972"
height="1.1490486"
x="24.638252"
y="26.853086" /><rect
y="37.990021"
x="24.660349"
height="1.1490486"
width="0.68500972"
id="rect4648"
style="fill:#232323;fill-opacity:1;stroke:none" /></g><g
id="g4600"
style="fill:#a2a2a2;fill-opacity:1"
transform="translate(69.009202,-2.3643883)"><rect
style="fill:#a2a2a2;fill-opacity:1;stroke:none"
id="rect4602"
width="1"
height="5.9375"
x="40.742825"
y="-1.6325631"
transform="matrix(0.67898035,0.73415645,-0.73415645,0.67898035,0,0)" /><path
sodipodi:type="arc"
style="fill:#a2a2a2;fill-opacity:1;stroke:none"
id="path4604"
sodipodi:cx="25.03125"
sodipodi:cy="33.03125"
sodipodi:rx="1.03125"
sodipodi:ry="0.96875"
d="M 26.0625,33.03125 C 26.0625,33.566276 25.600794,34 25.03125,34 24.461706,34 24,33.566276 24,33.03125 c 0,-0.535026 0.461706,-0.96875 1.03125,-0.96875 0.569544,0 1.03125,0.433724 1.03125,0.96875 z"
transform="matrix(0.95714504,0,0,1.0228099,1.028519,-0.77553647)" /></g><g
id="g4596"
transform="translate(68.942911,-2.9168155)"><rect
transform="matrix(0.67898035,0.73415645,-0.73415645,0.67898035,0,0)"
y="-1.6325631"
x="40.742825"
height="5.9375"
width="1"
id="rect4592"
style="fill:#ff2121;fill-opacity:1;stroke:none" /><path
transform="matrix(0.95714504,0,0,1.0228099,1.028519,-0.77553647)"
d="M 26.0625,33.03125 C 26.0625,33.566276 25.600794,34 25.03125,34 24.461706,34 24,33.566276 24,33.03125 c 0,-0.535026 0.461706,-0.96875 1.03125,-0.96875 0.569544,0 1.03125,0.433724 1.03125,0.96875 z"
sodipodi:ry="0.96875"
sodipodi:rx="1.03125"
sodipodi:cy="33.03125"
sodipodi:cx="25.03125"
id="path4594"
style="fill:#ff2121;fill-opacity:1;stroke:none"
sodipodi:type="arc" /></g><path
sodipodi:type="arc"
style="fill:none;stroke:#646464;stroke-width:1.37966764;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
id="path4608"
sodipodi:cx="25"
sodipodi:cy="33"
sodipodi:rx="10"
sodipodi:ry="10"
d="m 35,33 a 10,10 0 1 1 -20,0 10,10 0 1 1 20,0 z"
transform="matrix(0.74693846,0,0,0.74919978,75.258402,5.3595919)" /><g
id="g4628"
transform="translate(68.942911,-2.9168155)"><rect
y="26.853086"
x="24.638252"
height="1.1490486"
width="0.68500972"
id="rect4624"
style="fill:#232323;fill-opacity:1;stroke:none" /><rect
style="fill:#232323;fill-opacity:1;stroke:none"
id="rect4626"
width="0.68500972"
height="1.1490486"
x="24.660349"
y="37.990021" /></g><g
id="g4632"
transform="matrix(0,1,-1,0,126.93079,5.087457)"><rect
style="fill:#232323;fill-opacity:1;stroke:none"
id="rect4634"
width="0.68500972"
height="1.1490486"
x="24.638252"
y="26.853086" /><rect
y="37.990021"
x="24.660349"
height="1.1490486"
width="0.68500972"
id="rect4636"
style="fill:#232323;fill-opacity:1;stroke:none" /></g><rect
style="fill:#232323;fill-opacity:1;stroke:none"
id="rect4640"
width="0.68500972"
height="1.1490486"
x="-45.50618"
y="-93.834114"
transform="matrix(-0.70710678,0.70710678,-0.70710678,-0.70710678,0,0)" /><rect
y="-82.697182"
x="-45.484081"
height="1.1490486"
width="0.68500972"
id="rect4642"
style="fill:#232323;fill-opacity:1;stroke:none"
transform="matrix(-0.70710678,0.70710678,-0.70710678,-0.70710678,0,0)" /><g
transform="matrix(1.2021267,0,0,0.11694557,63.863529,11.401153)"
id="g4707"><rect
ry="16.649275"
rx="1.6196786"
y="27.449707"
x="10.297243"
height="18.119614"
width="29.256544"
id="rect4709"
style="fill:#d4d4d4;fill-opacity:1;stroke:none" /><g
id="g4711"><g
id="g4713"><rect
ry="16.514845"
style="fill:none;stroke:#646464;stroke-width:2.66511464;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
id="rect4715"
width="29.020325"
height="17.982044"
x="10.510214"
y="27.517153"
rx="1" /></g></g></g><g
id="g4985"
transform="translate(0,-11.125)"><g
transform="matrix(0.01936185,0,0,0.01859457,41.505849,40.621203)"
id="g4874"
style="display:inline"><rect
style="opacity:0.40206185;color:#000000;fill:url(#linearGradient4894);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible"
id="rect4876"
width="1339.6335"
height="478.35718"
x="-1559.2523"
y="-150.69685" /><path
inkscape:connector-curvature="0"
style="opacity:0.40206185;color:#000000;fill:url(#radialGradient4896);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible"
d="m -219.61876,-150.68038 c 0,0 0,478.33079 0,478.33079 142.874166,0.90045 345.40022,-107.16966 345.40014,-239.196175 0,-132.026537 -159.436816,-239.134595 -345.40014,-239.134615 z"
id="path4878"
sodipodi:nodetypes="cccc" /><path
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccc"
id="path4880"
d="m -1559.2523,-150.68038 c 0,0 0,478.33079 0,478.33079 -142.8742,0.90045 -345.4002,-107.16966 -345.4002,-239.196175 0,-132.026537 159.4368,-239.134595 345.4002,-239.134615 z"
style="opacity:0.40206185;color:#000000;fill:url(#radialGradient4898);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible" /></g><g
id="g4882"
transform="translate(-0.96783034,-2.9168155)"><rect
ry="1.9470588"
rx="1.9470588"
y="27.449707"
x="10.297243"
height="18.119614"
width="29.256544"
id="rect4884"
style="fill:#eaeaea;fill-opacity:1;stroke:none" /><g
id="g4886"><g
id="g4888"><rect
ry="1.9313381"
style="fill:none;stroke:#646464;stroke-width:0.99927008;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
id="rect4890"
width="29.020325"
height="17.982044"
x="10.510214"
y="27.517153"
rx="1.9313381" /></g><rect
ry="1.0032353"
style="fill:none;stroke:#ffffff;stroke-width:0.99249077;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
id="rect4892"
width="26.967867"
height="16.005489"
x="11.532422"
y="28.489801"
rx="1.0032353" /></g></g><g
transform="matrix(1,0,0,0.9039548,0,2.5932203)"
id="g4922"><rect
style="fill:#000000;fill-opacity:1;stroke:none"
id="rect4900"
width="1"
height="11.0625"
x="13.0625"
y="27" /><rect
y="27"
x="15.0625"
height="11.0625"
width="1"
id="rect4902"
style="fill:#000000;fill-opacity:1;stroke:none" /><rect
style="fill:#000000;fill-opacity:1;stroke:none"
id="rect4904"
width="1"
height="11.0625"
x="18"
y="27" /><rect
y="27"
x="19.9375"
height="11.0625"
width="2"
id="rect4906"
style="fill:#000000;fill-opacity:1;stroke:none" /><rect
y="27"
x="22.9375"
height="11.0625"
width="1"
id="rect4908"
style="fill:#000000;fill-opacity:1;stroke:none" /><rect
style="fill:#000000;fill-opacity:1;stroke:none"
id="rect4910"
width="2.0625"
height="11.0625"
x="25.9375"
y="27" /><rect
y="27"
x="28.9375"
height="11.0625"
width="1.125"
id="rect4912"
style="fill:#000000;fill-opacity:1;stroke:none" /><rect
style="fill:#000000;fill-opacity:1;stroke:none"
id="rect4914"
width="1.125"
height="11.0625"
x="30.9375"
y="27" /><rect
y="27"
x="33.9375"
height="11.0625"
width="1.125"
id="rect4916"
style="fill:#000000;fill-opacity:1;stroke:none" /></g><text
sodipodi:linespacing="125%"
id="text4918"
y="40.195068"
x="12.981453"
style="font-size:36.59571838px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
xml:space="preserve"><tspan
style="font-size:3.65957189px;font-style:normal;font-variant:normal;font-weight:500;font-stretch:normal;font-family:Inconsolata;-inkscape-font-specification:Inconsolata Medium"
y="40.195068"
x="12.981453"
id="tspan4920"
sodipodi:role="line">5 92102431 8</tspan></text>
<rect
y="31.0625"
x="9.0625"
height="1"
width="30"
id="rect4933"
style="fill:#ff0000;fill-opacity:1;stroke:none" /><rect
style="fill:#ff0000;fill-opacity:1;stroke:none;filter:url(#filter4971)"
id="rect4957"
width="30"
height="1"
x="9.0625"
y="31.0625" /><path
inkscape:connector-curvature="0"
id="path4975"
d="M 9.0625,32.0625 20,47.875 28.9375,47.9375 39,32.0625 z"
style="opacity:0.29787233;fill:url(#linearGradient4983);fill-opacity:1;stroke:none" /></g></svg>

After

Width:  |  Height:  |  Size: 41 KiB

View File

@ -0,0 +1,62 @@
v Affichage Catégories doivent respecter le poids
v Affichage Catégories par poids doivent avoir une hiérarchie plate
v Le Onscreen keyboard ne fonctionne plus
v Scrolling dans la sélection produit et dans
la liste de courses
v Réductions
v Redesign liste de courses
v bugs scans produits par prix par poids
v si pas photo photo par defaut
v WebSQL
v Redesign header, bouton retour aux backend en mode caissière
v bouton exit en mode caissière doit retourner au backend.
v posting orders
v bug ajout ligne payment à zero + curseur dedans + suppression + design plus grand
v si case print via proxy cochée, alors on skip l'ecran receipt
v bouton exit self checkout
v demarrage en mode self-checkout si self-checkout
v produits poid code barre non reconnus
v activer popup client aux écrans self-checkout
v discount pas plus grand que 100
v numpad dans l'écran payment pour pouvoir entrer le montant
v différence de total. Methode d'arrondis
- login alternatif
- user preferences : désactiver la balance etc.
- numpad state parfois sans state
- numpad state en mode pesée ?? le cacher ?
- Redesign du receipt
- retirer le receipt screen
- le numpad en mode paymenet marche pas
- l'écran de payement est hyper laid
- l'écran de pesée est hyper laid
x générer les données de printing
x popups d'erreur certainement buggés
TODO AUG 20
-----------
* GREEN STATUS
- make it green !
* TRUNK
- Connection status tooltip
v Remove receipt screen
- Finish the receipt JSON generation
- Modifie le widget de liste course
v supprimer l'écran
v bloquer sur l'impression
v L'impression est foireuse
* CLIENT
- create a new branch
- Self-checkout welcome screen
- removal of products for the root category
- Terminal de payement
- Code à barres
- Vidanges
- Design
- Backport
- CSS pour écran résistif
- Test sur matos

View File

@ -0,0 +1,235 @@
function openerp_pos_db(instance, module){
/* PosLS is a LocalStorage based implementation of the point of sale database,
it performs better for few products, but does not scale beyond 500 products.
*/
module.PosLS = instance.web.Class.extend({
name: 'openerp_pos_ls', //the prefix of the localstorage data
limit: 100, // the maximum number of results returned by a search
init: function(options){
options = options || {};
this.name = options.name || this.name;
this.limit = options.limit || this.limit;
//cache the data in memory to avoid roundtrips to the localstorage
this.cache = {};
this.category_by_id = {};
this.root_category_id = 0;
this.category_products = {};
this.category_ancestors = {};
this.category_childs = {};
this.category_parent = {};
this.category_search_string = {};
},
/* returns the category object from its id. If you pass a list of id as parameters, you get
* a list of category objects.
*/
get_category_by_id: function(categ_id){
if(categ_id instanceof Array){
var list = [];
for(var i = 0, len = categ_id.length; i < len; i++){
var cat = this.category_by_id[categ_id[i]];
if(cat){
list.push(cat);
}else{
console.error("get_category_by_id: no category has id:",categ_id[i]);
}
}
return list;
}else{
return this.category_by_id[categ_id];
}
},
/* returns a list of the category's child categories ids, or an empty list
* if a category has no childs */
get_category_childs_ids: function(categ_id){
return this.category_childs[categ_id] || [];
},
/* returns a list of all ancestors (parent, grand-parent, etc) categories ids
* starting from the root category to the direct parent */
get_category_ancestors_ids: function(categ_id){
return this.category_ancestors[categ_id] || [];
},
/* returns the parent category's id of a category, or the root_category_id if no parent.
* the root category is parent of itself. */
get_category_parent_id: function(categ_id){
return this.category_parent[categ_id] || this.root_category_id;
},
/* adds categories definitions to the database. categories is a list of categories objects as
* returned by the openerp server. Categories must be inserted before the products or the
* product/ categories association may (will) not work properly */
add_categories: function(categories){
var self = this;
if(!this.category_by_id[this.root_category_id]){
this.category_by_id[this.root_category_id] = {
id : this.root_category_id,
name : 'Root',
};
}
for(var i=0, len = categories.length; i < len; i++){
this.category_by_id[categories[i].id] = categories[i];
}
for(var i=0, len = categories.length; i < len; i++){
var cat = categories[i];
var parent_id = cat.parent_id[0] || this.root_category_id;
this.category_parent[cat.id] = cat.parent_id[0];
if(!this.category_childs[parent_id]){
this.category_childs[parent_id] = [];
}
this.category_childs[parent_id].push(cat.id);
}
function make_ancestors(cat_id, ancestors){
self.category_ancestors[cat_id] = ancestors;
ancestors = ancestors.slice(0);
ancestors.push(cat_id);
var childs = self.category_childs[cat_id] || [];
for(var i=0, len = childs.length; i < len; i++){
make_ancestors(childs[i], ancestors);
}
}
make_ancestors(this.root_category_id, []);
},
/* loads a record store from the database. returns default if nothing is found */
load: function(store,deft){
if(this.cache[store] !== undefined){
return this.cache[store];
}
var data = localStorage[this.name + '_' + store];
if(data !== undefined){
data = JSON.parse(data);
this.cache[store] = data;
return data;
}else{
return deft;
}
},
/* saves a record store to the database */
save: function(store,data){
localStorage[this.name + '_' + store] = JSON.stringify(data);
this.cache[store] = data;
},
_product_search_string: function(product){
var str = '' + product.id + ':' + product.name;
if(product.ean13){
str += '|' + product.ean13;
}
return str + '\n';
},
add_products: function(products){
var stored_products = this.load('products',{});
var stored_categories = this.load('categories',{});
if(!products instanceof Array){
products = [products];
}
for(var i = 0, len = products.length; i < len; i++){
var product = products[i];
var search_string = this._product_search_string(product);
var categ_id = product.pos_categ_id[0];
if(!stored_categories[categ_id]){
stored_categories[categ_id] = [];
}
stored_categories[categ_id].push(product.id);
if(this.category_search_string[categ_id] === undefined){
this.category_search_string[categ_id] = '';
}
this.category_search_string[categ_id] += search_string;
var ancestors = this.get_category_ancestors_ids(categ_id) || [];
for(var j = 0; j < ancestors.length; j++){
var ancestor = ancestors[j];
if(! stored_categories[ancestor]){
stored_categories[ancestor] = [];
}
stored_categories[ancestor].push(product.id);
if( this.category_search_string[ancestor] === undefined){
this.category_search_string[ancestor] = '';
}
this.category_search_string[ancestor] += search_string;
}
stored_products[product.id] = product;
}
this.save('products',stored_products);
this.save('categories',stored_categories);
},
/* removes all the data from the database. TODO : being able to selectively remove data */
clear: function(stores){
for(var i = 0, len = arguments.length; i < len; i++){
localStorage.removeItem(this.name + '_' + arguments[i]);
}
},
/* this internal methods returns the count of properties in an object. */
_count_props : function(obj){
var count = 0;
for(var prop in obj){
if(obj.hasOwnProperty(prop)){
count++;
}
}
return count;
},
get_product_by_id: function(id){
return this.load('products',{})[id];
},
get_product_by_ean13: function(ean13){
var products = this.load('products',{});
for(var i in products){
if( products[i] && products[i].ean13 === ean13){
return products[i];
}
}
return undefined;
},
get_product_by_category: function(category_id){
var stored_categories = this.load('categories',{});
var stored_products = this.load('products',{});
var product_ids = stored_categories[category_id];
var list = [];
for(var i = 0, len = Math.min(product_ids.length,this.limit); i < len; i++){
list.push(stored_products[product_ids[i]]);
}
return list;
},
/* returns a list of products with :
* - a category that is or is a child of category_id,
* - a name, package or ean13 containing the query (case insensitive)
*/
search_product_in_category: function(category_id, query){
var re = RegExp("([0-9]+):.*?"+query,"gi");
var results = [];
for(var i = 0; i < this.limit; i++){
r = re.exec(this.category_search_string[category_id]);
if(r){
var id = Number(r[1]);
results.push(this.get_product_by_id(id));
}else{
break;
}
}
return results;
},
add_order: function(order){
var last_id = this.load('last_order_id',0);
var orders = this.load('orders',[]);
orders.push({id: last_id + 1, data: order});
this.save('last_order_id',last_id+1);
this.save('orders',orders);
},
remove_order: function(order_id){
var orders = this.load('orders',[]);
orders = _.filter(orders, function(order){
return order.id !== order_id;
});
this.save('orders',orders);
},
get_orders: function(){
return this.load('orders',[]);
},
});
}

View File

@ -17,7 +17,9 @@ function openerp_pos_devices(instance,module){ //module is instance.point_of_sal
delay_payment: function(){ this.activate(); this.payment_status = 'waiting_for_payment'; },
}))();
//window.debug_devices = debug_devices;
if(jQuery.deparam(jQuery.param.querystring()).debug !== undefined){
window.debug_devices = debug_devices;
}
// this object interfaces with the local proxy to communicate to the various hardware devices
// connected to the Point of Sale. As the communication only goes from the POS to the proxy,
@ -42,21 +44,26 @@ function openerp_pos_devices(instance,module){ //module is instance.point_of_sal
success_callback = success_callback || function(){};
error_callback = error_callback || function(){};
if(debug_devices && debug_devices.active){
if(jQuery.deparam(jQuery.param.querystring()).debug !== undefined){
console.log('PROXY:',name,params);
}else{
}
if(!(debug_devices && debug_devices.active)){
this.connection.rpc('/pos/'+name, params || {}, success_callback, error_callback);
}
},
//a product has been scanned and recognized with success
scan_item_success: function(){
this.message('scan_item_success');
// ean is a parsed ean object
scan_item_success: function(ean){
this.message('scan_item_success',ean);
},
//a product has been scanned but not recognized
scan_item_error_unrecognized: function(){
this.message('scan_item_error_unrecognized');
// a product has been scanned but not recognized
// ean is a parsed ean object
scan_item_error_unrecognized: function(ean){
this.message('scan_item_error_unrecognized',ean);
},
//the client is asking for help
@ -222,11 +229,11 @@ function openerp_pos_devices(instance,module){ //module is instance.point_of_sal
this.action_callback_stack = [];
this.price_prefix_set = attributes.price_prefix_set || {'02':'', '22':'', '24':'', '26':'', '28':''};
this.weight_prefix_set = attributes.weight_prefix_set || {'21':'','23':'','27':'','29':'','25':''};
this.client_prefix_set = attributes.weight_prefix_set || {'42':''};
this.cashier_prefix_set = attributes.weight_prefix_set || {'40':''};
this.discount_prefix_set = attributes.weight_prefix_set || {'44':''};
this.weight_prefix_set = attributes.weight_prefix_set || {'21':''};
this.discount_prefix_set = attributes.discount_prefix_set || {'22':''};
this.price_prefix_set = attributes.price_prefix_set || {'23':''};
this.cashier_prefix_set = attributes.cashier_prefix_set || {'041':''};
this.client_prefix_set = attributes.client_prefix_set || {'042':''};
},
save_callbacks: function(){
var callbacks = {};
@ -270,41 +277,37 @@ function openerp_pos_devices(instance,module){ //module is instance.point_of_sal
this.action_callback[action] = undefined;
}
},
// returns the checksum of the ean, or -1 if the ean has not the correct length, ean must be a string
ean_checksum: function(ean){
var code = ean.split('');
if(code.length !== 13){
return -1;
}
var oddsum = 0, evensum = 0, total = 0;
code = code.reverse().splice(1);
for(var i = 0; i < code.length; i++){
if(i % 2 == 0){
oddsum += Number(code[i]);
}else{
evensum += Number(code[i]);
}
}
total = oddsum * 3 + evensum;
return Number((10 - total % 10) % 10);
},
// returns true if the ean is a valid EAN codebar number by checking the control digit.
// ean must be a string
check_ean: function(ean){
var code = ean.split('');
for(var i = 0; i < code.length; i++){
code[i] = Number(code[i]);
return this.ean_checksum(ean) === Number(ean[ean.length-1]);
},
// returns a valid zero padded ean13 from an ean prefix. the ean prefix must be a string.
sanitize_ean:function(ean){
ean = ean.substr(0,13);
for(var n = 0, count = (13 - ean.length); n < count; n++){
ean = ean + '0';
}
var st1 = code.slice();
var st2 = st1.slice(0,st1.length-1).reverse();
// some EAN13 barcodes have a length of 12, as they start by 0
while (st2.length < 12) {
st2.push(0);
}
var countSt3 = 1;
var st3 = 0;
$.each(st2, function() {
if (countSt3%2 === 1) {
st3 += this;
}
countSt3 ++;
});
st3 *= 3;
var st4 = 0;
var countSt4 = 1;
$.each(st2, function() {
if (countSt4%2 === 0) {
st4 += this;
}
countSt4 ++;
});
var st5 = st3 + st4;
var cd = (10 - (st5%10)) % 10;
return code[code.length-1] === cd;
return ean.substr(0,12) + this.ean_checksum(ean);
},
// attempts to interpret an ean (string encoding an ean)
@ -327,41 +330,47 @@ function openerp_pos_devices(instance,module){ //module is instance.point_of_sal
type:'unknown', //
prefix:'',
ean:ean,
base_ean: ean,
id:'',
value: 0,
unit: 'none',
};
var prefix2 = ean.substring(0,2);
console.log('ean',ean);
if(!this.check_ean(ean)){
function match_prefix(prefix_set, type){
for(prefix in prefix_set){
if(ean.substring(0,prefix.length) === prefix){
parse_result.prefix = prefix;
parse_result.type = type;
return true;
}
}
return false;
}
if (!this.check_ean(ean)){
parse_result.type = 'error';
}else if (prefix2 in this.price_prefix_set){
parse_result.type = 'price';
parse_result.prefix = prefix2;
} else if( match_prefix(this.price_prefix_set,'price')){
parse_result.id = ean.substring(0,7);
parse_result.base_ean = this.sanitize_ean(ean.substring(0,7));
parse_result.value = Number(ean.substring(7,12))/100.0;
parse_result.unit = 'euro';
} else if (prefix2 in this.weight_prefix_set){
parse_result.type = 'weight';
parse_result.prefix = prefix2;
} else if( match_prefix(this.weight_prefix_set,'weight')){
parse_result.id = ean.substring(0,7);
parse_result.value = Number(ean.substring(7,12))/1000.0;
parse_result.base_ean = this.sanitize_ean(ean.substring(0,7));
parse_result.unit = 'Kg';
}else if (prefix2 in this.client_prefix_set){
parse_result.type = 'client';
parse_result.prefix = prefix2;
} else if( match_prefix(this.client_prefix_set,'client')){
parse_result.id = ean.substring(0,7);
}else if (prefix2 in this.cashier_prefix_set){
parse_result.type = 'cashier';
parse_result.prefix = prefix2;
parse_result.unit = 'Kg';
} else if( match_prefix(this.cashier_prefix_set,'cashier')){
parse_result.id = ean.substring(0,7);
}else if (prefix2 in this.discount_prefix_set){
parse_result.type = 'discount';
parse_result.prefix = prefix2;
} else if( match_prefix(this.discount_prefix_set,'discount')){
parse_result.id = ean.substring(0,7);
parse_result.base_ean = this.sanitize_ean(ean.substring(0,7));
parse_result.value = Number(ean.substring(7,12))/100.0;
parse_result.unit = '%';
}else{
} else {
parse_result.type = 'unit';
parse_result.prefix = '';
parse_result.id = ean;
@ -402,7 +411,7 @@ function openerp_pos_devices(instance,module){ //module is instance.point_of_sal
var parse_result = self.parse_ean(codeNumbers.join(''));
if (parse_result.type === 'error') { //most likely a checksum error, raise warning
console.error('ERROR: barcode checksum error:',parse_result);
console.warn('WARNING: barcode checksum error:',parse_result);
}else if(parse_result.type in {'unit':'', 'weight':'', 'price':''}){ //ean is associated to a product
if(self.action_callback['product']){
self.action_callback['product'](parse_result);

View File

@ -5,6 +5,8 @@ openerp.point_of_sale = function(instance) {
var module = instance.point_of_sale;
openerp_pos_db(instance,module); // import db.js
openerp_pos_models(instance,module); // import pos_models.js
openerp_pos_basewidget(instance,module); // import pos_basewidget.js

View File

@ -1,51 +1,6 @@
function openerp_pos_models(instance, module){ //module is instance.point_of_sale
var QWeb = instance.web.qweb;
module.LocalStorageDAO = instance.web.Class.extend({
add_operation: function(operation) {
var self = this;
return $.async_when().pipe(function() {
var tmp = self._get('oe_pos_operations', []);
var last_id = self._get('oe_pos_operations_sequence', 1);
tmp.push({'id': last_id, 'data': operation});
self._set('oe_pos_operations', tmp);
self._set('oe_pos_operations_sequence', last_id + 1);
});
},
remove_operation: function(id) {
var self = this;
return $.async_when().pipe(function() {
var tmp = self._get('oe_pos_operations', []);
tmp = _.filter(tmp, function(el) {
return el.id !== id;
});
self._set('oe_pos_operations', tmp);
});
},
get_operations: function() {
var self = this;
return $.async_when().pipe(function() {
return self._get('oe_pos_operations', []);
});
},
_get: function(key, default_) {
var txt = localStorage['oe_pos_dao_'+key];
if (! txt)
return default_;
return JSON.parse(txt);
},
_set: function(key, value) {
localStorage['oe_pos_dao_'+key] = JSON.stringify(value);
},
reset_stored_data: function(){
for(key in localStorage){
if(key.indexOf('oe_pos_dao_') === 0){
delete localStorage[key];
}
}
},
});
var fetch = function(model, fields, domain, ctx){
return new instance.web.Model(model).query(fields).filter(domain).context(ctx).all()
};
@ -58,29 +13,20 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
// this is done asynchronously, a ready deferred alows the GUI to wait interactively
// for the loading to be completed
// There is a single instance of the PosModel for each Front-End instance, it is usually called
// 'pos' and is available to almost all widgets.
// 'pos' and is available to all widgets extending PosWidget.
module.PosModel = Backbone.Model.extend({
initialize: function(session, attributes) {
Backbone.Model.prototype.initialize.call(this, attributes);
var self = this;
this.dao = new module.LocalStorageDAO(); // used to store the order's data on the Hard Drive
this.session = session;
this.ready = $.Deferred(); // used to notify the GUI that the PosModel has loaded all resources
this.flush_mutex = new $.Mutex(); // used to make sure the orders are sent to the server once at time
//this.build_tree = _.bind(this.build_tree, this); // ???
this.session = session;
this.categories = {};
this.root_category = null;
this.weightable_categories = []; // a flat list of all categories that directly contain weightable products
this.barcode_reader = new module.BarcodeReader({'pos': this}); // used to read barcodes
this.proxy = new module.ProxyDevice(); // used to communicate to the hardware devices via a local proxy
// pos settings
this.use_scale = false;
this.use_proxy_printer = false;
this.use_virtual_keyboard = false;
this.use_websql = false;
this.use_barcode_scanner = false;
this.barcode_reader = new module.BarcodeReader({'pos': this}); // used to read barcodes
this.proxy = new module.ProxyDevice(); // used to communicate to the hardware devices via a local proxy
this.db = new module.PosLS(); // a database used to store the products and categories
this.db.clear('products','categories');
// default attributes values. If null, it will be loaded below.
this.set({
@ -98,12 +44,12 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
'products': new module.ProductCollection(),
'cashRegisters': null,
'product_list': null, // the list of all products, does not change.
'bank_statements': null,
'taxes': null,
'pos_session': null,
'pos_config': null,
'categories': null,
'units': null,
'units_by_id': null,
'selectedOrder': undefined,
});
@ -113,10 +59,9 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
// We fetch the backend data on the server asynchronously. this is done only when the pos user interface is launched,
// Any change on this data made on the server is thus not reflected on the point of sale until it is relaunched.
var user_def = fetch('res.users',['name','company_id'],[['id','=',this.session.uid]])
var loaded = fetch('res.users',['name','company_id'],[['id','=',this.session.uid]])
.pipe(function(users){
var user = users[0];
self.set('user',user);
self.set('user',users[0]);
return fetch('res.company',
[
@ -124,171 +69,114 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
'email',
'website',
'company_registry',
//TODO contact_address
'vat',
'name',
'phone'
'phone',
'partner_id',
],
[['id','=',user.company_id[0]]])
[['id','=',users[0].company_id[0]]]);
}).pipe(function(companies){
var company = companies[0];
self.set('company',company);
self.set('company',companies[0]);
return fetch('res.currency',['symbol','position'],[['id','=',company.currency_id[0]]]);
}).pipe(function (currencies){
return fetch('res.partner',['contact_address'],[['id','=',companies[0].partner_id[0]]]);
}).pipe(function(company_partners){
self.get('company').contact_address = company_partners[0].contact_address;
return fetch('res.currency',['symbol','position'],[['id','=',self.get('company').currency_id[0]]]);
}).pipe(function(currencies){
self.set('currency',currencies[0]);
});
var cat_def = fetch('pos.category', ['id','name', 'parent_id', 'child_id', 'image_medium'])
.pipe(function(result){
return self.set({'categories': result});
});
var uom_def = fetch( //unit of measure
'product.uom',
null,
null
).then(function(result){
self.set({'units': result});
return fetch('product.uom', null, null);
}).pipe(function(units){
self.set('units',units);
var units_by_id = {};
for(var i = 0, len = result.length; i < len; i++){
units_by_id[result[i].id] = result[i];
for(var i = 0, len = units.length; i < len; i++){
units_by_id[units[i].id] = units[i];
}
self.set({'units_by_id':units_by_id});
});
var pack_def = fetch(
'product.packaging',
null,
null
).then(function(packaging){
self.set('product.packaging',packaging);
});
var users_def = fetch(
'res.users',
['name','ean13'],
[['ean13', '!=', false]]
).then(function(result){
self.set({'user_list':result});
});
var tax_def = fetch('account.tax', ['amount','price_include','type'])
.then(function(result){
self.set({'taxes': result});
});
var session_def = fetch( // loading the PoS Session.
'pos.session',
['id', 'journal_ids','name','user_id','config_id','start_at','stop_at'],
[['state', '=', 'opened'], ['user_id', '=', this.session.uid]]
).pipe(function(result) {
// some data are associated with the pos session, like the pos config and bank statements.
// we must have a valid session before we can read those.
self.set('units_by_id',units_by_id);
var session_data_def = new $.Deferred();
return fetch('product.packaging', null, null);
}).pipe(function(packagings){
self.set('product.packaging',packagings);
if( result.length !== 0 ) {
var pos_session = result[0];
return fetch('res.users', ['name','ean13'], [['ean13', '!=', false]]);
}).pipe(function(users){
self.set('user_list',users);
self.set({'pos_session': pos_session});
return fetch('account.tax', ['amount', 'price_include', 'type']);
}).pipe(function(taxes){
self.set('taxes', taxes);
var pos_config_def = fetch(
'pos.config',
['name','journal_ids','shop_id','journal_id',
'iface_self_checkout', 'iface_websql', 'iface_led', 'iface_cashdrawer',
'iface_payment_terminal', 'iface_electronic_scale', 'iface_barscan', 'iface_vkeyboard',
'iface_print_via_proxy','iface_cashdrawer','state','sequence_id','session_ids'],
[['id','=', pos_session.config_id[0]]]
).pipe(function(result){
var pos_config = result[0]
self.set({'pos_config': pos_config});
self.use_scale = pos_config.iface_electronic_scale || false;
self.use_proxy_printer = pos_config.iface_print_via_proxy || false;
self.use_virtual_keyboard = pos_config.iface_vkeyboard || false;
self.use_websql = pos_config.iface_websql || false;
self.use_barcode_scanner = pos_config.iface_barscan || false;
self.use_selfcheckout = pos_config.iface_self_checkout || false;
self.use_cashbox = pos_config.iface_cashdrawer || false;
return fetch(
'pos.session',
['id', 'journal_ids','name','user_id','config_id','start_at','stop_at'],
[['state', '=', 'opened'], ['user_id', '=', self.session.uid]]
);
}).pipe(function(sessions){
self.set('pos_session', sessions[0]);
return shop_def = fetch('sale.shop',[], [['id','=',pos_config.shop_id[0]]])
}).pipe(function(shops){
self.set('shop',shops[0]);
return fetch(
'product.product',
//context {pricelist: shop.pricelist_id[0]}
['name', 'list_price','price','pos_categ_id', 'taxes_id','image_medium', 'ean13', 'to_weight', 'uom_id', 'uos_id', 'uos_coeff', 'mes_type'],
[['pos_categ_id','!=', false]],
{pricelist: shops[0].pricelist_id[0]} // context for price
);
}).pipe( function(product_list){
self.set({'product_list': product_list});
});
return fetch(
'pos.config',
['name','journal_ids','shop_id','journal_id',
'iface_self_checkout', 'iface_led', 'iface_cashdrawer',
'iface_payment_terminal', 'iface_electronic_scale', 'iface_barscan', 'iface_vkeyboard',
'iface_print_via_proxy','iface_cashdrawer','state','sequence_id','session_ids'],
[['id','=', self.get('pos_session').config_id[0]]]
);
}).pipe(function(configs){
var pos_config = configs[0];
self.set('pos_config', pos_config);
self.iface_electronic_scale = !!pos_config.iface_electronic_scale;
self.iface_print_via_proxy = !!pos_config.iface_print_via_proxy;
self.iface_vkeyboard = !!pos_config.iface_vkeyboard;
self.iface_self_checkout = !!pos_config.iface_self_checkout;
self.iface_cashdrawer = !!pos_config.iface_cashdrawer;
var bank_def = fetch(
'account.bank.statement',
['account_id','currency','journal_id','state','name','user_id','pos_session_id'],
[['state','=','open'],['pos_session_id', '=', pos_session.id]]
).then(function(result){
self.set({'bank_statements':result});
});
return fetch('sale.shop',[],[['id','=',pos_config.shop_id[0]]]);
}).pipe(function(shops){
self.set('shop',shops[0]);
var journal_def = fetch(
'account.journal',
undefined,
[['user_id','=',pos_session.user_id[0]]]
).then(function(result){
self.set({'journals':result});
});
return fetch('pos.category', ['id','name','parent_id','child_id','image'])
}).pipe(function(categories){
self.db.add_categories(categories);
// associate the bank statements with their journals.
var bank_process_def = $.when(bank_def, journal_def)
.then(function(){
var bank_statements = self.get('bank_statements');
var journals = self.get('journals');
for(var i = 0, ilen = bank_statements.length; i < ilen; i++){
for(var j = 0, jlen = journals.length; j < jlen; j++){
if(bank_statements[i].journal_id[0] === journals[j].id){
bank_statements[i].journal = journals[j];
bank_statements[i].self_checkout_payment_method = journals[j].self_checkout_payment_method;
}
}
}
});
return fetch(
'product.product',
['name', 'list_price','price','pos_categ_id', 'taxes_id', 'ean13', 'to_weight', 'uom_id', 'uos_id', 'uos_coeff', 'mes_type'],
[['pos_categ_id','!=', false]],
{pricelist: self.get('shop').pricelist_id[0]} // context for price
);
}).pipe(function(products){
self.db.add_products(products);
session_data_def = $.when(pos_config_def,bank_def,journal_def,bank_process_def);
return fetch(
'account.bank.statement',
['account_id','currency','journal_id','state','name','user_id','pos_session_id'],
[['state','=','open'],['pos_session_id', '=', self.get('pos_session').id]]
);
}).pipe(function(bank_statements){
self.set('bank_statements', bank_statements);
}else{
session_data_def.reject();
}
return session_data_def;
});
// associate the products with their categories
var prod_process_def = $.when(cat_def, session_def)
.pipe(function(){
var product_list = self.get('product_list');
var categories = self.get('categories');
var cat_by_id = {};
for(var i = 0; i < categories.length; i++){
cat_by_id[categories[i].id] = categories[i];
}
//set the parent in the category
for(var i = 0; i < categories.length; i++){
categories[i].parent_category = cat_by_id[categories[i].parent_id[0]];
}
for(var i = 0; i < product_list.length; i++){
product_list[i].pos_category = cat_by_id[product_list[i].pos_categ_id[0]];
return fetch('account.journal', undefined, [['user_id','=', self.get('pos_session').user_id[0]]]);
}).pipe(function(journals){
self.set('journals',journals);
// associate the bank statements with their journals.
var bank_statements = self.get('bank_statements');
for(var i = 0, ilen = bank_statements.length; i < ilen; i++){
for(var j = 0, jlen = journals.length; j < jlen; j++){
if(bank_statements[i].journal_id[0] === journals[j].id){
bank_statements[i].journal = journals[j];
bank_statements[i].self_checkout_payment_method = journals[j].self_checkout_payment_method;
}
}
}
self.set({'cashRegisters' : new module.CashRegisterCollection(self.get('bank_statements'))});
});
// when all the data has loaded, we compute some stuff, and declare the Pos ready to be used.
$.when(pack_def, cat_def, user_def, users_def, uom_def, session_def, tax_def, prod_process_def, user_def, this.flush())
$.when(loaded)
.then(function(){
self.build_categories();
self.set({'cashRegisters' : new module.CashRegisterCollection(self.get('bank_statements'))});
//self.log_loaded_data(); //Uncomment if you want to log the data to the console for easier debugging
self.ready.resolve();
},function(){
@ -302,7 +190,6 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
log_loaded_data: function(){
console.log('PosModel data has been loaded:');
console.log('PosModel: categories:',this.get('categories'));
console.log('PosModel: product_list:',this.get('product_list'));
console.log('PosModel: units:',this.get('units'));
console.log('PosModel: bank_statements:',this.get('bank_statements'));
console.log('PosModel: journals:',this.get('journals'));
@ -316,7 +203,6 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
console.log('PosModel: user_list:',this.get('user_list'));
console.log('PosModel: user:',this.get('user'));
console.log('PosModel.session:',this.session);
console.log('PosModel.categories:',this.categories);
console.log('PosModel end of data log.');
},
@ -324,7 +210,7 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
// order and a valid selected order
on_removed_order: function(removed_order){
if( this.get('orders').isEmpty()){
this.add_and_select_order(new module.Order({ pos: this }));
this.add_new_order();
}
if( this.get('selectedOrder') === removed_order){
this.set({ selectedOrder: this.get('orders').last() });
@ -333,175 +219,70 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
// saves the order locally and try to send it to the backend. 'record' is a bizzarely defined JSON version of the Order
push_order: function(record) {
var self = this;
return this.dao.add_operation(record).pipe(function(){
return self.flush();
});
this.db.add_order(record);
this.flush();
},
add_and_select_order: function(newOrder) {
(this.get('orders')).add(newOrder);
return this.set({
selectedOrder: newOrder
});
//creates a new empty order and sets it as the current order
add_new_order: function(){
var order = new module.Order({pos:this});
this.get('orders').add(order);
this.set('selectedOrder', order);
},
// attemps to send all pending orders ( stored in the DAO ) to the server.
// it will do it one by one, and remove the successfully sent ones from the DAO once
// it has been confirmed that they have been received.
// attemps to send all pending orders ( stored in the pos_db ) to the server,
// and remove the successfully sent ones from the db once
// it has been confirmed that they have been sent correctly.
flush: function() {
//this makes sure only one _int_flush is called at the same time
return this.flush_mutex.exec(_.bind(function() {
return this._int_flush();
return this._flush(0);
}, this));
},
_int_flush : function() {
// attempts to send an order of index 'index' in the list of order to send. The index
// is used to skip orders that failed. do not call this method outside the mutex provided
// by flush()
_flush: function(index){
var self = this;
var orders = this.db.get_orders();
self.set('nbr_pending_operations',orders.length);
this.dao.get_operations().pipe(function(operations) {
// operations are really Orders that are converted to json.
// they are saved to disk and then we attempt to send them to the backend so that they can
// be applied.
// since the network is not reliable we potentially have many 'pending operations' that have not been sent.
self.set( {'nbr_pending_operations':operations.length} );
if(operations.length === 0){
return $.when();
}
var order = operations[0];
// we prevent the default error handler and assume errors
// are a normal use case, except we stop the current iteration
return (new instance.web.Model('pos.order')).get_func('create_from_ui')([order])
.fail(function(unused, event){
// wtf ask niv
event.preventDefault();
})
.pipe(function(){
// success: remove the successfully sent operation, and try to send the next one
self.dao.remove_operation(operations[0].id).pipe(function(){
return self._int_flush();
});
}, function(){
// in case of error we just sit there and do nothing. wtf ask niv
return $.when();
});
});
var order = orders[index];
if(!order){
return;
}
//try to push an order to the server
(new instance.web.Model('pos.order')).get_func('create_from_ui')([order])
.fail(function(unused, event){
//don't show error popup if it fails
event.preventDefault();
console.error('Failed to send order:',order);
self._flush(index+1);
})
.done(function(){
//remove from db if success
self.db.remove_order(order.id);
self._flush(index);
});
},
// this adds several properties to the categories in order to make it easier to diplay them
// fields added include the list of product relevant to each category, list of child categories,
// list of ancestors, etc.
build_categories : function(){
var categories = this.get('categories');
var products = this.get('product_list');
scan_product: function(parsed_ean){
var self = this;
var product = this.db.get_product_by_ean13(parsed_ean.base_ean);
var selectedOrder = this.get('selectedOrder');
//append the content of array2 into array1
function append(array1, array2){
for(var i = 0, len = array2.length; i < len; i++){
array1.push(array2[i]);
}
if(!product){
return false;
}
function appendSet(set1, set2){
for(key in set2){
set1[key] = set2[key];
}
if(parsed_ean.type === 'price'){
selectedOrder.addProduct(new module.Product(product), {price:parsed_ean.value});
}else if(parsed_ean.type === 'weight'){
selectedOrder.addProduct(new module.Product(product), {quantity:parsed_ean.value, merge:false});
}else{
selectedOrder.addProduct(new module.Product(product));
}
var categories_by_id = {};
for(var i = 0; i < categories.length; i++){
categories_by_id[categories[i].id] = categories[i];
}
this.categories_by_id = categories_by_id;
var root_category = {
name : 'Root',
id : 0,
parent : null,
childrens : [],
};
// add parent and childrens field to categories, find root_categories
for(var i = 0; i < categories.length; i++){
var cat = categories[i];
cat.parent = categories_by_id[cat.parent_id[0]];
if(!cat.parent){
root_category.childrens.push(cat);
cat.parent = root_category;
}
cat.childrens = [];
for(var j = 0; j < cat.child_id.length; j++){
cat.childrens.push(categories_by_id[ cat.child_id[j] ]);
}
}
categories.push(root_category);
// set some default fields for next steps
for(var i = 0; i < categories.length; i++){
var cat = categories[i];
cat.product_list = []; //list of all products in the category
cat.product_set = {}; // [product.id] === true if product is in category
cat.weightable_product_list = [];
cat.weightable_product_set = {};
cat.weightable = false; //true if directly contains weightable products
}
this.root_category = root_category;
//we add the products to the categories.
for(var i = 0, len = products.length; i < len; i++){
var product = products[i];
var cat = categories_by_id[product.pos_categ_id[0]];
if(cat){
cat.product_list.push(product);
cat.product_set[product.id] = true;
if(product.to_weight){
cat.weightable_product_list.push(product);
cat.weightable_product_set[product.id] = true;
cat.weightable = true;
}
}
}
// we build a flat list of all categories that directly contains weightable products
this.weightable_categories = [];
for(var i = 0, len = categories.length; i < len; i++){
var cat = categories[i];
if(cat.weightable){
this.weightable_categories.push(cat);
}
}
// add ancestor field to categories, contains the list of parents of parents, from root to parent
function make_ancestors(cat, ancestors){
cat.ancestors = ancestors.slice(0);
ancestors.push(cat);
for(var i = 0; i < cat.childrens.length; i++){
make_ancestors(cat.childrens[i], ancestors.slice(0));
}
}
//add the products of the subcategories to the parent categories
function make_products(cat){
for(var i = 0; i < cat.childrens.length; i++){
make_products(cat.childrens[i]);
append(cat.product_list, cat.childrens[i].product_list);
append(cat.weightable_product_list, cat.childrens[i].weightable_product_list);
appendSet(cat.product_set, cat.childrens[i].product_set);
appendSet(cat.weightable_product_set, cat.childrens[i].weightable_product_set);
}
}
make_ancestors(root_category,[]);
make_products(root_category);
return true;
},
});
@ -608,15 +389,10 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
return false;
}else if(this.get_discount() > 0){ // we don't merge discounted orderlines
return false;
}else if(this.get_product_type() === 'unit'){
return true;
}else if(this.get_product_type() === 'weight'){
return true;
}else if(this.get_product_type() === 'price'){
return this.get_product().get('list_price') === orderline.get_product().get('list_price');
}else{
console.error('point_of_sale/pos_models.js/Orderline.can_be_merged_with() : unknown product type:',this.get('product_type'));
}else if(this.price !== orderline.price){
return false;
}else{
return true;
}
},
merge: function(orderline){
@ -882,6 +658,7 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
change: this.getChange(),
name : this.getName(),
client: client ? client.name : null ,
invoice_id: null, //TODO
cashier: cashier ? cashier.name : null,
date: {
year: date.getFullYear(),
@ -895,7 +672,7 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
email: company.email,
website: company.website,
company_registry: company.company_registry,
contact_address: null, //TODO
contact_address: company.contact_address,
vat: company.vat,
name: company.name,
phone: company.phone,

View File

@ -152,14 +152,6 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
this.pos_widget.screen_selector.show_popup('help');
},
logout_button_action: function(){
this.pos_widget.screen_selector.set_user_mode('client');
},
close_button_action: function(){
this.pos_widget.try_close();
},
barcode_product_screen: 'products', //if defined, this screen will be loaded when a product is scanned
barcode_product_error_popup: 'error', //if defined, this popup will be loaded when there's an error in the popup
@ -167,14 +159,16 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
// it will add the product to the order and go to barcode_product_screen. Or show barcode_product_error_popup if
// there's an error.
barcode_product_action: function(ean){
if(this.pos_widget.scan_product(ean)){
this.pos.proxy.scan_item_success();
if(this.barcode_product_screen){
this.pos_widget.screen_selector.set_current_screen(this.barcode_product_screen);
var self = this;
if(self.pos.scan_product(ean)){
self.pos.proxy.scan_item_success(ean);
if(self.barcode_product_screen){
self.pos_widget.screen_selector.set_current_screen(self.barcode_product_screen);
}
}else{
if(this.barcode_product_error_popup){
this.pos_widget.screen_selector.show_popup(this.barcode_product_error_popup);
self.pos.proxy.scan_item_error_unrecognized(ean);
if(self.barcode_product_error_popup && self.pos_widget.screen_selector.get_user_mode() !== 'cashier'){
self.pos_widget.screen_selector.show_popup(self.barcode_product_error_popup);
}
}
},
@ -194,6 +188,7 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
return true;
}
}
this.pos.proxy.scan_item_unrecognized(ean);
return false;
},
@ -207,9 +202,11 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
if(users[i].ean13 === ean.ean){
this.pos.get('selectedOrder').set_client(users[i]);
this.pos_widget.username.refresh();
this.pos.proxy.scan_item_success(ean);
return true;
}
}
this.pos.proxy.scan_item_unrecognized(ean);
return false;
//TODO start the transaction
},
@ -217,29 +214,78 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
// what happens when a discount barcode is scanned : the default behavior
// is to set the discount on the last order.
barcode_discount_action: function(ean){
this.pos.proxy.scan_item_success(ean);
var last_orderline = this.pos.get('selectedOrder').getLastOrderline();
if(last_orderline){
last_orderline.set_discount(ean.value)
}
},
// shows an action bar on the screen. The actionbar is automatically shown when you add a button
// with add_action_button()
show_action_bar: function(){
this.pos_widget.action_bar.show();
this.$element.css({'bottom':'105px'});
},
// hides the action bar. The actionbar is automatically hidden when it is empty
hide_action_bar: function(){
this.pos_widget.action_bar.hide();
this.$element.css({'bottom':'0px'});
},
// adds a new button to the action bar. The button definition takes three parameters, all optional :
// - label: the text below the button
// - icon: a small icon that will be shown
// - click: a callback that will be executed when the button is clicked.
// the method returns a reference to the button widget, and automatically show the actionbar.
add_action_button: function(button_def){
this.show_action_bar();
return this.pos_widget.action_bar.add_new_button(button_def);
},
// this method shows the screen and sets up all the widget related to this screen. Extend this method
// if you want to alter the behavior of the screen.
show: function(){
var self = this;
this.hidden = false;
if(this.$element){
this.$element.show();
}
if(this.pos_widget.action_bar.get_button_count() > 0){
this.show_action_bar();
}else{
this.hide_action_bar();
}
// we add the help button by default. we do this because the buttons are cleared on each refresh so that
// the button stay local to each screen
this.pos_widget.left_action_bar.add_new_button({
label: 'help',
icon: '/point_of_sale/static/src/img/icons/png48/help.png',
click: function(){ self.help_button_action(); },
});
var self = this;
var cashier_mode = this.pos_widget.screen_selector.get_user_mode() === 'cashier';
this.pos_widget.set_numpad_visible(this.show_numpad && cashier_mode);
this.pos_widget.set_leftpane_visible(this.show_leftpane);
this.pos_widget.set_left_action_bar_visible(this.show_leftpane && !cashier_mode);
this.pos_widget.set_cashier_controls_visible(cashier_mode);
this.pos_widget.action_bar.set_element_visible('help-button', !cashier_mode, function(){ self.help_button_action(); });
this.pos_widget.action_bar.set_element_visible('logout-button', cashier_mode && this.pos.use_selfcheckout, function(){ self.logout_button_action(); });
this.pos_widget.action_bar.set_element_visible('close-button', cashier_mode, function(){ self.close_button_action(); });
if(cashier_mode && this.pos.iface_self_checkout){
this.pos_widget.client_button.show();
}else{
this.pos_widget.client_button.hide();
}
if(cashier_mode){
this.pos_widget.close_button.show();
}else{
this.pos_widget.close_button.hide();
}
this.pos_widget.username.set_user_mode(this.pos_widget.screen_selector.get_user_mode());
@ -251,16 +297,14 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
});
},
// this method is called when the screen is closed to make place for a new screen. this is a good place
// to put your cleanup stuff as it is guaranteed that for each show() there is one and only one close()
close: function(){
if(this.pos.barcode_reader){
this.pos.barcode_reader.reset_action_callbacks();
}
if(this.pos_widget.action_bar){
this.pos_widget.action_bar.destroy_buttons();
}
this.pos_widget.action_bar.destroy_buttons();
this.pos_widget.left_action_bar.destroy_buttons();
},
// this methods hides the screen. It's not a good place to put your cleanup stuff as it is called on the
@ -349,6 +393,9 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
module.ScaleInviteScreenWidget = module.ScreenWidget.extend({
template:'ScaleInviteScreenWidget',
next_screen:'scale',
previous_screen:'products',
show: function(){
this._super();
var self = this;
@ -359,21 +406,19 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
var weight = self.pos.proxy.weighting_read_kg();
if(weight > 0.001){
clearInterval(this.intervalID);
self.pos_widget.screen_selector.set_current_screen('scale');
self.pos_widget.screen_selector.set_current_screen(self.next_screen);
}
},500);
this.pos_widget.action_bar.add_new_button(
{
this.add_action_button({
label: 'back',
icon: '/point_of_sale/static/src/img/icons/png48/go-previous.png',
click: function(){
clearInterval(this.intervalID);
self.pos.proxy.weighting_end();
self.pos_widget.screen_selector.set_current_screen('products');
self.pos_widget.screen_selector.set_current_screen(self.previous_screen);
}
}
);
});
},
close: function(){
this._super();
@ -383,25 +428,30 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
module.ScaleScreenWidget = module.ScreenWidget.extend({
template:'ScaleScreenWidget',
next_screen: 'products',
previous_screen: 'products',
show: function(){
this._super();
this.renderElement();
var self = this;
this.pos_widget.action_bar.add_new_button({
this.add_action_button({
label: 'back',
icon: '/point_of_sale/static/src/img/icons/png48/go-previous.png',
click: function(){
self.pos_widget.screen_selector.set_current_screen('products');
self.pos_widget.screen_selector.set_current_screen(self.previous_screen);
}
});
this.validate_button = this.pos_widget.action_bar.add_new_button({
this.validate_button = this.add_action_button({
label: 'Validate',
icon: '/point_of_sale/static/src/img/icons/png48/validate.png',
click: function(){
self.order_product();
self.pos_widget.screen_selector.set_current_screen('products');
self.pos_widget.screen_selector.set_current_screen(self.next_screen);
},
});
@ -419,7 +469,7 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
this._super();
this.$('.product-picture').click(function(){
self.order_product();
self.pos_widget.screen_selector.set_current_screen('products');
self.pos_widget.screen_selector.set_current_screen(self.next_screen);
});
},
get_product: function(){
@ -444,7 +494,7 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
},
get_product_image: function(){
var product = this.get_product();
return product ? product.get('image_medium') : undefined;
return product ? product.get('image') : undefined;
},
get_product_weight: function(){
return this.weight || 0;
@ -458,6 +508,10 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
module.ClientPaymentScreenWidget = module.ScreenWidget.extend({
template:'ClientPaymentScreenWidget',
next_screen: 'welcome',
previous_screen: 'products',
show: function(){
this._super();
var self = this;
@ -486,7 +540,7 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
self.pos.push_order(currentOrder.exportAsJSON()).then(function() {
currentOrder.destroy();
self.pos.proxy.transaction_end();
self.pos_widget.screen_selector.set_current_screen('welcome');
self.pos_widget.screen_selector.set_current_screen(self.next_screen);
});
}else if(payment === 'payment_rejected'){
clearInterval(this.intervalID);
@ -494,17 +548,15 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
}
},500);
this.pos_widget.action_bar.add_new_button(
{
this.add_action_button({
label: 'back',
icon: '/point_of_sale/static/src/img/icons/png48/go-previous.png',
click: function(){ //TODO Go to ask for weighting screen
click: function(){
clearInterval(this.intervalID);
self.pos.proxy.payment_canceled();
self.pos_widget.screen_selector.set_current_screen('products');
self.pos_widget.screen_selector.set_current_screen(self.previous_screen);
}
}
);
});
},
close: function(){
this._super();
@ -515,23 +567,32 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
module.WelcomeScreenWidget = module.ScreenWidget.extend({
template:'WelcomeScreenWidget',
next_screen: 'products',
show_numpad: false,
show_leftpane: false,
barcode_client_action: function(ean){
this._super(ean);
this.pos_widget.screen_selector.set_current_screen('products');
this.pos_widget.screen_selector.set_current_screen(this.next_screen);
},
show: function(){
this._super();
var self = this;
$('.goodbye-message').css({opacity:1}).show();
setTimeout(function(){
$('.goodbye-message').animate({opacity:0},500,'swing',function(){$('.goodbye-message').hide();});
},3000);
},
});
module.ProductScreenWidget = module.ScreenWidget.extend({
template:'ProductScreenWidget',
scale_screen: 'scale_invite',
client_next_screen: 'client_payment',
show_numpad: true,
show_leftpane: true,
@ -542,8 +603,8 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
this.product_list_widget = new module.ProductListWidget(this,{
click_product_action: function(product){
if(product.get('to_weight') && self.pos.use_scale){
self.pos_widget.screen_selector.set_current_screen('scale_invite', {product: product});
if(product.get('to_weight') && self.pos.iface_electronic_scale){
self.pos_widget.screen_selector.set_current_screen(self.scale_screen, {product: product});
}else{
self.pos.get('selectedOrder').addProduct(product);
}
@ -559,16 +620,16 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
this.product_categories_widget.reset_category();
this.pos_widget.order_widget.set_numpad_state(this.pos_widget.numpad.state);
if(this.pos.use_virtual_keyboard){
if(this.pos.iface_vkeyboard){
this.pos_widget.onscreen_keyboard.connect();
}
if(this.pos_widget.screen_selector.current_mode === 'client'){
this.pos_widget.action_bar.add_new_button({
this.add_action_button({
label: 'pay',
icon: '/point_of_sale/static/src/img/icons/png48/go-next.png',
click: function(){
self.pos_widget.screen_selector.set_current_screen('client_payment');
self.pos_widget.screen_selector.set_current_screen(self.client_next_screen);
}
});
}
@ -604,17 +665,19 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
this._super();
var self = this;
this.pos_widget.action_bar.add_new_button({
this.add_action_button({
label: 'Print',
icon: '/point_of_sale/static/src/img/icons/png48/printer.png',
click: function(){ self.print(); },
});
this.pos_widget.action_bar.add_new_button({
this.add_action_button({
label: 'Next Order',
icon: '/point_of_sale/static/src/img/icons/png48/go-next.png',
click: function() { self.finishOrder(); },
});
window.print();
},
print: function() {
window.print();
@ -643,6 +706,8 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
module.PaymentScreenWidget = module.ScreenWidget.extend({
template: 'PaymentScreenWidget',
back_screen: 'products',
next_screen: 'receipt',
init: function(parent, options) {
this._super(parent,options);
this.model = options.model;
@ -654,21 +719,21 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
this._super();
var self = this;
if(this.pos.use_cashbox){
if(this.pos.iface_cashdrawer){
this.pos.proxy.open_cashbox();
}
this.set_numpad_state(this.pos_widget.numpad.state);
this.back_button = this.pos_widget.action_bar.add_new_button({
this.back_button = this.add_action_button({
label: 'Back',
icon: '/point_of_sale/static/src/img/icons/png48/go-previous.png',
click: function(){
self.pos_widget.screen_selector.set_current_screen('products');
self.pos_widget.screen_selector.set_current_screen(self.back_screen);
},
});
this.validate_button = this.pos_widget.action_bar.add_new_button({
this.validate_button = this.add_action_button({
label: 'Validate',
icon: '/point_of_sale/static/src/img/icons/png48/validate.png',
click: function(){
@ -682,24 +747,18 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
this.pos_widget.payment_screen.set_numpad_state(null);
},
back: function() {
this.pos_widget.screen_selector.set_current_screen('products');
this.pos_widget.screen_selector.set_current_screen(self.back_screen);
},
validateCurrentOrder: function() {
var self = this;
var currentOrder = this.pos.get('selectedOrder');
this.validate_button.$element.attr('disabled','disabled'); //FIXME is the css actually using this attr ?
this.pos.push_order(currentOrder.exportAsJSON())
.then(function() {
self.validate_button.$element.removeAttr('disabled');
if(self.pos.use_proxy_printer){
self.pos.proxy.print_receipt(currentOrder.export_for_printing());
self.pos.get('selectedOrder').destroy(); //finish order and go back to scan screen
}else{
self.pos_widget.screen_selector.set_current_screen('receipt');
}
});
if(this.pos.iface_print_via_proxy){
this.pos.proxy.print_receipt(currentOrder.export_for_printing());
this.pos.get('selectedOrder').destroy(); //finish order and go back to scan screen
}else{
this.pos_widget.screen_selector.set_current_screen(this.next_screen);
}
},
bindPaymentLineEvents: function() {
this.currentPaymentLines = (this.pos.get('selectedOrder')).get('paymentLines');

View File

@ -65,6 +65,7 @@ function openerp_pos_scrollbar(instance, module){ //module is instance.point_of_
this.auto_hide(false);
this.$element.bind('mousewheel',function(event,delta){
self.scroll(delta*self.wheel_step);
return false;
});
this.$element.bind('click',function(event){
var vpos = event.pageY - self.$element.offset().top;

View File

@ -1,6 +1,57 @@
function openerp_pos_widgets(instance, module){ //module is instance.point_of_sale
var QWeb = instance.web.qweb;
// The ImageCache is used to hide the latency of the application cache on-disk access in chrome
// that causes annoying flickering on product pictures. Why the hell a simple access to
// the application cache involves such latency is beyond me, hopefully one day this can be
// removed.
module.ImageCache = instance.web.Class.extend({
init: function(options){
options = options || {};
this.max_size = options.max_size || 500;
this.cache = {};
this.access_time = {};
this.size = 0;
},
get_image_uncached: function(url){
var img = new Image();
img.src = url;
return img;
},
// returns a DOM Image object from an url, and cache the last 500 (by default) results
get_image: function(url){
var cached = this.cache[url];
if(cached){
this.access_time[url] = (new Date()).getTime();
return cached;
}else{
var img = new Image();
img.src = url;
while(this.size >= this.max_size){
var oldestUrl = null;
var oldestTime = (new Date()).getTime();
for(var url in this.cache){
var time = this.access_time[url];
if(time <= oldestTime){
oldestTime = time;
oldestUrl = url;
}
}
if(oldestUrl){
delete this.cache[oldestUrl];
delete this.access_time[oldestUrl];
}
this.size--;
}
this.cache[url] = img;
this.access_time[url] = (new Date()).getTime();
this.size++;
return img;
}
},
});
module.NumpadWidget = module.PosBaseWidget.extend({
template:'NumpadWidget',
init: function(parent, options) {
@ -112,7 +163,7 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
template:'OrderWidget',
init: function(parent, options) {
this._super(parent,options);
this.compact = false;
this.display_mode = options.display_mode || 'numpad'; // 'maximized' | 'actionbar' | 'numpad'
this.set_numpad_state(options.numpadState);
this.pos.bind('change:selectedOrder', this.change_selected_order, this);
this.bind_orderline_events();
@ -165,8 +216,12 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
var self = this;
this._super();
if(!this.compact){
if(this.display_mode === 'maximized'){
$('.point-of-sale .order-container').css({'bottom':'0px'});
}else if(this.display_mode === 'actionbar'){
$('.point-of-sale .order-container').css({'bottom':'105px'});
}else if(this.display_mode !== 'numpad'){
console.error('ERROR: OrderWidget renderElement(): wrong display_mode:',this.display_mode);
}
var $content = this.$('.orderlines');
@ -211,9 +266,9 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
var total = order ? order.getTotal() : 0;
this.$('.summary .value.total').html(this.format_currency(total));
},
set_compact: function(compact){
if(this.compact !== compact){
this.compact = compact;
set_display_mode: function(mode){
if(this.display_mode !== mode){
this.display_mode = mode;
this.renderElement();
}
},
@ -225,20 +280,16 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
this._super(parent,options);
this.model = options.model;
this.model.attributes.weight = options.weight;
this.next_screen = options.next_screen;
this.click_product_action = options.click_product_action;
this.next_screen = options.next_screen; //when a product is clicked, this screen is set
this.click_product_action = options.click_product_action;
},
add_to_order: function(event) {
/* Preserve the category URL */
event.preventDefault();
return (this.pos.get('selectedOrder')).addProduct(this.model);
},
set_weight: function(weight){
this.model.attributes.weight = weight;
this.renderElement();
// returns the url of the product thumbnail
get_image_url: function() {
return '/web/binary/image?session_id='+instance.session.session_id+'&model=product.product&field=image&id='+this.model.get('id');
},
renderElement: function() {
this._super();
this.$('img').replaceWith(this.pos_widget.image_cache.get_image(this.get_image_url()));
var self = this;
$("a", this.$element).click(function(e){
if(self.click_product_action){
@ -313,6 +364,7 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
module.ActionButtonWidget = instance.web.Widget.extend({
template:'ActionButtonWidget',
icon_template:'ActionButtonWidgetWithIcon',
init: function(parent, options){
this._super(parent, options);
this.label = options.label || 'button';
@ -320,7 +372,7 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
this.click_action = options.click;
if(options.icon){
this.icon = options.icon;
this.template = 'ActionButtonWidgetWithIcon';
this.template = this.icon_template;
}
},
renderElement: function(){
@ -359,21 +411,25 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
this.button_list = [];
return this;
},
get_button_count: function(){
return this.button_list.length;
},
add_new_button: function(button_options){
if(arguments.length == 1){
var button = new module.ActionButtonWidget(this,button_options);
this.button_list.push(button);
button.appendTo($('.pos-actionbar-button-list'));
return button;
}else{
for(var i = 0; i < arguments.length; i++){
this.add_new_button(arguments[i]);
}
}
return undefined;
var button = new module.ActionButtonWidget(this,button_options);
this.button_list.push(button);
button.appendTo(this.$('.pos-actionbar-button-list'));
return button;
},
show:function(){
this.$element.show();
},
hide:function(){
this.$element.hide();
},
});
module.CategoryButton = module.PosBaseWidget.extend({
});
module.ProductCategoriesWidget = module.PosBaseWidget.extend({
template: 'ProductCategoriesWidget',
init: function(parent, options){
@ -389,41 +445,53 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
// changes the category. if undefined, sets to root category
set_category : function(category){
var db = this.pos.db;
if(!category){
this.category = this.pos.root_category;
this.category = db.get_category_by_id(db.root_category_id);
}else{
this.category = category;
}
this.breadcrumb = [];
for(var i = 1; i < this.category.ancestors.length; i++){
this.breadcrumb.push(this.category.ancestors[i]);
var ancestors_ids = db.get_category_ancestors_ids(this.category.id);
for(var i = 1; i < ancestors_ids.length; i++){
this.breadcrumb.push(db.get_category_by_id(ancestors_ids[i]));
}
if(this.category !== this.pos.root_category){
if(this.category.id !== db.root_category_id){
this.breadcrumb.push(this.category);
}
if(this.product_type === 'weightable'){
this.subcategories = [];
for(var i = 0; i < this.category.childrens.length; i++){
if(this.category.childrens[i].weightable_product_list.length > 0){
this.subcategories.push( this.category.childrens[i]);
}
}
}else{
this.subcategories = this.category.childrens || [];
}
this.subcategories = db.get_category_by_id(db.get_category_childs_ids(this.category.id));
},
get_image_url: function(category){
return '/web/binary/image?session_id='+instance.session.session_id+'&model=pos.category&field=image&id='+category.id;
},
renderElement: function(){
var self = this;
this._super();
_.each(this.subcategories, function(category){
var button = QWeb.render('CategoryButton',{category:category});
button = _.str.trim(button);
$(button).appendTo(this.$('.category-list')).click(function(event){
var hasimages = false; //if none of the subcategories have images, we don't display buttons with icons
_.each(this.subcategories, function(category){
if(category.image){
hasimages = true;
}
});
_.each(this.subcategories, function(category){
if(hasimages){
var button = QWeb.render('CategoryButton',{category:category});
var button = _.str.trim(button);
var button = $(button);
button.find('img').replaceWith(self.pos_widget.image_cache.get_image(self.get_image_url(category)));
}else{
var button = QWeb.render('CategorySimpleButton',{category:category});
button = _.str.trim(button); // we remove whitespace between buttons to fix spacing
var button = $(button);
}
button.appendTo(this.$('.category-list')).click(function(event){
var id = category.id;
var cat = self.pos.categories_by_id[id];
var cat = self.pos.db.get_category_by_id(id);
self.set_category(cat);
self.renderElement();
self.search_and_categories(cat);
@ -432,7 +500,7 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
// breadcrumb click actions
this.$(".oe-pos-categories-list a").click(function(event){
var id = $(event.target).data("category-id");
var category = self.pos.categories_by_id[id];
var category = self.pos.db.get_category_by_id(id);
self.set_category(category);
self.renderElement();
self.search_and_categories(category);
@ -455,47 +523,30 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
// filters the products, and sets up the search callbacks
search_and_categories: function(category){
var self = this;
var all_products = this.pos.get('product_list');
var all_packages = this.pos.get('product.packaging');
// find all products belonging to the current category
var products = [];
if(this.product_type === 'weightable'){
products = all_products.filter( function(product){
return self.category.weightable_product_set[product.id];
});
}else{
products = all_products.filter( function(product){
return self.category.product_set[product.id];
});
}
var products = this.pos.db.get_product_by_category(this.category.id);
self.pos.get('products').reset(products);
// product lists watch for reset events on 'products' to re-render.
// FIXME that means all productlist widget re-render... even the hidden ones !
this.pos.get('products').reset(products);
// find all the products whose name match the query in the searchbox
// filter the products according to the search string
this.$('.searchbox input').keyup(function(){
var results, search_str;
search_str = $(this).val().toLowerCase();
if(search_str){
results = products.filter( function(p){
return p.name.toLowerCase().indexOf(search_str) != -1 ||
(p.ean13 && p.ean13.indexOf(search_str) != -1);
});
self.$element.find('.search-clear').fadeIn();
query = $(this).val().toLowerCase();
if(query){
var products = self.pos.db.search_product_in_category(self.category.id, query);
self.pos.get('products').reset(products);
self.$('.search-clear').fadeIn();
}else{
results = products;
self.$element.find('.search-clear').fadeOut();
var products = self.pos.db.get_product_by_category(self.category.id);
self.pos.get('products').reset(products);
self.$('.search-clear').fadeOut();
}
self.pos.get('products').reset(results);
});
this.$('.searchbox input').click(function(){
});
this.$('.searchbox input').click(function(){}); //Why ???
//reset the search when clicking on reset
this.$('.search-clear').click(function(){
var products = self.pos.db.get_product_by_category(self.category.id);
self.pos.get('products').reset(products);
self.$('.searchbox input').val('').focus();
self.$('.search-clear').fadeOut();
@ -519,11 +570,6 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
self.renderElement();
});
},
set_weight: function(weight){
for(var i = 0; i < this.product_list.length; i++){
this.product_list[i].set_weight(weight);
}
},
renderElement: function() {
var self = this;
this._super();
@ -586,14 +632,35 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
},
});
module.HeaderButtonWidget = module.PosBaseWidget.extend({
template: 'HeaderButtonWidget',
init: function(parent, options){
options = options || {};
this._super(parent, options);
this.action = options.action;
this.label = options.label;
},
renderElement: function(){
var self = this;
this._super();
if(this.action){
this.$element.click(function(){ self.action(); });
}
},
show: function(){ this.$element.show(); },
hide: function(){ this.$element.hide(); },
});
// ---------- Main Point of Sale Widget ----------
// this is used to notify the user that data is being synchronized on the network
module.SynchNotificationWidget = module.PosBaseWidget.extend({
template: "SynchNotificationWidget",
init: function(parent,options) {
init: function(parent, options){
options = options || {};
this._super(parent,options);
this._super(parent, options);
},
renderElement: function() {
var self = this;
@ -632,9 +699,19 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
this.pos_widget = this; //So that pos_widget's childs have pos_widget set automatically
this.numpad_visible = true;
this.left_action_bar_visible = true;
this.leftpane_visible = true;
this.leftpane_width = '440px';
this.cashier_controls_visible = true;
this.image_cache = new module.ImageCache(); // for faster products image display
/*
//Epileptic mode
setInterval(function(){
$('body').css({'-webkit-filter':'hue-rotate('+Math.random()*360+'deg)' });
},100);
*/
},
start: function() {
@ -643,7 +720,9 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
self.build_currency_template();
self.renderElement();
self.$('.neworder-button').click(_.bind(self.create_new_order, self));
self.$('.neworder-button').click(function(){
self.pos.add_new_order();
});
//when a new order is created, add an order button widget
self.pos.get('orders').bind('add', function(new_order){
@ -659,6 +738,10 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
self.build_widgets();
self.screen_selector.set_default_screen();
self.pos.barcode_reader.connect();
instance.webclient.set_content_full_screen(true);
if (!self.pos.get('pos_session')) {
@ -667,9 +750,14 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
self.screen_selector.show_popup('error', 'Sorry, we could not find any PoS Configuration for this session');
}
self.$('.loader').animate({opacity:0},3000,'swing',function(){$('.loader').hide();});
self.$('.loader').animate({opacity:0},1500,'swing',function(){self.$('.loader').hide();});
self.$('.loader img').hide();
if(jQuery.deparam(jQuery.param.querystring()).debug !== undefined){
window.pos = self.pos;
window.pos_widget = self.pos_widget;
}
},function(){ // error when loading models data from the backend
self.$('.loader img').hide();
return new instance.web.Model("ir.model.data").get_func("search_read")([['name', '=', 'action_pos_session_opening']], ['res_id'])
@ -682,8 +770,11 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
}, self));
});
},
// This method instantiates all the screens, widgets, etc. If you want to add new screens change the
// startup screen, etc, override this method.
build_widgets: function() {
var self = this;
// -------- Screens ---------
@ -725,13 +816,16 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
// -------- Misc ---------
this.notification = new module.SynchNotificationWidget(this,{});
this.notification.replace(this.$('.placeholder-SynchNotificationWidget'));
this.notification.appendTo(this.$('#rightheader'));
this.username = new module.UsernameWidget(this,{});
this.username.replace(this.$('.placeholder-UsernameWidget'));
this.action_bar = new module.ActionBarWidget(this);
this.action_bar.appendTo($(".point-of-sale #content"));
this.action_bar.appendTo($(".point-of-sale #rightpane"));
this.left_action_bar = new module.ActionBarWidget(this);
this.left_action_bar.appendTo($(".point-of-sale #leftpane"));
this.paypad = new module.PaypadWidget(this, {});
this.paypad.replace($('#placeholder-PaypadWidget'));
@ -746,6 +840,19 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
'keyboard_model': 'simple'
});
this.onscreen_keyboard.appendTo($(".point-of-sale #content"));
this.close_button = new module.HeaderButtonWidget(this,{
label:'Close',
action: function(){ self.try_close(); },
});
this.close_button.appendTo(this.$('#rightheader'));
this.client_button = new module.HeaderButtonWidget(this,{
label:'Self-Checkout',
action: function(){ self.screen_selector.set_user_mode('client'); },
});
this.client_button.appendTo(this.$('#rightheader'));
// -------- Screen Selector ---------
@ -768,59 +875,11 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
},
default_client_screen: 'welcome',
default_cashier_screen: 'products',
default_mode: this.pos.use_selfcheckout ? 'client' : 'cashier',
default_mode: this.pos.iface_self_checkout ? 'client' : 'cashier',
});
this.screen_selector.set_default_screen();
this.pos.barcode_reader.connect();
},
//FIXME this method is probably not at the right place ...
scan_product: function(parsed_ean){
var selectedOrder = this.pos.get('selectedOrder');
var scannedProductModel = this.get_product_by_ean(parsed_ean);
if (!scannedProductModel){
return false;
} else {
if(parsed_ean.type === 'price'){
selectedOrder.addProduct(new module.Product(scannedProductModel), { price:parsed_ean.value});
}else if(parsed_ean.type === 'weight'){
selectedOrder.addProduct(new module.Product(scannedProductModel), { quantity:parsed_ean.value, merge:false});
}else{
selectedOrder.addProduct(new module.Product(scannedProductModel));
}
return true;
}
},
get_product_by_ean: function(parsed_ean) {
var allProducts = this.pos.get('product_list');
var allPackages = this.pos.get('product.packaging');
var scannedProductModel = undefined;
if (parsed_ean.type === 'price' || parsed_ean.type === 'weight') {
var itemCode = parsed_ean.id;
var scannedPackaging = _.detect(allPackages, function(pack) {
return pack.ean && pack.ean.substring(0,7) === itemCode;
});
if (scannedPackaging) {
scannedProductModel = _.detect(allProducts, function(pc) { return pc.id === scannedPackaging.product_id[0];});
}else{
scannedProductModel = _.detect(allProducts, function(pc) { return pc.ean13 && (pc.ean13.substring(0,7) === parsed_ean.id);});
}
} else if(parsed_ean.type === 'unit'){
scannedProductModel = _.detect(allProducts, function(pc) { return pc.ean13 === parsed_ean.ean;}); //TODO DOES NOT SCALE
}
return scannedProductModel;
},
// creates a new order, and add it to the list of orders.
create_new_order: function() {
var new_order;
new_order = new module.Order({ pos: this.pos });
this.pos.get('orders').add(new_order);
this.pos.set({ selectedOrder: new_order });
},
changed_pending_operations: function () {
var self = this;
this.synch_notification.on_change_nbr_pending(self.pos.get('nbr_pending_operations').length);
@ -830,16 +889,35 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
if(visible !== this.numpad_visible){
this.numpad_visible = visible;
if(visible){
this.set_left_action_bar_visible(false);
this.numpad.show();
this.paypad.show();
this.order_widget.set_compact(true);
this.order_widget.set_display_mode('numpad');
}else{
this.numpad.hide();
this.paypad.hide();
this.order_widget.set_compact(false);
if(this.order_widget.display_mode === 'numpad'){
this.order_widget.set_display_mode('maximized');
}
}
}
},
set_left_action_bar_visible: function(visible){
if(visible !== this.left_action_bar_visible){
this.left_action_bar_visible = visible;
if(visible){
this.set_numpad_visible(false);
this.left_action_bar.show();
this.order_widget.set_display_mode('actionbar');
}else{
this.left_action_bar.hide();
if(this.order_widget.display_mode === 'actionbar'){
this.order_widget.set_display_mode('maximized');
}
}
}
},
//shows or hide the leftpane (contains the list of orderlines, the numpad, the paypad, etc.)
set_leftpane_visible: function(visible){
if(visible !== this.leftpane_visible){
@ -874,12 +952,14 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
});
},
close: function() {
var self = this;
this.pos.barcode_reader.disconnect();
return new instance.web.Model("ir.model.data").get_func("search_read")([['name', '=', 'action_client_pos_menu']], ['res_id']).pipe(
_.bind(function(res) {
return this.rpc('/web/action/load', {'action_id': res[0]['res_id']}).pipe(_.bind(function(result) {
var action = result.result;
action.context = _.extend(action.context || {}, {'cancel_action': {type: 'ir.actions.client', tag: 'reload'}});
//self.destroy();
this.do_action(action);
}, this));
}, this));

View File

@ -15,7 +15,7 @@
<button class="neworder-button">+</button>
<ol id="orders"></ol>
</div>
<span class="placeholder-SynchNotificationWidget"></span>
<!-- here goes header buttons -->
</div>
</div>
<div id="content">
@ -44,6 +44,12 @@
</div>
</t>
<t t-name="HeaderButtonWidget">
<div class="header-button">
<t t-esc="widget.label" />
</div>
</t>
<t t-name="PosCloseWarning">
<div>There are pending operations that could not be saved into the database, are you sure you want to exit?</div>
</t>
@ -83,7 +89,7 @@
<t t-name="CategoryButton">
<li class='category-button'>
<div class="category-img">
<img t-att-src="'data:image/gif;base64,'+ category.image_medium" />
<img src="" />
</div>
<div class="category-name">
<t t-esc="category.name"/>
@ -91,6 +97,12 @@
</li>
</t>
<t t-name="CategorySimpleButton">
<li class='category-simple-button'>
<t t-esc="category.name"/>
</li>
</t>
<t t-name="ProductCategoriesWidget">
<header>
<ol class="breadcrumb">
@ -220,7 +232,7 @@
<t t-if="widget.currency.position == 'after'" t-esc="widget.currency.symbol"/>
</span>
</div>
<div class="infoline">
<div class="infoline" >
<span class='left-block'>
Change:
</span>
@ -252,6 +264,9 @@
<img src="/point_of_sale/static/src/img/scan.png" />
<p> Please scan an item or your member card </p>
</div>
<div class="goodbye-message">
<p>Thank you for shopping with us.</p>
</div>
</t>
@ -342,7 +357,7 @@
<li class='product'>
<a href="#">
<div class="product-img">
<img t-att-src="'data:image/gif;base64,'+ widget.model.get('image_medium')" />
<img src='' /> <!-- the product thumbnail -->
<t t-if="!widget.model.get('to_weight')">
<span class="price-tag">
<t t-esc="widget.format_currency(widget.model.get('list_price'))"/>
@ -493,7 +508,7 @@
<tr><td>Tax:</td><td class="pos-right-align">
<t t-esc="widget.format_currency(widget.currentOrder.getTax().toFixed(2))"/>
</td></tr>
<tr><td>Total:</td><td class="pos-right-align">
<tr class="emph"><td>Total:</td><td class="pos-right-align">
<t t-esc="widget.format_currency(widget.currentOrder.getTotal().toFixed(2))"/>
</td></tr>
</table>
@ -519,30 +534,7 @@
<t t-name="ActionBarWidget">
<div class="pos-actionbar">
<div class="pos-actionbar-left-pane">
<div class="button help-button">
<div class='icon'>
<img src="/point_of_sale/static/src/img/icons/png48/help.png" />
<div class='iconlabel'>Help</div>
</div>
</div>
<div class="button close-button">
<div class='icon'>
<img src="/point_of_sale/static/src/img/icons/png48/shut-down.png" />
<div class='iconlabel'>Close</div>
</div>
</div>
<div class="button logout-button">
<div class='icon'>
<img src="/point_of_sale/static/src/img/icons/png48/system-log-out.png" />
<div class='iconlabel'>Client Mode</div>
</div>
</div>
</div>
<ul class="pos-actionbar-button-list">
<!-- <li class="button">BUTTOOON</li>
<li class="button">JEEENKIINS</li>
<li class="button rightalign">ARGH</li> -->
</ul>
</div>
</t>

View File

@ -1,22 +0,0 @@
<!doctype html>
<html>
<head>
<title>JSON</title>
<script type="text/javascript" src="http://localhost:8069/web/webclient/js"></script>
</head>
<body>
<h1>JSON</h1>
<p id="response"></p>
<script type="text/javascript">
var c = new openerp.init(['web', 'point_of_sale']);
c.connection.session_bind('http://localhost:8069').then(function() {
var w = new c.point_of_sale.test();
w.appendTo($("#response"));
});
</script>
</body>
</html>

View File

@ -9,9 +9,3 @@
-
!python {model: pos.open.statement}: |
self.open_statement(cr, uid, [ref('new_statement_open')], context={})
-
I check that I have some bank statements open for the admin user
-
!python {model: account.bank.statement}: |
ids = self.search(cr, uid, [('state', 'in', ('open','new')), ('user_id', '=', 1)])
assert (len(ids)>0), 'No statement open for the admin user!'

View File

@ -0,0 +1,56 @@
#!/usr/bin/python
import sys
import re
import math
def ean_checksum(eancode):
"""returns the checksum of an ean string of length 13, returns -1 if the string has the wrong length"""
if len(eancode) <> 13:
return -1
oddsum=0
evensum=0
total=0
eanvalue=eancode
reversevalue = eanvalue[::-1]
finalean=reversevalue[1:]
for i in range(len(finalean)):
if i % 2 == 0:
oddsum += int(finalean[i])
else:
evensum += int(finalean[i])
total=(oddsum * 3) + evensum
check = int(10 - math.ceil(total % 10.0)) %10
return check
def check_ean(eancode):
"""returns True if eancode is a valid ean13 string, or null"""
if not eancode:
return True
if len(eancode) <> 13:
return False
try:
int(eancode)
except:
return False
return ean_checksum(eancode) == int(eancode[-1])
def sanitize_ean13(ean13):
"""Creates and returns a valid ean13 from an invalid one"""
if not ean13:
return "0000000000000"
ean13 = re.sub("[A-Za-z]","0",ean13);
ean13 = re.sub("[^0-9]","",ean13);
ean13 = ean13[:13]
if len(ean13) < 13:
ean13 = ean13 + '0' * (13-len(ean13))
return ean13[:-1] + str(ean_checksum(ean13))
def main():
for arg in sys.argv[1:]:
print sanitize_ean13(arg)
if __name__ == '__main__':
main()

View File

@ -4,13 +4,17 @@ from osv import osv, fields
from tools.translate import _
import netsvc
from openerp.addons.point_of_sale.point_of_sale import pos_session
class pos_session_opening(osv.osv_memory):
_name = 'pos.session.opening'
_columns = {
'pos_config_id' : fields.many2one('pos.config', 'Point of Sale', required=True),
'pos_session_id' : fields.many2one('pos.session', 'PoS Session'),
'pos_state' : fields.char('Session State'),
'pos_state' : fields.selection(pos_session.POS_SESSION_STATE,
'Session State', readonly=True),
'show_config' : fields.boolean('Show Config', readonly=True),
}
def open_ui(self, cr, uid, ids, context=None):
@ -19,7 +23,7 @@ class pos_session_opening(osv.osv_memory):
context['active_id'] = data.pos_session_id.id
return {
'type' : 'ir.actions.client',
'name' : 'Start Point Of Sale',
'name' : _('Start Point Of Sale'),
'tag' : 'pos.ui',
'context' : context
}
@ -87,7 +91,11 @@ class pos_session_opening(osv.osv_memory):
if not result:
r = self.pool.get('pos.config').search(cr, uid, [], context=context)
result = r and r[0] or False
count = self.pool.get('pos.config').search_count(cr, uid, [('state', '=', 'active')], context=context)
show_config = bool(count > 1)
return {
'pos_config_id' : result
'pos_config_id' : result,
'show_config' : show_config,
}
pos_session_opening()

View File

@ -6,14 +6,15 @@
<field name="model">pos.session.opening</field>
<field name="arch" type="xml">
<form string="PoS Session Opening" version="7.0">
<separator string="Select your Point of Sale" colspan="4" />
<group>
<field name="show_config" invisible="1" />
<separator string="Select your Point of Sale" colspan="4" attrs="{'invisible' : [('show_config', '=', False)]}" />
<group attrs="{'invisible' : [('show_config', '=', False)]}">
<field name="pos_config_id" on_change="on_change_config(pos_config_id)"
widget="selection" domain="[('state','=','active')]"
class="oe_inline"/>
<field name="pos_state" class="oe_inline" attrs="{'invisible' : [('pos_state', '=', False)]}" />
</group>
<field name="pos_session_id" invisible="1"/>
<field name="pos_state" invisible="1"/>
<button name="open_ui" type="object" string="Start Selling"
attrs="{'invisible' : [('pos_state', 'not in', ('opened',))]}"
class="oe_highlight"

View File

@ -28,18 +28,10 @@ import re
import tools
from tools.translate import _
def is_pair(x):
return not x%2
def check_ean(eancode):
if not eancode:
return True
def ean_checksum(eancode):
"""returns the checksum of an ean string of length 13, returns -1 if the string has the wrong length"""
if len(eancode) <> 13:
return False
try:
int(eancode)
except:
return False
return -1
oddsum=0
evensum=0
total=0
@ -48,17 +40,38 @@ def check_ean(eancode):
finalean=reversevalue[1:]
for i in range(len(finalean)):
if is_pair(i):
if i % 2 == 0:
oddsum += int(finalean[i])
else:
evensum += int(finalean[i])
total=(oddsum * 3) + evensum
check = int(10 - math.ceil(total % 10.0)) %10
return check
if check != int(eancode[-1]):
def check_ean(eancode):
"""returns True if eancode is a valid ean13 string, or null"""
if not eancode:
return True
if len(eancode) <> 13:
return False
return True
try:
int(eancode)
except:
return False
return ean_checksum(eancode) == int(eancode[-1])
def sanitize_ean13(ean13):
"""Creates and returns a valid ean13 from an invalid one"""
if not ean13:
return "0000000000000"
ean13 = re.sub("[A-Za-z]","0",ean13);
ean13 = re.sub("[^0-9]","",ean13);
ean13 = ean13[:13]
if len(ean13) < 13:
ean13 = ean13 + '0' * (13-len(ean13))
return ean13[:-1] + str(ean_checksum(ean13))
#----------------------------------------------------------
# UOM
#----------------------------------------------------------
@ -195,8 +208,10 @@ product_ul()
class product_category(osv.osv):
def name_get(self, cr, uid, ids, context=None):
if not len(ids):
if isinstance(ids, (list, tuple)) and not len(ids):
return []
if isinstance(ids, (long, int)):
ids = [ids]
reads = self.read(cr, uid, ids, ['name','parent_id'], context=context)
res = []
for record in reads:
@ -594,6 +609,7 @@ class product_product(osv.osv):
res = check_ean(product['ean13'])
return res
_constraints = [(_check_ean_key, 'Error: Invalid ean code', ['ean13'])]
def on_order(self, cr, uid, ids, orderline, quantity):

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,167 +0,0 @@
# Translation of OpenERP Server.
# This file contains the translation of the following modules:
# * sale_crm
#
msgid ""
msgstr ""
"Project-Id-Version: OpenERP Server 6.0.2\n"
"Report-Msgid-Bugs-To: support@openerp.com\n"
"POT-Creation-Date: 2011-07-05 15:46+0000\n"
"PO-Revision-Date: 2011-07-05 15:46+0000\n"
"Last-Translator: <>\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"
#. module: sale_crm
#: field:crm.make.sale,partner_id:0
msgid "Customer"
msgstr "Cliente"
#. module: sale_crm
#: view:crm.make.sale:0
msgid "Convert to Quotation"
msgstr "Convertir a presupuesto"
#. module: sale_crm
#: code:addons/sale_crm/wizard/crm_make_sale.py:108
#, python-format
msgid "is converted to Quotation."
msgstr "se ha convertido a presupuesto."
#. module: sale_crm
#: code:addons/sale_crm/wizard/crm_make_sale.py:89
#, python-format
msgid "Data Insufficient!"
msgstr "¡Datos insuficientes!"
#. module: sale_crm
#: code:addons/sale_crm/wizard/crm_make_sale.py:89
#, python-format
msgid "Customer has no addresses defined!"
msgstr "¡El cliente no tiene definidas direcciones!"
#. module: sale_crm
#: model:ir.model,name:sale_crm.model_crm_make_sale
msgid "Make sales"
msgstr "Crear ventas"
#. module: sale_crm
#: model:ir.module.module,description:sale_crm.module_meta_information
msgid "\n"
"This module adds a shortcut on one or several opportunity cases in the CRM.\n"
"This shortcut allows you to generate a sales order based on the selected case.\n"
"If different cases are open (a list), it generates one sale order by\n"
"case.\n"
"The case is then closed and linked to the generated sales order.\n"
"\n"
"We suggest you to install this module if you installed both the sale and the\n"
"crm modules.\n"
" "
msgstr "\n"
"Este módulo añade un acceso directo a uno o varios casos de oportunidades en el CRM.\n"
"Este acceso directo le permite generar un pedido de venta a partir del caso seleccionado.\n"
"Si están abiertos distintos casos (una lista), genera un pedido de venta por\n"
"caso.\n"
"El caso se cierra y es enlazado al pedido ventas generado.\n"
"\n"
"Le sugerimos que instale este módulo si ha instalado ambos módulos sale y crm.\n"
" "
#. module: sale_crm
#: view:crm.make.sale:0
msgid "_Create"
msgstr "_Crear"
#. module: sale_crm
#: sql_constraint:sale.order:0
msgid "Order Reference must be unique !"
msgstr "¡La referencia del pedido debe ser única!"
#. module: sale_crm
#: help:crm.make.sale,close:0
msgid "Check this to close the opportunity after having created the sale order."
msgstr "Marque esta opción para cerrar la oportunidad después de haber creado el pedido de venta."
#. module: sale_crm
#: view:crm.lead:0
msgid "Convert to Quote"
msgstr "Convertir a presupuesto"
#. module: sale_crm
#: view:account.invoice.report:0
#: view:board.board:0
msgid "Monthly Turnover"
msgstr "Volumen mensual"
#. module: sale_crm
#: code:addons/sale_crm/wizard/crm_make_sale.py:110
#, python-format
msgid "Converted to Sales Quotation(id: %s)."
msgstr "Convertido a presupuesto de venta (id: %s)."
#. module: sale_crm
#: code:addons/sale_crm/wizard/crm_make_sale.py:108
#, python-format
msgid "Opportunity "
msgstr "Oportunidad "
#. module: sale_crm
#: code:addons/sale_crm/wizard/crm_make_sale.py:92
#, python-format
msgid "Opportunity: %s"
msgstr "Oportunidad: %s"
#. module: sale_crm
#: model:ir.module.module,shortdesc:sale_crm.module_meta_information
msgid "Creates Sales order from Opportunity"
msgstr "Crea pedido de venta desde oportunidad"
#. module: sale_crm
#: model:ir.actions.act_window,name:sale_crm.action_quotation_for_sale_crm
msgid "Quotations"
msgstr "Presupuestos"
#. module: sale_crm
#: field:crm.make.sale,shop_id:0
msgid "Shop"
msgstr "Tienda"
#. module: sale_crm
#: view:board.board:0
msgid "Opportunities by Stage"
msgstr "Oportunidades por etapa"
#. module: sale_crm
#: view:board.board:0
msgid "My Quotations"
msgstr "Mis presupuestos"
#. module: sale_crm
#: field:crm.make.sale,close:0
msgid "Close Opportunity"
msgstr "Cerrar oportunidad"
#. module: sale_crm
#: view:sale.order:0
#: field:sale.order,section_id:0
msgid "Sales Team"
msgstr "Equipo de ventas"
#. module: sale_crm
#: model:ir.actions.act_window,name:sale_crm.action_crm_make_sale
msgid "Make Quotation"
msgstr "Realizar un presupuesto"
#. module: sale_crm
#: view:crm.make.sale:0
msgid "Cancel"
msgstr "Cancelar"
#. module: sale_crm
#: model:ir.model,name:sale_crm.model_sale_order
msgid "Sales Order"
msgstr "Pedido de venta"

View File

@ -1,335 +0,0 @@
# Translation of OpenERP Server.
# This file contains the translation of the following modules:
# * sale_margin
#
msgid ""
msgstr ""
"Project-Id-Version: OpenERP Server 6.0.2\n"
"Report-Msgid-Bugs-To: support@openerp.com\n"
"POT-Creation-Date: 2011-07-05 15:57+0000\n"
"PO-Revision-Date: 2011-07-05 15:57+0000\n"
"Last-Translator: <>\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"
#. module: sale_margin
#: view:report.account.invoice.product:0
msgid "Category"
msgstr "Categoría"
#. module: sale_margin
#: selection:report.account.invoice.product,type:0
msgid "Customer Refund"
msgstr "Factura de abono de cliente"
#. module: sale_margin
#: selection:report.account.invoice.product,type:0
msgid "Customer Invoice"
msgstr "Factura de cliente"
#. module: sale_margin
#: selection:report.account.invoice.product,month:0
msgid "February"
msgstr "Febrero"
#. module: sale_margin
#: view:report.account.invoice.product:0
msgid "Current"
msgstr "Actual"
#. module: sale_margin
#: view:report.account.invoice.product:0
msgid "Group By..."
msgstr "Agrupar por..."
#. module: sale_margin
#: view:report.account.invoice.product:0
#: field:report.account.invoice.product,state:0
msgid "State"
msgstr "Estado"
#. module: sale_margin
#: model:ir.module.module,description:sale_margin.module_meta_information
msgid " \n"
" This module adds the 'Margin' on sales order,\n"
" which gives the profitability by calculating the difference between the Unit Price and Cost Price\n"
" "
msgstr " \n"
" Este módulo añade el 'Margen' en el pedido de venta,\n"
" que proporciona la rentabilidad calculando la diferencia entre el precio unidad y el precio de coste\n"
" "
#. module: sale_margin
#: view:report.account.invoice.product:0
#: selection:report.account.invoice.product,state:0
msgid "Draft"
msgstr "Borrador"
#. module: sale_margin
#: selection:report.account.invoice.product,state:0
msgid "Paid"
msgstr "Pagado"
#. module: sale_margin
#: model:ir.model,name:sale_margin.model_stock_picking
msgid "Picking List"
msgstr "Lista de Empaque"
#. module: sale_margin
#: help:sale.order,margin:0
msgid "It gives profitability by calculating the difference between the Unit Price and Cost Price."
msgstr "Proporciona la rentabilidad calculando la diferencia entre el precio unidad y el precio de coste."
#. module: sale_margin
#: field:report.account.invoice.product,type:0
msgid "Type"
msgstr "Tipo"
#. module: sale_margin
#: model:ir.model,name:sale_margin.model_report_account_invoice_product
msgid "Invoice Statistics"
msgstr "Estadísticas de facturas"
#. module: sale_margin
#: view:report.account.invoice.product:0
#: field:report.account.invoice.product,product_id:0
msgid "Product"
msgstr "Producto"
#. module: sale_margin
#: sql_constraint:sale.order:0
msgid "Order Reference must be unique !"
msgstr "¡La referencia del pedido debe ser única!"
#. module: sale_margin
#: view:report.account.invoice.product:0
msgid "Invoice by Partner"
msgstr "Facturas por empresa"
#. module: sale_margin
#: selection:report.account.invoice.product,month:0
msgid "August"
msgstr "Agosto"
#. module: sale_margin
#: view:report.account.invoice.product:0
#: selection:report.account.invoice.product,state:0
msgid "Pro-forma"
msgstr "Pro-forma"
#. module: sale_margin
#: selection:report.account.invoice.product,month:0
msgid "May"
msgstr "Mayo"
#. module: sale_margin
#: selection:report.account.invoice.product,month:0
msgid "June"
msgstr "Junio"
#. module: sale_margin
#: view:report.account.invoice.product:0
msgid "Date Invoiced"
msgstr "Fecha de facturación"
#. module: sale_margin
#: model:ir.module.module,shortdesc:sale_margin.module_meta_information
msgid "Margins in Sales Order"
msgstr "Márgenes en pedidos de venta"
#. module: sale_margin
#: view:report.account.invoice.product:0
msgid "Search Margin"
msgstr "Buscar margen"
#. module: sale_margin
#: view:report.account.invoice.product:0
msgid "This Year"
msgstr "Este año"
#. module: sale_margin
#: field:report.account.invoice.product,date:0
msgid "Date"
msgstr "Fecha"
#. module: sale_margin
#: selection:report.account.invoice.product,month:0
msgid "July"
msgstr "Julio"
#. module: sale_margin
#: view:report.account.invoice.product:0
msgid "Extended Filters..."
msgstr "Filtros extendidos..."
#. module: sale_margin
#: view:report.account.invoice.product:0
msgid "This Month"
msgstr "Este Mes"
#. module: sale_margin
#: view:report.account.invoice.product:0
#: field:report.account.invoice.product,day:0
msgid "Day"
msgstr "Día"
#. module: sale_margin
#: field:report.account.invoice.product,categ_id:0
msgid "Categories"
msgstr "Categorias"
#. module: sale_margin
#: field:account.invoice.line,cost_price:0
#: field:report.account.invoice.product,cost_price:0
#: field:sale.order.line,purchase_price:0
msgid "Cost Price"
msgstr "Precio coste"
#. module: sale_margin
#: selection:report.account.invoice.product,month:0
msgid "October"
msgstr "Octubre"
#. module: sale_margin
#: selection:report.account.invoice.product,month:0
msgid "January"
msgstr "Enero"
#. module: sale_margin
#: view:report.account.invoice.product:0
#: field:report.account.invoice.product,year:0
msgid "Year"
msgstr "Año"
#. module: sale_margin
#: selection:report.account.invoice.product,month:0
msgid "September"
msgstr "Septiembre"
#. module: sale_margin
#: selection:report.account.invoice.product,month:0
msgid "April"
msgstr "Abril"
#. module: sale_margin
#: field:report.account.invoice.product,amount:0
msgid "Amount"
msgstr "Importe"
#. module: sale_margin
#: selection:report.account.invoice.product,type:0
msgid "Supplier Refund"
msgstr "Factura de abono de proveedor"
#. module: sale_margin
#: selection:report.account.invoice.product,month:0
msgid "March"
msgstr "Marzo"
#. module: sale_margin
#: field:report.account.invoice.product,margin:0
#: field:sale.order,margin:0
#: field:sale.order.line,margin:0
msgid "Margin"
msgstr "Margen"
#. module: sale_margin
#: selection:report.account.invoice.product,month:0
msgid "November"
msgstr "Noviembre"
#. module: sale_margin
#: field:report.account.invoice.product,quantity:0
msgid "Quantity"
msgstr "Cantidad"
#. module: sale_margin
#: view:report.account.invoice.product:0
msgid "Invoices by product"
msgstr "Facturas por producto"
#. module: sale_margin
#: selection:report.account.invoice.product,type:0
msgid "Supplier Invoice"
msgstr "Factura de proveedor"
#. module: sale_margin
#: field:stock.picking,invoice_ids:0
msgid "Invoices"
msgstr "Facturas"
#. module: sale_margin
#: selection:report.account.invoice.product,month:0
msgid "December"
msgstr "Diciembre"
#. module: sale_margin
#: model:ir.model,name:sale_margin.model_account_invoice_line
msgid "Invoice Line"
msgstr "Línea de factura"
#. module: sale_margin
#: view:report.account.invoice.product:0
#: field:report.account.invoice.product,month:0
msgid "Month"
msgstr "Mes"
#. module: sale_margin
#: selection:report.account.invoice.product,state:0
msgid "Canceled"
msgstr "Cancelada"
#. module: sale_margin
#: model:ir.actions.act_window,help:sale_margin.action_report_account_invoice_report
msgid "This report gives you an overview of all the invoices generated by the system. You can sort and group your results by specific selection criteria to quickly find what you are looking for."
msgstr "Este informe ofrece una visión general de todas las facturas generadas por el sistema. Puede ordenar y agrupar los resultados por criterios específicos de selección para encontrar rápidamente lo que busca."
#. module: sale_margin
#: model:ir.ui.menu,name:sale_margin.menu_report_account_invoice_product
msgid "Invoice Report"
msgstr "Informe de factura"
#. module: sale_margin
#: view:report.account.invoice.product:0
msgid "Done"
msgstr "Realizado"
#. module: sale_margin
#: model:ir.ui.menu,name:sale_margin.menu_report_account_invoice_reoirt
msgid "Invoice"
msgstr "Factura"
#. module: sale_margin
#: view:stock.picking:0
msgid "Customer Invoices"
msgstr "Facturas de cliente"
#. module: sale_margin
#: view:report.account.invoice.product:0
#: field:report.account.invoice.product,partner_id:0
msgid "Partner"
msgstr "Socio"
#. module: sale_margin
#: model:ir.model,name:sale_margin.model_sale_order
msgid "Sales Order"
msgstr "Pedido de venta"
#. module: sale_margin
#: selection:report.account.invoice.product,state:0
msgid "Open"
msgstr "Abierta"
#. module: sale_margin
#: model:ir.actions.act_window,name:sale_margin.action_report_account_invoice_report
msgid "Invoice Analysis"
msgstr "Análisis de facturas"
#. module: sale_margin
#: model:ir.model,name:sale_margin.model_sale_order_line
msgid "Sales Order Line"
msgstr "Línea pedido de venta"

View File

@ -1,71 +0,0 @@
# Translation of OpenERP Server.
# This file contains the translation of the following modules:
# * sale_order_dates
#
msgid ""
msgstr ""
"Project-Id-Version: OpenERP Server 6.0.2\n"
"Report-Msgid-Bugs-To: support@openerp.com\n"
"POT-Creation-Date: 2011-07-05 16:02+0000\n"
"PO-Revision-Date: 2011-07-05 16:02+0000\n"
"Last-Translator: <>\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"
#. module: sale_order_dates
#: sql_constraint:sale.order:0
msgid "Order Reference must be unique !"
msgstr "¡La referencia del pedido debe ser única!"
#. module: sale_order_dates
#: help:sale.order,requested_date:0
msgid "Date on which customer has requested for sales."
msgstr "Fecha en que el cliente ha solicitado la venta."
#. module: sale_order_dates
#: field:sale.order,commitment_date:0
msgid "Commitment Date"
msgstr "Fecha compromiso"
#. module: sale_order_dates
#: field:sale.order,effective_date:0
msgid "Effective Date"
msgstr "Fecha efectiva"
#. module: sale_order_dates
#: model:ir.module.module,shortdesc:sale_order_dates.module_meta_information
msgid "Sales Order Dates"
msgstr "Fechas en pedidos de venta"
#. module: sale_order_dates
#: help:sale.order,effective_date:0
msgid "Date on which picking is created."
msgstr "Fecha en que la orden ha sido creada."
#. module: sale_order_dates
#: field:sale.order,requested_date:0
msgid "Requested Date"
msgstr "Fecha solicitud"
#. module: sale_order_dates
#: model:ir.model,name:sale_order_dates.model_sale_order
msgid "Sales Order"
msgstr "Pedido de venta"
#. module: sale_order_dates
#: model:ir.module.module,description:sale_order_dates.module_meta_information
msgid "\n"
"Add commitment, requested and effective dates on the sales order.\n"
""
msgstr "\n"
"Añade las fechas de compromiso, solicitada y efectiva en el pedido de venta.\n"
""
#. module: sale_order_dates
#: help:sale.order,commitment_date:0
msgid "Date on which delivery of products is to be made."
msgstr "Fecha en que la entrega de productos se va a realizar."

File diff suppressed because it is too large Load Diff