Merge lp:~mmakonnen/openobject-addons/point_of_sale_enhanced-70 into lp:openobject-addons/7.0
- point_of_sale_enhanced-70
- Merge into 7.0
Status: | Needs review |
---|---|
Proposed branch: | lp:~mmakonnen/openobject-addons/point_of_sale_enhanced-70 |
Merge into: | lp:openobject-addons/7.0 |
Diff against target: |
1181 lines (+810/-6) (has conflicts) 9 files modified
point_of_sale/controllers/main.py (+16/-0) point_of_sale/point_of_sale.py (+3/-1) point_of_sale/static/src/css/pos.css (+173/-0) point_of_sale/static/src/js/db.js (+72/-0) point_of_sale/static/src/js/devices.js (+179/-0) point_of_sale/static/src/js/models.js (+65/-4) point_of_sale/static/src/js/screens.js (+109/-0) point_of_sale/static/src/js/widgets.js (+113/-1) point_of_sale/static/src/xml/pos.xml (+80/-0) Text conflict in point_of_sale/static/src/js/db.js Text conflict in point_of_sale/static/src/js/models.js Text conflict in point_of_sale/static/src/js/screens.js |
To merge this branch: | bzr merge lp:~mmakonnen/openobject-addons/point_of_sale_enhanced-70 |
Related bugs: | |
Related blueprints: |
pos - save / search draft orders
(Undefined)
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
OpenERP Core Team | Pending | ||
Review via email: mp+151375@code.launchpad.net |
Commit message
Description of the change
Eloi-Minorisa (eloigf) wrote : | # |
> Hi everyone,
>
> How can this module be installed? I have a fresh installation of OpenERP 7 and
> I need to select a customer in POS. SO I found this module and I've tried to
> install it several times, but I can't.
> I can't find it in Modules list (I must be doing something wrong). Somewhere I
> read I should replace the original module with this one, but I didn't work.
> I guess it must be a very easy issue, but I'm unable to find it ou ... Sorry.
>
> Thanks in advance,
>
> Àlex
This module requires point_of_sale to be overriden.
But Thierry Godin has a better solution. Check out this module before.
http://
Alex (agg-bcn) wrote : | # |
Hi Minorisa,
thanks for your answer. I had already seen Thierry Godin solution, but I think it is in French (I'm not talking about the post, but the solution)
Thanks,
Àlex
Unmerged revisions
- 8522. By Michael Telahun Makonnen
-
In point_of_sale module add ability to select customer from the POS screen
Add a button in the upper right header which, when clicked, pops up a
dialog box to select a customer. You can search for a customer by
name, tin no, email, phone or mobile.Currently doesn't support adding a new customer from this screen. You
will have to leave the POS and add the customer in the normal way. - 8521. By Michael Telahun Makonnen
-
Add a keypad input device to the point of sale
It uses the numeric keypad on the keyboard to mimic the keypad found on
most cash registers. The numbers are used to match a product by its code,
and the non-numeric keys are used as modifiers:
'/' - X Quantity
'*' - AMT Manual Price Override
'-' - % - Discount %
'+' - PLU (Product Code)
Keyboard "Back Space" and "Delete" keys may be used to clear the buffer.It may be useful to tape over the modifier keys with the appropriate
symbols to make it easier for the cashiers to get used to it.Implementation Notes:
-------------- -------
- By necessity the product code may contain only numeric identifiers
- Doesn't emulate VOID key (using Enter key would interfere with
bar code reader)
- Doesn't emulate surcharge key (% +)Some examples of usage showing what the operation would look like
on a regular Cash Register and using the numeric keypad:1. Add a product
(code: 100, quantity: 1, Price:list price, no discount)
Cash Register: 100 [PLU]
Numeric Keypad: 100 +2. Add 9 pieces of a product
(code: 102, quantity: 9, Price: list price, no discount)
Cash Register: 9 [ X ] 102 [PLU]
Numeric Keypad: 9 / 102 +3. Add 3 pieces of a product and set the price at 9.99
(code: 450, quantity: 3, Price: 9.99, no discount)
Cash Register: 3 [ X ] 9.99 [AMT] 450 [PLU]
Numeric Keypad: 3 / 9.99 * 450 +4. Add 5 pieces of a product, set price to 23.50, and discount it by 10%
(code: 300, quantity: 5, price: 23.50, discount: 10%)
Cash Register: 5 [ X ] 23.50 [AMT] 10 [% -] 300 [PLU]
Numeric Keypad: 5 / 23.50 * 10 - 300 + - 8520. By Michael Telahun Makonnen
-
POS: When we hit [Enter] in the search box add the product with the matching product code
The previous commit only added the product if it was the only one returned by the search.
Now, it will search the list of matched products for one with the same exact
product code as the search term. If it finds a match it will add it to the order. - 8519. By Michael Telahun Makonnen
-
POS: hitting [Enter] in the search field will add the product to the order
- 8518. By Michael Telahun Makonnen
-
In POS allow searching by product code as well
- 8517. By Michael Telahun Makonnen
-
In the POS module show the product code before the price
If you have a lot of similarly (identicaly) named products it becomes difficult to
tell them apart in the POS unless you can also see their code.
Preview Diff
1 | === modified file 'point_of_sale/controllers/main.py' | |||
2 | --- point_of_sale/controllers/main.py 2012-11-29 22:26:45 +0000 | |||
3 | +++ point_of_sale/controllers/main.py 2013-03-03 00:11:29 +0000 | |||
4 | @@ -84,6 +84,22 @@ | |||
5 | 84 | return | 84 | return |
6 | 85 | 85 | ||
7 | 86 | @openerp.addons.web.http.jsonrequest | 86 | @openerp.addons.web.http.jsonrequest |
8 | 87 | def keypad_item_success(self, request, data): | ||
9 | 88 | """ | ||
10 | 89 | A product has been entered by keypad with success | ||
11 | 90 | """ | ||
12 | 91 | print 'keypad_item_success: ' + str(data) | ||
13 | 92 | return | ||
14 | 93 | |||
15 | 94 | @openerp.addons.web.http.jsonrequest | ||
16 | 95 | def keypad_item_error_unrecognized(self, request, data): | ||
17 | 96 | """ | ||
18 | 97 | A product has been entered by keypad without success | ||
19 | 98 | """ | ||
20 | 99 | print 'keypad_item_error_unrecognized: ' + str(data) | ||
21 | 100 | return | ||
22 | 101 | |||
23 | 102 | @openerp.addons.web.http.jsonrequest | ||
24 | 87 | def help_needed(self, request): | 103 | def help_needed(self, request): |
25 | 88 | """ | 104 | """ |
26 | 89 | The user wants an help (ex: light is on) | 105 | The user wants an help (ex: light is on) |
27 | 90 | 106 | ||
28 | === modified file 'point_of_sale/point_of_sale.py' | |||
29 | --- point_of_sale/point_of_sale.py 2013-02-27 10:45:05 +0000 | |||
30 | +++ point_of_sale/point_of_sale.py 2013-03-03 00:11:29 +0000 | |||
31 | @@ -484,12 +484,14 @@ | |||
32 | 484 | order_ids = [] | 484 | order_ids = [] |
33 | 485 | for tmp_order in orders: | 485 | for tmp_order in orders: |
34 | 486 | order = tmp_order['data'] | 486 | order = tmp_order['data'] |
35 | 487 | partner_id = order['partner_id'] if 'partner_id' in order else False | ||
36 | 487 | order_id = self.create(cr, uid, { | 488 | order_id = self.create(cr, uid, { |
37 | 488 | 'name': order['name'], | 489 | 'name': order['name'], |
38 | 489 | 'user_id': order['user_id'] or False, | 490 | 'user_id': order['user_id'] or False, |
39 | 490 | 'session_id': order['pos_session_id'], | 491 | 'session_id': order['pos_session_id'], |
40 | 491 | 'lines': order['lines'], | 492 | 'lines': order['lines'], |
42 | 492 | 'pos_reference':order['name'] | 493 | 'pos_reference':order['name'], |
43 | 494 | 'partner_id':partner_id, | ||
44 | 493 | }, context) | 495 | }, context) |
45 | 494 | 496 | ||
46 | 495 | for payments in order['statement_ids']: | 497 | for payments in order['statement_ids']: |
47 | 496 | 498 | ||
48 | === modified file 'point_of_sale/static/src/css/pos.css' | |||
49 | --- point_of_sale/static/src/css/pos.css 2013-01-29 15:24:21 +0000 | |||
50 | +++ point_of_sale/static/src/css/pos.css 2013-03-03 00:11:29 +0000 | |||
51 | @@ -194,6 +194,15 @@ | |||
52 | 194 | font-weight: 900; | 194 | font-weight: 900; |
53 | 195 | } | 195 | } |
54 | 196 | 196 | ||
55 | 197 | .point-of-sale #rightheader .customername{ | ||
56 | 198 | float:right; | ||
57 | 199 | color:#DDD; | ||
58 | 200 | font-size:16px; | ||
59 | 201 | margin-right:32px; | ||
60 | 202 | margin-top:10px; | ||
61 | 203 | font-style:italic; | ||
62 | 204 | } | ||
63 | 205 | |||
64 | 197 | /* c) The session buttons */ | 206 | /* c) The session buttons */ |
65 | 198 | 207 | ||
66 | 199 | .point-of-sale #rightheader .header-button{ | 208 | .point-of-sale #rightheader .header-button{ |
67 | @@ -585,6 +594,15 @@ | |||
68 | 585 | max-width: 120px; | 594 | max-width: 120px; |
69 | 586 | } | 595 | } |
70 | 587 | 596 | ||
71 | 597 | .point-of-sale .product .code-tag { | ||
72 | 598 | position: absolute; | ||
73 | 599 | top: 2px; | ||
74 | 600 | left: 2px; | ||
75 | 601 | vertical-align: top; | ||
76 | 602 | line-height: 13px; | ||
77 | 603 | padding: 2px 5px; | ||
78 | 604 | } | ||
79 | 605 | |||
80 | 588 | .point-of-sale .product .price-tag { | 606 | .point-of-sale .product .price-tag { |
81 | 589 | position: absolute; | 607 | position: absolute; |
82 | 590 | top: 2px; | 608 | top: 2px; |
83 | @@ -1346,6 +1364,161 @@ | |||
84 | 1346 | line-height:180px; | 1364 | line-height:180px; |
85 | 1347 | } | 1365 | } |
86 | 1348 | 1366 | ||
87 | 1367 | .point-of-sale .modal-dialog .popup-selection{ | ||
88 | 1368 | position: absolute; | ||
89 | 1369 | left:30%; | ||
90 | 1370 | top:10%; | ||
91 | 1371 | width: 700px; | ||
92 | 1372 | height:70%; | ||
93 | 1373 | padding:10px; | ||
94 | 1374 | padding-top:20px; | ||
95 | 1375 | text-align:left; | ||
96 | 1376 | font-size:14px; | ||
97 | 1377 | font-weight:bold; | ||
98 | 1378 | background-color: #F0EEEE; | ||
99 | 1379 | border-radius: 4px; | ||
100 | 1380 | box-shadow: 0px -1px white, 0px 1px white, 0px 4px #949494, 0px 10px 20px rgba(0, 0, 0, 0.3); | ||
101 | 1381 | z-index:1200; | ||
102 | 1382 | } | ||
103 | 1383 | |||
104 | 1384 | .point-of-sale .popup-selection .button{ | ||
105 | 1385 | float:right; | ||
106 | 1386 | width: 110px; | ||
107 | 1387 | height: 40px; | ||
108 | 1388 | line-height:40px; | ||
109 | 1389 | text-align:center; | ||
110 | 1390 | margin:3px; | ||
111 | 1391 | margin-top:10px; | ||
112 | 1392 | margin-right:50px; | ||
113 | 1393 | |||
114 | 1394 | font-size: 14px; | ||
115 | 1395 | font-weight: bold; | ||
116 | 1396 | |||
117 | 1397 | cursor: pointer; | ||
118 | 1398 | |||
119 | 1399 | border: 1px solid #cacaca; | ||
120 | 1400 | border-radius: 4px; | ||
121 | 1401 | |||
122 | 1402 | background: #e2e2e2; | ||
123 | 1403 | background: -webkit-linear-gradient(#f0f0f0, #e2e2e2); | ||
124 | 1404 | background: -moz-linear-gradient(#f0f0f0, #e2e2e2); | ||
125 | 1405 | background: -ms-linear-gradient(#f0f0f0, #e2e2e2); | ||
126 | 1406 | background: linear-gradient(#f0f0f0, #e2e2e2); | ||
127 | 1407 | -webkit-box-shadow: 0px 2px 2px rgba(0,0,0, 0.3); | ||
128 | 1408 | -moz-box-shadow: 0px 2px 2px rgba(0,0,0, 0.3); | ||
129 | 1409 | box-shadow: 0px 2px 2px rgba(0,0,0, 0.3); | ||
130 | 1410 | } | ||
131 | 1411 | .point-of-sale .popup-selection .button:hover { | ||
132 | 1412 | color: white; | ||
133 | 1413 | background: #7f82ac; | ||
134 | 1414 | border: 1px solid #7f82ac; | ||
135 | 1415 | background: -webkit-linear-gradient(#9d9fc5, #7f82ac); | ||
136 | 1416 | background: -moz-linear-gradient(#9d9fc5, #7f82ac); | ||
137 | 1417 | background: -ms-linear-gradient(#9d9fc5, #7f82ac); | ||
138 | 1418 | background: linear-gradient(#9d9fc5, #7f82ac); | ||
139 | 1419 | |||
140 | 1420 | -webkit-transition-property: background, border; | ||
141 | 1421 | -webkit-transition-duration: 0.2s; | ||
142 | 1422 | -webkit-transition-timing-function: ease-out; | ||
143 | 1423 | } | ||
144 | 1424 | |||
145 | 1425 | /* customer selection */ | ||
146 | 1426 | |||
147 | 1427 | #customer-cancel { | ||
148 | 1428 | position: absolute; | ||
149 | 1429 | left: 10px; | ||
150 | 1430 | bottom: 10px; | ||
151 | 1431 | } | ||
152 | 1432 | |||
153 | 1433 | .point-of-sale .customer-list-container { | ||
154 | 1434 | position: absolute; | ||
155 | 1435 | top:70px; | ||
156 | 1436 | left:5px; | ||
157 | 1437 | right:5px; | ||
158 | 1438 | bottom: 70px; | ||
159 | 1439 | text-align: left; | ||
160 | 1440 | overflow: auto; | ||
161 | 1441 | } | ||
162 | 1442 | |||
163 | 1443 | .point-of-sale .customer-list-scroller { | ||
164 | 1444 | -webkit-box-sizing: border-box; | ||
165 | 1445 | -moz-box-sizing: border-box; | ||
166 | 1446 | -ms-box-sizing: border-box; | ||
167 | 1447 | box-sizing: border-box; | ||
168 | 1448 | width:98%; | ||
169 | 1449 | } | ||
170 | 1450 | |||
171 | 1451 | .customer-list-scroller ol { | ||
172 | 1452 | list-style: none; | ||
173 | 1453 | } | ||
174 | 1454 | |||
175 | 1455 | .customer-list-scroller ol li { } | ||
176 | 1456 | |||
177 | 1457 | .customer-list-scroller ol li a { | ||
178 | 1458 | display:block; | ||
179 | 1459 | text-decoration:none; | ||
180 | 1460 | color:#000000; | ||
181 | 1461 | background-color:#FFFFFF; | ||
182 | 1462 | line-height:20px; | ||
183 | 1463 | border-bottom-style:solid; | ||
184 | 1464 | border-bottom-width:1px; | ||
185 | 1465 | border-bottom-color:#CCCCCC; | ||
186 | 1466 | padding-left:10px; | ||
187 | 1467 | cursor:pointer; | ||
188 | 1468 | } | ||
189 | 1469 | |||
190 | 1470 | .customer-list-scroller ol li a:hover { | ||
191 | 1471 | color:#FFFFFF; | ||
192 | 1472 | background-image:url(/point_of_sale/static/src/img/hover.png); | ||
193 | 1473 | background-repeat:repeat-x; | ||
194 | 1474 | } | ||
195 | 1475 | |||
196 | 1476 | .point-of-sale .customer { | ||
197 | 1477 | width: 100%; | ||
198 | 1478 | } | ||
199 | 1479 | |||
200 | 1480 | .point-of-sale .customer a { | ||
201 | 1481 | width: 100%; | ||
202 | 1482 | } | ||
203 | 1483 | |||
204 | 1484 | .point-of-sale .customer .customer-field { | ||
205 | 1485 | width: 130px; | ||
206 | 1486 | padding: 5px; | ||
207 | 1487 | margin: 5px; | ||
208 | 1488 | } | ||
209 | 1489 | |||
210 | 1490 | .point-of-sale .customer .customer-name{ | ||
211 | 1491 | width: 200px; | ||
212 | 1492 | padding: 5px; | ||
213 | 1493 | margin: 5px; | ||
214 | 1494 | } | ||
215 | 1495 | |||
216 | 1496 | .point-of-sale .customer .customer-phone { | ||
217 | 1497 | width: 50px; | ||
218 | 1498 | padding: 5px; | ||
219 | 1499 | margin: 5px; | ||
220 | 1500 | } | ||
221 | 1501 | |||
222 | 1502 | .point-of-sale .customer-searchbox { | ||
223 | 1503 | right: 2px; | ||
224 | 1504 | } | ||
225 | 1505 | .point-of-sale .customer-searchbox input { | ||
226 | 1506 | width: 130px; | ||
227 | 1507 | border-radius: 11px; | ||
228 | 1508 | border: 1px solid #cecbcb; | ||
229 | 1509 | padding: 3px 19px; | ||
230 | 1510 | margin: 6px; | ||
231 | 1511 | background: url("../img/search.png") no-repeat 5px; | ||
232 | 1512 | background-color: white; | ||
233 | 1513 | } | ||
234 | 1514 | .point-of-sale .customer-search-clear { | ||
235 | 1515 | postion: absolute; | ||
236 | 1516 | top: 11px; | ||
237 | 1517 | right: 600px; | ||
238 | 1518 | cursor: pointer; | ||
239 | 1519 | display: none; | ||
240 | 1520 | } | ||
241 | 1521 | |||
242 | 1349 | /* ********* The ScrollBarWidget ********* */ | 1522 | /* ********* The ScrollBarWidget ********* */ |
243 | 1350 | 1523 | ||
244 | 1351 | .point-of-sale .scrollbar{ | 1524 | .point-of-sale .scrollbar{ |
245 | 1352 | 1525 | ||
246 | === added file 'point_of_sale/static/src/img/hover.png' | |||
247 | 1353 | Binary files point_of_sale/static/src/img/hover.png 1970-01-01 00:00:00 +0000 and point_of_sale/static/src/img/hover.png 2013-03-03 00:11:29 +0000 differ | 1526 | Binary files point_of_sale/static/src/img/hover.png 1970-01-01 00:00:00 +0000 and point_of_sale/static/src/img/hover.png 2013-03-03 00:11:29 +0000 differ |
248 | === modified file 'point_of_sale/static/src/js/db.js' | |||
249 | --- point_of_sale/static/src/js/db.js 2013-01-24 14:06:31 +0000 | |||
250 | +++ point_of_sale/static/src/js/db.js 2013-03-03 00:11:29 +0000 | |||
251 | @@ -53,7 +53,11 @@ | |||
252 | 53 | this.category_search_string = {}; | 53 | this.category_search_string = {}; |
253 | 54 | this.packagings_by_id = {}; | 54 | this.packagings_by_id = {}; |
254 | 55 | this.packagings_by_product_id = {}; | 55 | this.packagings_by_product_id = {}; |
255 | 56 | <<<<<<< TREE | ||
256 | 56 | this.packagings_by_ean13 = {}; | 57 | this.packagings_by_ean13 = {}; |
257 | 58 | ======= | ||
258 | 59 | this.customer_list_search_strings = ''; | ||
259 | 60 | >>>>>>> MERGE-SOURCE | ||
260 | 57 | }, | 61 | }, |
261 | 58 | /* returns the category object from its id. If you pass a list of id as parameters, you get | 62 | /* returns the category object from its id. If you pass a list of id as parameters, you get |
262 | 59 | * a list of category objects. | 63 | * a list of category objects. |
263 | @@ -150,6 +154,9 @@ | |||
264 | 150 | if(product.ean13){ | 154 | if(product.ean13){ |
265 | 151 | str += '|' + product.ean13; | 155 | str += '|' + product.ean13; |
266 | 152 | } | 156 | } |
267 | 157 | if(product.code){ | ||
268 | 158 | str += '|' + product.code; | ||
269 | 159 | } | ||
270 | 153 | var packagings = this.packagings_by_product_id[product.id] || []; | 160 | var packagings = this.packagings_by_product_id[product.id] || []; |
271 | 154 | for(var i = 0; i < packagings.length; i++){ | 161 | for(var i = 0; i < packagings.length; i++){ |
272 | 155 | str += '|' + packagings[i].ean; | 162 | str += '|' + packagings[i].ean; |
273 | @@ -209,6 +216,35 @@ | |||
274 | 209 | } | 216 | } |
275 | 210 | } | 217 | } |
276 | 211 | }, | 218 | }, |
277 | 219 | _customer_search_string: function(customer){ | ||
278 | 220 | var str = '' + customer.id + ':' + customer.name; | ||
279 | 221 | if(customer.vat){ | ||
280 | 222 | str += '|' + customer.vat; | ||
281 | 223 | } | ||
282 | 224 | if(customer.email){ | ||
283 | 225 | str += '|' + customer.email; | ||
284 | 226 | } | ||
285 | 227 | if(customer.phone){ | ||
286 | 228 | str += '|' + customer.phone; | ||
287 | 229 | } | ||
288 | 230 | if(customer.mobile){ | ||
289 | 231 | str += '|' + customer.mobile; | ||
290 | 232 | } | ||
291 | 233 | return str + '\n'; | ||
292 | 234 | }, | ||
293 | 235 | add_customers: function(customers){ | ||
294 | 236 | var stored_customers = this.load('customers',{}); | ||
295 | 237 | |||
296 | 238 | if(!customers instanceof Array){ | ||
297 | 239 | customers = [customers]; | ||
298 | 240 | } | ||
299 | 241 | for(var i = 0, len = customers.length; i < len; i++){ | ||
300 | 242 | var c = customers[i]; | ||
301 | 243 | this.customer_list_search_strings += this._customer_search_string(c); | ||
302 | 244 | stored_customers[c.id] = c; | ||
303 | 245 | } | ||
304 | 246 | this.save('customers',stored_customers); | ||
305 | 247 | }, | ||
306 | 212 | /* removes all the data from the database. TODO : being able to selectively remove data */ | 248 | /* removes all the data from the database. TODO : being able to selectively remove data */ |
307 | 213 | clear: function(stores){ | 249 | clear: function(stores){ |
308 | 214 | for(var i = 0, len = arguments.length; i < len; i++){ | 250 | for(var i = 0, len = arguments.length; i < len; i++){ |
309 | @@ -238,6 +274,14 @@ | |||
310 | 238 | } | 274 | } |
311 | 239 | return undefined; | 275 | return undefined; |
312 | 240 | }, | 276 | }, |
313 | 277 | get_product_by_code: function(code){ | ||
314 | 278 | var products = this.load('products', {}); | ||
315 | 279 | for(var i in products){ | ||
316 | 280 | if(products[i] && products[i].code === code){ | ||
317 | 281 | return products[i]; | ||
318 | 282 | } | ||
319 | 283 | } | ||
320 | 284 | }, | ||
321 | 241 | get_product_by_category: function(category_id){ | 285 | get_product_by_category: function(category_id){ |
322 | 242 | var product_ids = this.product_by_category_id[category_id]; | 286 | var product_ids = this.product_by_category_id[category_id]; |
323 | 243 | var list = []; | 287 | var list = []; |
324 | @@ -248,6 +292,17 @@ | |||
325 | 248 | } | 292 | } |
326 | 249 | return list; | 293 | return list; |
327 | 250 | }, | 294 | }, |
328 | 295 | get_customer_by_id: function(id){ | ||
329 | 296 | return this.load('customers',{})[id]; | ||
330 | 297 | }, | ||
331 | 298 | get_all_customers: function(){ | ||
332 | 299 | list = []; | ||
333 | 300 | stored_customers = this.load('customers',{}); | ||
334 | 301 | for (var i in stored_customers) { | ||
335 | 302 | list.push(stored_customers[i]); | ||
336 | 303 | } | ||
337 | 304 | return list; | ||
338 | 305 | }, | ||
339 | 251 | /* returns a list of products with : | 306 | /* returns a list of products with : |
340 | 252 | * - a category that is or is a child of category_id, | 307 | * - a category that is or is a child of category_id, |
341 | 253 | * - a name, package or ean13 containing the query (case insensitive) | 308 | * - a name, package or ean13 containing the query (case insensitive) |
342 | @@ -266,6 +321,23 @@ | |||
343 | 266 | } | 321 | } |
344 | 267 | return results; | 322 | return results; |
345 | 268 | }, | 323 | }, |
346 | 324 | /* returns a list of customers with : | ||
347 | 325 | * - a name, TIN, email, or phone/mobile containing the query (case insensitive) | ||
348 | 326 | */ | ||
349 | 327 | search_customers: function(query){ | ||
350 | 328 | var re = RegExp("([0-9]+):.*?"+query,"gi"); | ||
351 | 329 | var results = []; | ||
352 | 330 | for(var i = 0; i < this.limit; i++){ | ||
353 | 331 | r = re.exec(this.customer_list_search_strings); | ||
354 | 332 | if(r){ | ||
355 | 333 | var id = Number(r[1]); | ||
356 | 334 | results.push(this.get_customer_by_id(id)); | ||
357 | 335 | }else{ | ||
358 | 336 | break; | ||
359 | 337 | } | ||
360 | 338 | } | ||
361 | 339 | return results; | ||
362 | 340 | }, | ||
363 | 269 | add_order: function(order){ | 341 | add_order: function(order){ |
364 | 270 | var last_id = this.load('last_order_id',0); | 342 | var last_id = this.load('last_order_id',0); |
365 | 271 | var orders = this.load('orders',[]); | 343 | var orders = this.load('orders',[]); |
366 | 272 | 344 | ||
367 | === modified file 'point_of_sale/static/src/js/devices.js' | |||
368 | --- point_of_sale/static/src/js/devices.js 2013-01-11 14:42:55 +0000 | |||
369 | +++ point_of_sale/static/src/js/devices.js 2013-03-03 00:11:29 +0000 | |||
370 | @@ -66,6 +66,18 @@ | |||
371 | 66 | return this.message('scan_item_error_unrecognized',{ean: ean}); | 66 | return this.message('scan_item_error_unrecognized',{ean: ean}); |
372 | 67 | }, | 67 | }, |
373 | 68 | 68 | ||
374 | 69 | //a product has been entered by keypad and recognized with success | ||
375 | 70 | // data is a parsed {product.code,qty,price} object | ||
376 | 71 | keypad_item_success: function(data){ | ||
377 | 72 | return this.message('keypad_item_success',{data: data}); | ||
378 | 73 | }, | ||
379 | 74 | |||
380 | 75 | // a product has been entered by keypad but not recognized | ||
381 | 76 | // data is a parsed {product.code,qty,price} object | ||
382 | 77 | keypad_item_error_unrecognized: function(data){ | ||
383 | 78 | return this.message('keypad_item_error_unrecognized',{data: data}); | ||
384 | 79 | }, | ||
385 | 80 | |||
386 | 69 | //the client is asking for help | 81 | //the client is asking for help |
387 | 70 | help_needed: function(){ | 82 | help_needed: function(){ |
388 | 71 | return this.message('help_needed'); | 83 | return this.message('help_needed'); |
389 | @@ -472,5 +484,172 @@ | |||
390 | 472 | $('body').undelegate('', 'keyup') | 484 | $('body').undelegate('', 'keyup') |
391 | 473 | }, | 485 | }, |
392 | 474 | }); | 486 | }); |
393 | 487 | |||
394 | 488 | // this module mimics a keypad-only cash register. Use connect() and | ||
395 | 489 | // disconnect() to activate and deactivate it. Use set_action_callback to | ||
396 | 490 | // tell it what to do when the cashier enters product data(qty, price, etc). | ||
397 | 491 | module.Keypad = instance.web.Class.extend({ | ||
398 | 492 | init: function(attributes){ | ||
399 | 493 | this.pos = attributes.pos; | ||
400 | 494 | this.action_callback = undefined; | ||
401 | 495 | this.saved_callback_stack = []; | ||
402 | 496 | }, | ||
403 | 497 | |||
404 | 498 | save_callback: function(){ | ||
405 | 499 | this.saved_callback_stack.push(this.action_callback); | ||
406 | 500 | }, | ||
407 | 501 | |||
408 | 502 | restore_callback: function(){ | ||
409 | 503 | if (this.saved_callback_stack.length > 0) { | ||
410 | 504 | this.action_callback = this.saved_callback_stack.pop(); | ||
411 | 505 | } | ||
412 | 506 | }, | ||
413 | 507 | |||
414 | 508 | set_action_callback: function(callback){ | ||
415 | 509 | this.action_callback = callback | ||
416 | 510 | }, | ||
417 | 511 | |||
418 | 512 | //remove action callback | ||
419 | 513 | reset_action_callback: function(){ | ||
420 | 514 | this.action_callback = undefined; | ||
421 | 515 | }, | ||
422 | 516 | |||
423 | 517 | reset_parse_result: function(parse_result) { | ||
424 | 518 | parse_result.code = 0; | ||
425 | 519 | parse_result.qty = 0; | ||
426 | 520 | parse_result.priceOverride = false; | ||
427 | 521 | parse_result.price = 0.00; | ||
428 | 522 | parse_result.discount = 0.00; | ||
429 | 523 | parse_result.void_last_line = false; | ||
430 | 524 | }, | ||
431 | 525 | |||
432 | 526 | copy_parse_result: function(src) { | ||
433 | 527 | var dst = { | ||
434 | 528 | code: null, | ||
435 | 529 | qty: 1, | ||
436 | 530 | priceOverride: false, | ||
437 | 531 | price: 0.00, | ||
438 | 532 | discount: 0.00, | ||
439 | 533 | void_last_line: false, | ||
440 | 534 | }; | ||
441 | 535 | dst.code = src.code; | ||
442 | 536 | dst.qty = src.qty; | ||
443 | 537 | dst.priceOverride = src.priceOverride; | ||
444 | 538 | dst.price = src.price; | ||
445 | 539 | dst.discount = src.discount; | ||
446 | 540 | dst.void_last_line = src.void_last_line; | ||
447 | 541 | return dst; | ||
448 | 542 | }, | ||
449 | 543 | |||
450 | 544 | // starts catching keyboard events and tries to interpret keystrokes, | ||
451 | 545 | // calling the callback when needed. | ||
452 | 546 | connect: function(){ | ||
453 | 547 | var self = this; | ||
454 | 548 | var KC_PLU = 107; // KeyCode: Product Code (Keypad '+') | ||
455 | 549 | var KC_QTY = 111; // KeyCode: Quantity (Keypad '/') | ||
456 | 550 | var KC_AMT = 106; // KeyCode: Price (Keypad '*') | ||
457 | 551 | var KC_DISC = 109; // KeyCode: Discount Percentage [0..100] (Keypad '-') | ||
458 | 552 | var KC_VOID = 13; // KeyCode: Void current line (Keyboard/Keypad Enter key) | ||
459 | 553 | var KC_CLR1 = 46; // KeyCode: Clear last line of order (Keyboard Delete key) | ||
460 | 554 | var KC_CLR2 = 8; // KeyCode: Clear current line (Keyboard Backspace key) | ||
461 | 555 | var codeNumbers = []; | ||
462 | 556 | var codeChars = []; | ||
463 | 557 | var parse_result = { | ||
464 | 558 | code: null, | ||
465 | 559 | qty: 1, | ||
466 | 560 | priceOverride: false, | ||
467 | 561 | price: 0.00, | ||
468 | 562 | discount: 0.00, | ||
469 | 563 | void_last_line: false, | ||
470 | 564 | }; | ||
471 | 565 | var kc_lookup = { | ||
472 | 566 | 96: '0', | ||
473 | 567 | 97: '1', | ||
474 | 568 | 98: '2', | ||
475 | 569 | 99: '3', | ||
476 | 570 | 100: '4', | ||
477 | 571 | 101: '5', | ||
478 | 572 | 102: '6', | ||
479 | 573 | 103: '7', | ||
480 | 574 | 104: '8', | ||
481 | 575 | 105: '9', | ||
482 | 576 | 106: '*', | ||
483 | 577 | 107: '+', | ||
484 | 578 | 109: '-', | ||
485 | 579 | 110: '.', | ||
486 | 580 | 111: '/', | ||
487 | 581 | }; | ||
488 | 582 | |||
489 | 583 | // Catch keyup events anywhere in the POS interface. Barcode reader also does this, but won't interfere | ||
490 | 584 | // because it looks for a specific timing between keyup events. On the plus side this should mean that you | ||
491 | 585 | // can use both the keypad and the barcode reader during the same session (but for separate order lines). | ||
492 | 586 | // This could be useful in cases where the scanner can't read the barcode. | ||
493 | 587 | $('body').delegate('','keyup', function (e){ | ||
494 | 588 | console.log('keyup:'+String.fromCharCode(e.keyCode)+' '+e.keyCode,e); | ||
495 | 589 | //We only care about numbers and modifiers | ||
496 | 590 | token = e.keyCode; | ||
497 | 591 | if ((token >= 96 && token <= 111) || token === KC_PLU || token === KC_QTY || token === KC_AMT) { | ||
498 | 592 | |||
499 | 593 | if (token === KC_PLU) { | ||
500 | 594 | parse_result.code = codeChars.join(''); | ||
501 | 595 | var res = self.copy_parse_result(parse_result); | ||
502 | 596 | codeNumbers = []; | ||
503 | 597 | codeChars = []; | ||
504 | 598 | self.reset_parse_result(parse_result); | ||
505 | 599 | console.log('PLU token: code:'+res.code+', qty:'+res.qty+', price:'+res.price+', discount:'+res.discount); | ||
506 | 600 | self.action_callback(res); | ||
507 | 601 | } else if (token === KC_QTY) { | ||
508 | 602 | parse_result.qty = parseInt(codeChars.join('')); | ||
509 | 603 | codeNumbers = []; | ||
510 | 604 | codeChars = []; | ||
511 | 605 | console.log('QTY token: qty:'+parse_result.qty); | ||
512 | 606 | } else if (token === KC_AMT) { | ||
513 | 607 | parse_result.price = parseFloat(codeChars.join('')).toFixed(2); | ||
514 | 608 | parse_result.priceOverride = true; | ||
515 | 609 | codeNumbers = []; | ||
516 | 610 | codeChars = []; | ||
517 | 611 | console.log('AMT token: price:'+parse_result.price); | ||
518 | 612 | } else if (token === KC_DISC) { | ||
519 | 613 | parse_result.discount = parseFloat(codeChars.join('')); | ||
520 | 614 | codeNumbers = []; | ||
521 | 615 | codeChars = []; | ||
522 | 616 | console.log('DISC token: discount:'+parse_result.discount); | ||
523 | 617 | } else { | ||
524 | 618 | codeNumbers.push(token - 48); | ||
525 | 619 | codeChars.push(kc_lookup[token]); | ||
526 | 620 | } | ||
527 | 621 | } else if (token === KC_VOID) { | ||
528 | 622 | /* | ||
529 | 623 | * This is commented out for now. We don't want to interfere with | ||
530 | 624 | * the 'Enter' keycode used by the barcode reader to signify a scan. | ||
531 | 625 | */ | ||
532 | 626 | // Void the last line of the order only if there isn't another line in pregress. | ||
533 | 627 | // if (codeNumbers.length === 0) { | ||
534 | 628 | // parse_result.void_last_line = true; | ||
535 | 629 | // var res = self.copy_parse_result(parse_result); | ||
536 | 630 | // codeNumbers = []; | ||
537 | 631 | // codeChars = []; | ||
538 | 632 | // self.reset_parse_result(parse_result); | ||
539 | 633 | // console.log('VOID token:'+res.void_last_line); | ||
540 | 634 | // self.action_callback(res); | ||
541 | 635 | // } | ||
542 | 636 | } else { | ||
543 | 637 | // For now pressing Backspace or Delete just defaults to doing nothing. | ||
544 | 638 | // In the future we might want it to display a popup or something. | ||
545 | 639 | if (token === KC_CLR1 || token === KC_CLR2) { | ||
546 | 640 | ; | ||
547 | 641 | } | ||
548 | 642 | codeNumbers = []; | ||
549 | 643 | codeChars = []; | ||
550 | 644 | self.reset_parse_result(parse_result); | ||
551 | 645 | } | ||
552 | 646 | }); | ||
553 | 647 | }, | ||
554 | 648 | |||
555 | 649 | // stops catching keyboard events | ||
556 | 650 | disconnect: function(){ | ||
557 | 651 | $('body').undelegate('', 'keyup') | ||
558 | 652 | }, | ||
559 | 653 | }); | ||
560 | 475 | 654 | ||
561 | 476 | } | 655 | } |
562 | 477 | 656 | ||
563 | === modified file 'point_of_sale/static/src/js/models.js' | |||
564 | --- point_of_sale/static/src/js/models.js 2013-01-28 17:18:00 +0000 | |||
565 | +++ point_of_sale/static/src/js/models.js 2013-03-03 00:11:29 +0000 | |||
566 | @@ -39,10 +39,17 @@ | |||
567 | 39 | this.flush_mutex = new $.Mutex(); // used to make sure the orders are sent to the server once at time | 39 | this.flush_mutex = new $.Mutex(); // used to make sure the orders are sent to the server once at time |
568 | 40 | 40 | ||
569 | 41 | this.barcode_reader = new module.BarcodeReader({'pos': this}); // used to read barcodes | 41 | this.barcode_reader = new module.BarcodeReader({'pos': this}); // used to read barcodes |
570 | 42 | this.keypad = new module.Keypad({'pos': this}); // used to simulate a cash register keypad | ||
571 | 42 | this.proxy = new module.ProxyDevice(); // used to communicate to the hardware devices via a local proxy | 43 | this.proxy = new module.ProxyDevice(); // used to communicate to the hardware devices via a local proxy |
572 | 43 | this.db = new module.PosLS(); // a database used to store the products and categories | 44 | this.db = new module.PosLS(); // a database used to store the products and categories |
573 | 45 | <<<<<<< TREE | ||
574 | 44 | this.db.clear('products','categories'); | 46 | this.db.clear('products','categories'); |
575 | 45 | this.debug = jQuery.deparam(jQuery.param.querystring()).debug !== undefined; //debug mode | 47 | this.debug = jQuery.deparam(jQuery.param.querystring()).debug !== undefined; //debug mode |
576 | 48 | ======= | ||
577 | 49 | this.db.clear('products','categories','customers'); | ||
578 | 50 | this.debug = jQuery.deparam(jQuery.param.querystring()).debug !== undefined; //debug mode | ||
579 | 51 | |||
580 | 52 | >>>>>>> MERGE-SOURCE | ||
581 | 46 | 53 | ||
582 | 47 | // default attributes values. If null, it will be loaded below. | 54 | // default attributes values. If null, it will be loaded below. |
583 | 48 | this.set({ | 55 | this.set({ |
584 | @@ -59,6 +66,7 @@ | |||
585 | 59 | 'orders': new module.OrderCollection(), | 66 | 'orders': new module.OrderCollection(), |
586 | 60 | //this is the product list as seen by the product list widgets, it will change based on the category filters | 67 | //this is the product list as seen by the product list widgets, it will change based on the category filters |
587 | 61 | 'products': new module.ProductCollection(), | 68 | 'products': new module.ProductCollection(), |
588 | 69 | 'customers': new module.CustomerCollection(), | ||
589 | 62 | 'cashRegisters': null, | 70 | 'cashRegisters': null, |
590 | 63 | 71 | ||
591 | 64 | 'bank_statements': null, | 72 | 'bank_statements': null, |
592 | @@ -144,6 +152,10 @@ | |||
593 | 144 | }).then(function(partners){ | 152 | }).then(function(partners){ |
594 | 145 | self.set('partner_list',partners); | 153 | self.set('partner_list',partners); |
595 | 146 | 154 | ||
596 | 155 | return self.fetch('res.partner', ['name','vat','email','phone','mobile'], [['customer', '=', true]]); | ||
597 | 156 | }).then(function(customers){ | ||
598 | 157 | self.db.add_customers(customers); | ||
599 | 158 | |||
600 | 147 | return self.fetch('account.tax', ['amount', 'price_include', 'type']); | 159 | return self.fetch('account.tax', ['amount', 'price_include', 'type']); |
601 | 148 | }).then(function(taxes){ | 160 | }).then(function(taxes){ |
602 | 149 | self.set('taxes', taxes); | 161 | self.set('taxes', taxes); |
603 | @@ -187,7 +199,7 @@ | |||
604 | 187 | 199 | ||
605 | 188 | return self.fetch( | 200 | return self.fetch( |
606 | 189 | 'product.product', | 201 | 'product.product', |
608 | 190 | ['name', 'list_price','price','pos_categ_id', 'taxes_id', 'ean13', | 202 | ['name', 'code', 'list_price','price','pos_categ_id', 'taxes_id', 'ean13', |
609 | 191 | 'to_weight', 'uom_id', 'uos_id', 'uos_coeff', 'mes_type', 'description_sale', 'description'], | 203 | 'to_weight', 'uom_id', 'uos_id', 'uos_coeff', 'mes_type', 'description_sale', 'description'], |
610 | 192 | [['sale_ok','=',true],['available_in_pos','=',true]], | 204 | [['sale_ok','=',true],['available_in_pos','=',true]], |
611 | 193 | {pricelist: self.get('shop').pricelist_id[0]} // context for price | 205 | {pricelist: self.get('shop').pricelist_id[0]} // context for price |
612 | @@ -327,6 +339,46 @@ | |||
613 | 327 | } | 339 | } |
614 | 328 | return true; | 340 | return true; |
615 | 329 | }, | 341 | }, |
616 | 342 | |||
617 | 343 | keypad_enter_product: function(parsed_data){ | ||
618 | 344 | var self = this; | ||
619 | 345 | var doMerge = true; | ||
620 | 346 | var product = this.db.get_product_by_code(parsed_data.code); | ||
621 | 347 | var selectedOrder = this.get('selectedOrder'); | ||
622 | 348 | |||
623 | 349 | if (parsed_data.void_last_line) { | ||
624 | 350 | line = selectedOrder.getLastOrderline(); | ||
625 | 351 | if (line) { | ||
626 | 352 | line.set_quantity(Number.NaN); | ||
627 | 353 | } | ||
628 | 354 | return true; | ||
629 | 355 | } | ||
630 | 356 | |||
631 | 357 | if (!product){ | ||
632 | 358 | return false; | ||
633 | 359 | } | ||
634 | 360 | |||
635 | 361 | if (parsed_data.discount > 0) { | ||
636 | 362 | doMerge = false; | ||
637 | 363 | } | ||
638 | 364 | |||
639 | 365 | if (!parsed_data.priceOverride) { | ||
640 | 366 | parsed_data.price = product.price; | ||
641 | 367 | } | ||
642 | 368 | |||
643 | 369 | // if (product.get('to_weight') && self.pos.iface_electronic_scale) { | ||
644 | 370 | // self.pos_widget.screen_selector.set_current_screen(self.scale_screen, {product: product}); | ||
645 | 371 | // } else { | ||
646 | 372 | selectedOrder.addProduct(new module.Product(product), { quantity: parsed_data.qty, | ||
647 | 373 | price: parsed_data.price, | ||
648 | 374 | merge: doMerge}); | ||
649 | 375 | // } | ||
650 | 376 | if (!doMerge){ | ||
651 | 377 | selectedOrder.selected_orderline.set_discount(parsed_data.discount); | ||
652 | 378 | } | ||
653 | 379 | self.get('products').reset(product); | ||
654 | 380 | return true; | ||
655 | 381 | }, | ||
656 | 330 | }); | 382 | }); |
657 | 331 | 383 | ||
658 | 332 | module.CashRegister = Backbone.Model.extend({ | 384 | module.CashRegister = Backbone.Model.extend({ |
659 | @@ -742,7 +794,7 @@ | |||
660 | 742 | this.get('paymentLines').each(function(paymentline){ | 794 | this.get('paymentLines').each(function(paymentline){ |
661 | 743 | paymentlines.push(paymentline.export_for_printing()); | 795 | paymentlines.push(paymentline.export_for_printing()); |
662 | 744 | }); | 796 | }); |
664 | 745 | var client = this.get('client'); | 797 | var client = this.pos.get('selectedOrder').get('client'); |
665 | 746 | var cashier = this.pos.get('cashier') || this.pos.get('user'); | 798 | var cashier = this.pos.get('cashier') || this.pos.get('user'); |
666 | 747 | var company = this.pos.get('company'); | 799 | var company = this.pos.get('company'); |
667 | 748 | var shop = this.pos.get('shop'); | 800 | var shop = this.pos.get('shop'); |
668 | @@ -759,7 +811,7 @@ | |||
669 | 759 | total_discount: this.getDiscountTotal(), | 811 | total_discount: this.getDiscountTotal(), |
670 | 760 | change: this.getChange(), | 812 | change: this.getChange(), |
671 | 761 | name : this.getName(), | 813 | name : this.getName(), |
673 | 762 | client: client ? client.name : null , | 814 | client: client ? client : null , |
674 | 763 | invoice_id: null, //TODO | 815 | invoice_id: null, //TODO |
675 | 764 | cashier: cashier ? cashier.name : null, | 816 | cashier: cashier ? cashier.name : null, |
676 | 765 | date: { | 817 | date: { |
677 | @@ -795,6 +847,7 @@ | |||
678 | 795 | (this.get('paymentLines')).each(_.bind( function(item) { | 847 | (this.get('paymentLines')).each(_.bind( function(item) { |
679 | 796 | return paymentLines.push([0, 0, item.export_as_JSON()]); | 848 | return paymentLines.push([0, 0, item.export_as_JSON()]); |
680 | 797 | }, this)); | 849 | }, this)); |
681 | 850 | var client = this.pos.get('selectedOrder').get('client'); | ||
682 | 798 | return { | 851 | return { |
683 | 799 | name: this.getName(), | 852 | name: this.getName(), |
684 | 800 | amount_paid: this.getPaidTotal(), | 853 | amount_paid: this.getPaidTotal(), |
685 | @@ -804,7 +857,7 @@ | |||
686 | 804 | lines: orderLines, | 857 | lines: orderLines, |
687 | 805 | statement_ids: paymentLines, | 858 | statement_ids: paymentLines, |
688 | 806 | pos_session_id: this.pos.get('pos_session').id, | 859 | pos_session_id: this.pos.get('pos_session').id, |
690 | 807 | partner_id: this.pos.get('client') ? this.pos.get('client').id : undefined, | 860 | partner_id: client ? client.id : undefined, |
691 | 808 | user_id: this.pos.get('cashier') ? this.pos.get('cashier').id : this.pos.get('user').id, | 861 | user_id: this.pos.get('cashier') ? this.pos.get('cashier').id : this.pos.get('user').id, |
692 | 809 | }; | 862 | }; |
693 | 810 | }, | 863 | }, |
694 | @@ -894,4 +947,12 @@ | |||
695 | 894 | this.set({buffer:'0'}); | 947 | this.set({buffer:'0'}); |
696 | 895 | }, | 948 | }, |
697 | 896 | }); | 949 | }); |
698 | 950 | |||
699 | 951 | module.Customer = Backbone.Model.extend({ | ||
700 | 952 | }); | ||
701 | 953 | |||
702 | 954 | module.CustomerCollection = Backbone.Collection.extend({ | ||
703 | 955 | model: module.Customer, | ||
704 | 956 | }); | ||
705 | 957 | |||
706 | 897 | } | 958 | } |
707 | 898 | 959 | ||
708 | === modified file 'point_of_sale/static/src/js/screens.js' | |||
709 | --- point_of_sale/static/src/js/screens.js 2013-01-29 14:01:46 +0000 | |||
710 | +++ point_of_sale/static/src/js/screens.js 2013-03-03 00:11:29 +0000 | |||
711 | @@ -222,6 +222,20 @@ | |||
712 | 222 | } | 222 | } |
713 | 223 | }, | 223 | }, |
714 | 224 | 224 | ||
715 | 225 | // what happens when a product is entered by keypad emulator : | ||
716 | 226 | // it will add the product to the order. | ||
717 | 227 | keypad_product_action: function(data){ | ||
718 | 228 | var self = this; | ||
719 | 229 | if(self.pos.keypad_enter_product(data)){ | ||
720 | 230 | self.pos.proxy.keypad_item_success(data); | ||
721 | 231 | }else{ | ||
722 | 232 | self.pos.proxy.keypad_item_error_unrecognized(data); | ||
723 | 233 | if(self.product_error_popup && self.pos_widget.screen_selector.get_user_mode() === 'cashier'){ | ||
724 | 234 | self.pos_widget.screen_selector.show_popup(self.product_error_popup); | ||
725 | 235 | } | ||
726 | 236 | } | ||
727 | 237 | }, | ||
728 | 238 | |||
729 | 225 | // shows an action bar on the screen. The actionbar is automatically shown when you add a button | 239 | // shows an action bar on the screen. The actionbar is automatically shown when you add a button |
730 | 226 | // with add_action_button() | 240 | // with add_action_button() |
731 | 227 | show_action_bar: function(){ | 241 | show_action_bar: function(){ |
732 | @@ -283,8 +297,10 @@ | |||
733 | 283 | this.pos_widget.client_button.hide(); | 297 | this.pos_widget.client_button.hide(); |
734 | 284 | } | 298 | } |
735 | 285 | if(cashier_mode){ | 299 | if(cashier_mode){ |
736 | 300 | this.pos_widget.select_customer_button.show(); | ||
737 | 286 | this.pos_widget.close_button.show(); | 301 | this.pos_widget.close_button.show(); |
738 | 287 | }else{ | 302 | }else{ |
739 | 303 | this.pos_widget.select_customer_button.hide(); | ||
740 | 288 | this.pos_widget.close_button.hide(); | 304 | this.pos_widget.close_button.hide(); |
741 | 289 | } | 305 | } |
742 | 290 | 306 | ||
743 | @@ -296,11 +312,16 @@ | |||
744 | 296 | 'client' : self.barcode_client_action ? function(ean){ self.barcode_client_action(ean); } : undefined , | 312 | 'client' : self.barcode_client_action ? function(ean){ self.barcode_client_action(ean); } : undefined , |
745 | 297 | 'discount': self.barcode_discount_action ? function(ean){ self.barcode_discount_action(ean); } : undefined, | 313 | 'discount': self.barcode_discount_action ? function(ean){ self.barcode_discount_action(ean); } : undefined, |
746 | 298 | }); | 314 | }); |
747 | 315 | |||
748 | 316 | this.pos.keypad.set_action_callback(function(data){ self.keypad_product_action(data); }); | ||
749 | 299 | }, | 317 | }, |
750 | 300 | 318 | ||
751 | 301 | // this method is called when the screen is closed to make place for a new screen. this is a good place | 319 | // this method is called when the screen is closed to make place for a new screen. this is a good place |
752 | 302 | // to put your cleanup stuff as it is guaranteed that for each show() there is one and only one close() | 320 | // to put your cleanup stuff as it is guaranteed that for each show() there is one and only one close() |
753 | 303 | close: function(){ | 321 | close: function(){ |
754 | 322 | if(this.pos.keypad){ | ||
755 | 323 | this.pos.keypad.reset_action_callback(); | ||
756 | 324 | } | ||
757 | 304 | if(this.pos.barcode_reader){ | 325 | if(this.pos.barcode_reader){ |
758 | 305 | this.pos.barcode_reader.reset_action_callbacks(); | 326 | this.pos.barcode_reader.reset_action_callbacks(); |
759 | 306 | } | 327 | } |
760 | @@ -400,6 +421,7 @@ | |||
761 | 400 | this._super(); | 421 | this._super(); |
762 | 401 | this.pos.proxy.help_needed(); | 422 | this.pos.proxy.help_needed(); |
763 | 402 | this.pos.proxy.scan_item_error_unrecognized(); | 423 | this.pos.proxy.scan_item_error_unrecognized(); |
764 | 424 | this.pos.proxy.keypad_item_error_unrecognized(); | ||
765 | 403 | 425 | ||
766 | 404 | this.pos.barcode_reader.save_callbacks(); | 426 | this.pos.barcode_reader.save_callbacks(); |
767 | 405 | this.pos.barcode_reader.reset_action_callbacks(); | 427 | this.pos.barcode_reader.reset_action_callbacks(); |
768 | @@ -410,6 +432,8 @@ | |||
769 | 410 | self.pos_widget.screen_selector.set_user_mode('cashier'); | 432 | self.pos_widget.screen_selector.set_user_mode('cashier'); |
770 | 411 | }, | 433 | }, |
771 | 412 | }); | 434 | }); |
772 | 435 | this.pos.keypad.save_callback(); | ||
773 | 436 | this.pos.keypad.reset_action_callback(); | ||
774 | 413 | this.$('.footer .button').off('click').click(function(){ | 437 | this.$('.footer .button').off('click').click(function(){ |
775 | 414 | self.pos_widget.screen_selector.close_popup(); | 438 | self.pos_widget.screen_selector.close_popup(); |
776 | 415 | }); | 439 | }); |
777 | @@ -417,6 +441,7 @@ | |||
778 | 417 | close:function(){ | 441 | close:function(){ |
779 | 418 | this._super(); | 442 | this._super(); |
780 | 419 | this.pos.proxy.help_canceled(); | 443 | this.pos.proxy.help_canceled(); |
781 | 444 | this.pos.keypad.restore_callback(); | ||
782 | 420 | this.pos.barcode_reader.restore_callbacks(); | 445 | this.pos.barcode_reader.restore_callbacks(); |
783 | 421 | }, | 446 | }, |
784 | 422 | }); | 447 | }); |
785 | @@ -797,10 +822,12 @@ | |||
786 | 797 | this.user = this.pos.get('user'); | 822 | this.user = this.pos.get('user'); |
787 | 798 | this.company = this.pos.get('company'); | 823 | this.company = this.pos.get('company'); |
788 | 799 | this.shop_obj = this.pos.get('shop'); | 824 | this.shop_obj = this.pos.get('shop'); |
789 | 825 | this.client = null; | ||
790 | 800 | }, | 826 | }, |
791 | 801 | renderElement: function() { | 827 | renderElement: function() { |
792 | 802 | this._super(); | 828 | this._super(); |
793 | 803 | this.pos.bind('change:selectedOrder', this.change_selected_order, this); | 829 | this.pos.bind('change:selectedOrder', this.change_selected_order, this); |
794 | 830 | this.pos.get('selectedOrder').bind('change:client', this.change_client, this); | ||
795 | 804 | this.change_selected_order(); | 831 | this.change_selected_order(); |
796 | 805 | }, | 832 | }, |
797 | 806 | show: function(){ | 833 | show: function(){ |
798 | @@ -840,6 +867,10 @@ | |||
799 | 840 | this.currentPaymentLines.bind('all', this.refresh, this); | 867 | this.currentPaymentLines.bind('all', this.refresh, this); |
800 | 841 | this.refresh(); | 868 | this.refresh(); |
801 | 842 | }, | 869 | }, |
802 | 870 | change_client: function() { | ||
803 | 871 | this.client = this.pos.get('selectedOrder').get('client'); | ||
804 | 872 | this.refresh(); | ||
805 | 873 | }, | ||
806 | 843 | refresh: function() { | 874 | refresh: function() { |
807 | 844 | this.currentOrder = this.pos.get('selectedOrder'); | 875 | this.currentOrder = this.pos.get('selectedOrder'); |
808 | 845 | $('.pos-receipt-container', this.$el).html(QWeb.render('PosTicket',{widget:this})); | 876 | $('.pos-receipt-container', this.$el).html(QWeb.render('PosTicket',{widget:this})); |
809 | @@ -1006,4 +1037,82 @@ | |||
810 | 1006 | this.currentPaymentLines.last().set_amount(val); | 1037 | this.currentPaymentLines.last().set_amount(val); |
811 | 1007 | }, | 1038 | }, |
812 | 1008 | }); | 1039 | }); |
813 | 1040 | <<<<<<< TREE | ||
814 | 1041 | ======= | ||
815 | 1042 | |||
816 | 1043 | module.SelectCustomerPopupWidget = module.PopUpWidget.extend({ | ||
817 | 1044 | template:'SelectCustomerPopupWidget', | ||
818 | 1045 | |||
819 | 1046 | start: function(){ | ||
820 | 1047 | this._super(); | ||
821 | 1048 | var self = this; | ||
822 | 1049 | this.customer_list_widget = new module.CustomerListWidget(this,{ | ||
823 | 1050 | click_customer_action: function(customer){ | ||
824 | 1051 | this.pos.get('selectedOrder').set_client(customer); | ||
825 | 1052 | this.pos_widget.customername.refresh(); | ||
826 | 1053 | this.pos_widget.screen_selector.set_current_screen('products'); | ||
827 | 1054 | }, | ||
828 | 1055 | }); | ||
829 | 1056 | }, | ||
830 | 1057 | |||
831 | 1058 | show: function(){ | ||
832 | 1059 | this._super(); | ||
833 | 1060 | var self = this; | ||
834 | 1061 | this.renderElement(); | ||
835 | 1062 | |||
836 | 1063 | this.customer_list_widget.replace($('.placeholder-CustomerListWidget')); | ||
837 | 1064 | |||
838 | 1065 | this.$('.button.cancel').off('click').click(function(){ | ||
839 | 1066 | self.pos_widget.screen_selector.set_current_screen('products'); | ||
840 | 1067 | }); | ||
841 | 1068 | |||
842 | 1069 | this.customer_search(); | ||
843 | 1070 | }, | ||
844 | 1071 | |||
845 | 1072 | // Customer search filter | ||
846 | 1073 | customer_search: function(){ | ||
847 | 1074 | var self = this; | ||
848 | 1075 | |||
849 | 1076 | // find all products belonging to the current category | ||
850 | 1077 | var customers = this.pos.db.get_all_customers(); | ||
851 | 1078 | self.pos.get('customers').reset(customers); | ||
852 | 1079 | |||
853 | 1080 | // filter customers according to the search string | ||
854 | 1081 | this.$('.customer-searchbox input').keyup(function(event){ | ||
855 | 1082 | query = $(this).val().toLowerCase(); | ||
856 | 1083 | if(query){ | ||
857 | 1084 | var customers = self.pos.db.search_customers(query); | ||
858 | 1085 | self.pos.get('customers').reset(customers); | ||
859 | 1086 | self.$('.customer-search-clear').fadeIn(); | ||
860 | 1087 | if(event.keyCode == 13){ | ||
861 | 1088 | var c = null; | ||
862 | 1089 | if(customers.length == 1){ | ||
863 | 1090 | c = self.pos.get('customers').get(customers[0]); | ||
864 | 1091 | } | ||
865 | 1092 | if(c !== null){ | ||
866 | 1093 | self.pos_widget.select_customer_popup.customer_list_widget.click_customer_action(c); | ||
867 | 1094 | self.$('.customer-search-clear').trigger('click'); | ||
868 | 1095 | } | ||
869 | 1096 | } | ||
870 | 1097 | }else{ | ||
871 | 1098 | var customers = self.pos.db.get_all_customers(); | ||
872 | 1099 | self.pos.get('customers').reset(customers); | ||
873 | 1100 | self.$('.customer-search-clear').fadeOut(); | ||
874 | 1101 | } | ||
875 | 1102 | }); | ||
876 | 1103 | |||
877 | 1104 | this.$('.customer-searchbox input').click(function(){}); //Why ??? | ||
878 | 1105 | |||
879 | 1106 | //reset the search when clicking on reset | ||
880 | 1107 | this.$('.customer-search-clear').click(function(){ | ||
881 | 1108 | var customers = self.pos.db.get_all_customers(); | ||
882 | 1109 | self.pos.get('customers').reset(customers); | ||
883 | 1110 | self.$('.customer-searchbox input').val('').focus(); | ||
884 | 1111 | self.$('.customer-search-clear').fadeOut(); | ||
885 | 1112 | }); | ||
886 | 1113 | }, | ||
887 | 1114 | |||
888 | 1115 | }); | ||
889 | 1116 | |||
890 | 1117 | >>>>>>> MERGE-SOURCE | ||
891 | 1009 | } | 1118 | } |
892 | 1010 | 1119 | ||
893 | === modified file 'point_of_sale/static/src/js/widgets.js' | |||
894 | --- point_of_sale/static/src/js/widgets.js 2013-01-29 14:01:46 +0000 | |||
895 | +++ point_of_sale/static/src/js/widgets.js 2013-03-03 00:11:29 +0000 | |||
896 | @@ -564,12 +564,31 @@ | |||
897 | 564 | self.pos.get('products').reset(products); | 564 | self.pos.get('products').reset(products); |
898 | 565 | 565 | ||
899 | 566 | // filter the products according to the search string | 566 | // filter the products according to the search string |
901 | 567 | this.$('.searchbox input').keyup(function(){ | 567 | this.$('.searchbox input').keyup(function(event){ |
902 | 568 | query = $(this).val().toLowerCase(); | 568 | query = $(this).val().toLowerCase(); |
903 | 569 | if(query){ | 569 | if(query){ |
904 | 570 | var products = self.pos.db.search_product_in_category(self.category.id, query); | 570 | var products = self.pos.db.search_product_in_category(self.category.id, query); |
905 | 571 | self.pos.get('products').reset(products); | 571 | self.pos.get('products').reset(products); |
906 | 572 | self.$('.search-clear').fadeIn(); | 572 | self.$('.search-clear').fadeIn(); |
907 | 573 | if(event.keyCode == 13){ | ||
908 | 574 | var p = null; | ||
909 | 575 | if(products.length > 1){ | ||
910 | 576 | i = 0; | ||
911 | 577 | while(i < products.length){ | ||
912 | 578 | if (products[i].code == query){ | ||
913 | 579 | p = self.pos.get('products').get(products[i]); | ||
914 | 580 | break; | ||
915 | 581 | } | ||
916 | 582 | i++; | ||
917 | 583 | } | ||
918 | 584 | }else if(products.length == 1){ | ||
919 | 585 | p = self.pos.get('products').get(products[0]); | ||
920 | 586 | } | ||
921 | 587 | if(p !== null){ | ||
922 | 588 | self.pos_widget.product_screen.product_list_widget.click_product_action(p); | ||
923 | 589 | self.$('.search-clear').trigger('click'); | ||
924 | 590 | } | ||
925 | 591 | } | ||
926 | 573 | }else{ | 592 | }else{ |
927 | 574 | var products = self.pos.db.get_product_by_category(self.category.id); | 593 | var products = self.pos.db.get_product_by_category(self.category.id); |
928 | 575 | self.pos.get('products').reset(products); | 594 | self.pos.get('products').reset(products); |
929 | @@ -672,6 +691,27 @@ | |||
930 | 672 | }, | 691 | }, |
931 | 673 | }); | 692 | }); |
932 | 674 | 693 | ||
933 | 694 | module.CustomernameWidget = module.PosBaseWidget.extend({ | ||
934 | 695 | template: 'CustomernameWidget', | ||
935 | 696 | init: function(parent, options){ | ||
936 | 697 | var options = options || {}; | ||
937 | 698 | this._super(parent,options); | ||
938 | 699 | this.pos.bind('change:selectedOrder', this.renderElement, this); | ||
939 | 700 | }, | ||
940 | 701 | refresh: function(){ | ||
941 | 702 | this.renderElement(); | ||
942 | 703 | }, | ||
943 | 704 | get_name: function(){ | ||
944 | 705 | var user; | ||
945 | 706 | customer = this.pos.get('selectedOrder').get_client(); | ||
946 | 707 | if(customer){ | ||
947 | 708 | return customer.name; | ||
948 | 709 | }else{ | ||
949 | 710 | return ""; | ||
950 | 711 | } | ||
951 | 712 | }, | ||
952 | 713 | }); | ||
953 | 714 | |||
954 | 675 | module.HeaderButtonWidget = module.PosBaseWidget.extend({ | 715 | module.HeaderButtonWidget = module.PosBaseWidget.extend({ |
955 | 676 | template: 'HeaderButtonWidget', | 716 | template: 'HeaderButtonWidget', |
956 | 677 | init: function(parent, options){ | 717 | init: function(parent, options){ |
957 | @@ -867,6 +907,8 @@ | |||
958 | 867 | window.screen_selector = self.screen_selector; | 907 | window.screen_selector = self.screen_selector; |
959 | 868 | 908 | ||
960 | 869 | self.pos.barcode_reader.connect(); | 909 | self.pos.barcode_reader.connect(); |
961 | 910 | |||
962 | 911 | self.pos.keypad.connect(); | ||
963 | 870 | 912 | ||
964 | 871 | instance.webclient.set_content_full_screen(true); | 913 | instance.webclient.set_content_full_screen(true); |
965 | 872 | 914 | ||
966 | @@ -939,6 +981,9 @@ | |||
967 | 939 | 981 | ||
968 | 940 | this.error_negative_price_popup = new module.ErrorNegativePricePopupWidget(this, {}); | 982 | this.error_negative_price_popup = new module.ErrorNegativePricePopupWidget(this, {}); |
969 | 941 | this.error_negative_price_popup.appendTo($('.point-of-sale')); | 983 | this.error_negative_price_popup.appendTo($('.point-of-sale')); |
970 | 984 | |||
971 | 985 | this.select_customer_popup = new module.SelectCustomerPopupWidget(this, {}); | ||
972 | 986 | this.select_customer_popup.appendTo($('.point-of-sale')); | ||
973 | 942 | 987 | ||
974 | 943 | // -------- Misc --------- | 988 | // -------- Misc --------- |
975 | 944 | 989 | ||
976 | @@ -980,6 +1025,15 @@ | |||
977 | 980 | }); | 1025 | }); |
978 | 981 | this.client_button.appendTo(this.$('#rightheader')); | 1026 | this.client_button.appendTo(this.$('#rightheader')); |
979 | 982 | 1027 | ||
980 | 1028 | this.select_customer_button = new module.HeaderButtonWidget(this,{ | ||
981 | 1029 | label:'Select Customer', | ||
982 | 1030 | action: function(){ self.screen_selector.show_popup('select-customer'); }, | ||
983 | 1031 | }); | ||
984 | 1032 | this.select_customer_button.appendTo(this.$('#rightheader')); | ||
985 | 1033 | |||
986 | 1034 | this.customername = new module.CustomernameWidget(this,{}); | ||
987 | 1035 | this.customername.appendTo(this.$('#rightheader')); | ||
988 | 1036 | |||
989 | 983 | 1037 | ||
990 | 984 | // -------- Screen Selector --------- | 1038 | // -------- Screen Selector --------- |
991 | 985 | 1039 | ||
992 | @@ -1001,6 +1055,7 @@ | |||
993 | 1001 | 'error-session': this.error_session_popup, | 1055 | 'error-session': this.error_session_popup, |
994 | 1002 | 'error-negative-price': this.error_negative_price_popup, | 1056 | 'error-negative-price': this.error_negative_price_popup, |
995 | 1003 | 'choose-receipt': this.choose_receipt_popup, | 1057 | 'choose-receipt': this.choose_receipt_popup, |
996 | 1058 | 'select-customer': this.select_customer_popup, | ||
997 | 1004 | }, | 1059 | }, |
998 | 1005 | default_client_screen: 'welcome', | 1060 | default_client_screen: 'welcome', |
999 | 1006 | default_cashier_screen: 'products', | 1061 | default_cashier_screen: 'products', |
1000 | @@ -1087,6 +1142,7 @@ | |||
1001 | 1087 | close: function() { | 1142 | close: function() { |
1002 | 1088 | var self = this; | 1143 | var self = this; |
1003 | 1089 | this.pos.barcode_reader.disconnect(); | 1144 | this.pos.barcode_reader.disconnect(); |
1004 | 1145 | this.pos.keypad.disconnect(); | ||
1005 | 1090 | return new instance.web.Model("ir.model.data").get_func("search_read")([['name', '=', 'action_client_pos_menu']], ['res_id']).pipe( | 1146 | return new instance.web.Model("ir.model.data").get_func("search_read")([['name', '=', 'action_client_pos_menu']], ['res_id']).pipe( |
1006 | 1091 | _.bind(function(res) { | 1147 | _.bind(function(res) { |
1007 | 1092 | return this.rpc('/web/action/load', {'action_id': res[0]['res_id']}).pipe(_.bind(function(result) { | 1148 | return this.rpc('/web/action/load', {'action_id': res[0]['res_id']}).pipe(_.bind(function(result) { |
1008 | @@ -1103,4 +1159,60 @@ | |||
1009 | 1103 | this._super(); | 1159 | this._super(); |
1010 | 1104 | } | 1160 | } |
1011 | 1105 | }); | 1161 | }); |
1012 | 1162 | |||
1013 | 1163 | module.CustomerWidget = module.PosBaseWidget.extend({ | ||
1014 | 1164 | template: 'CustomerWidget', | ||
1015 | 1165 | init: function(parent, options) { | ||
1016 | 1166 | this._super(parent,options); | ||
1017 | 1167 | this.model = options.model; | ||
1018 | 1168 | this.click_customer_action = options.click_customer_action; | ||
1019 | 1169 | }, | ||
1020 | 1170 | renderElement: function() { | ||
1021 | 1171 | this._super(); | ||
1022 | 1172 | var self = this; | ||
1023 | 1173 | $("a", this.$el).click(function(e){ | ||
1024 | 1174 | if(self.click_customer_action){ | ||
1025 | 1175 | self.click_customer_action(self.model.toJSON()); | ||
1026 | 1176 | } | ||
1027 | 1177 | }); | ||
1028 | 1178 | }, | ||
1029 | 1179 | }); | ||
1030 | 1180 | |||
1031 | 1181 | module.CustomerListWidget = module.ScreenWidget.extend({ | ||
1032 | 1182 | template:'CustomerListWidget', | ||
1033 | 1183 | init: function(parent, options) { | ||
1034 | 1184 | var self = this; | ||
1035 | 1185 | this._super(parent,options); | ||
1036 | 1186 | this.model = options.model; | ||
1037 | 1187 | this.customer_list = []; | ||
1038 | 1188 | this.next_screen = options.next_screen || false; | ||
1039 | 1189 | this.click_customer_action = options.click_customer_action; | ||
1040 | 1190 | |||
1041 | 1191 | var customers = self.pos.db.get_all_customers(); | ||
1042 | 1192 | self.pos.get('customers').reset(customers); | ||
1043 | 1193 | this.pos.get('customers').bind('reset', function(){ | ||
1044 | 1194 | self.renderElement(); | ||
1045 | 1195 | }); | ||
1046 | 1196 | }, | ||
1047 | 1197 | renderElement: function() { | ||
1048 | 1198 | var self = this; | ||
1049 | 1199 | this._super(); | ||
1050 | 1200 | this.customer_list = []; | ||
1051 | 1201 | |||
1052 | 1202 | this.pos.get('customers') | ||
1053 | 1203 | .chain() | ||
1054 | 1204 | .map(function(customer) { | ||
1055 | 1205 | var customer = new module.CustomerWidget(self, { | ||
1056 | 1206 | model: customer, | ||
1057 | 1207 | next_screen: 'products', | ||
1058 | 1208 | click_customer_action: self.click_customer_action, | ||
1059 | 1209 | }) | ||
1060 | 1210 | self.customer_list.push(customer); | ||
1061 | 1211 | return customer; | ||
1062 | 1212 | }) | ||
1063 | 1213 | .invoke('appendTo', this.$('.customer-list')); | ||
1064 | 1214 | |||
1065 | 1215 | }, | ||
1066 | 1216 | }); | ||
1067 | 1217 | |||
1068 | 1106 | } | 1218 | } |
1069 | 1107 | 1219 | ||
1070 | === modified file 'point_of_sale/static/src/xml/pos.xml' | |||
1071 | --- point_of_sale/static/src/xml/pos.xml 2013-01-28 17:18:00 +0000 | |||
1072 | +++ point_of_sale/static/src/xml/pos.xml 2013-03-03 00:11:29 +0000 | |||
1073 | @@ -375,6 +375,9 @@ | |||
1074 | 375 | <a href="#"> | 375 | <a href="#"> |
1075 | 376 | <div class="product-img"> | 376 | <div class="product-img"> |
1076 | 377 | <img src='' /> <!-- the product thumbnail --> | 377 | <img src='' /> <!-- the product thumbnail --> |
1077 | 378 | <span class="code-tag"> | ||
1078 | 379 | <t t-esc="widget.model.get('code')"/> | ||
1079 | 380 | </span> | ||
1080 | 378 | <t t-if="!widget.model.get('to_weight')"> | 381 | <t t-if="!widget.model.get('to_weight')"> |
1081 | 379 | <span class="price-tag"> | 382 | <span class="price-tag"> |
1082 | 380 | <t t-esc="widget.format_currency(widget.model.get('price'))"/> | 383 | <t t-esc="widget.format_currency(widget.model.get('price'))"/> |
1083 | @@ -553,6 +556,12 @@ | |||
1084 | 553 | </span> | 556 | </span> |
1085 | 554 | </t> | 557 | </t> |
1086 | 555 | 558 | ||
1087 | 559 | <t t-name="CustomernameWidget"> | ||
1088 | 560 | <span class="customername"> | ||
1089 | 561 | <t t-esc="widget.get_name()" /> | ||
1090 | 562 | </span> | ||
1091 | 563 | </t> | ||
1092 | 564 | |||
1093 | 556 | <t t-name="PosTicket"> | 565 | <t t-name="PosTicket"> |
1094 | 557 | <div class="pos-sale-ticket"> | 566 | <div class="pos-sale-ticket"> |
1095 | 558 | 567 | ||
1096 | @@ -560,9 +569,18 @@ | |||
1097 | 560 | Date.CultureInfo.formatPatterns.longTime)"/> <t t-esc="widget.currentOrder.attributes.name"/></div> | 569 | Date.CultureInfo.formatPatterns.longTime)"/> <t t-esc="widget.currentOrder.attributes.name"/></div> |
1098 | 561 | <br /> | 570 | <br /> |
1099 | 562 | <t t-esc="widget.company.name"/><br /> | 571 | <t t-esc="widget.company.name"/><br /> |
1100 | 572 | <t t-if="widget.company.vat"> | ||
1101 | 573 | TIN: <t t-esc="widget.company.vat"/><br /> | ||
1102 | 574 | </t> | ||
1103 | 563 | Phone: <t t-esc="widget.company.phone || ''"/><br /> | 575 | Phone: <t t-esc="widget.company.phone || ''"/><br /> |
1104 | 564 | User: <t t-esc="widget.user.name"/><br /> | 576 | User: <t t-esc="widget.user.name"/><br /> |
1105 | 565 | Shop: <t t-esc="widget.shop_obj.name"/><br /> | 577 | Shop: <t t-esc="widget.shop_obj.name"/><br /> |
1106 | 578 | <t t-if="widget.client"> | ||
1107 | 579 | Customer: <t t-esc="widget.client.name"/><br /> | ||
1108 | 580 | <t t-if="widget.client.vat"> | ||
1109 | 581 | Customer TIN: <t t-esc="widget.client.vat"/><br /> | ||
1110 | 582 | </t> | ||
1111 | 583 | </t> | ||
1112 | 566 | <br /> | 584 | <br /> |
1113 | 567 | <table> | 585 | <table> |
1114 | 568 | <tr t-foreach="widget.currentOrderLines.toArray()" t-as="orderline"> | 586 | <tr t-foreach="widget.currentOrderLines.toArray()" t-as="orderline"> |
1115 | @@ -747,4 +765,66 @@ | |||
1116 | 747 | </div> | 765 | </div> |
1117 | 748 | </t> | 766 | </t> |
1118 | 749 | 767 | ||
1119 | 768 | <t t-name="CustomerListWidget"> | ||
1120 | 769 | <div class='customer-list-container'> | ||
1121 | 770 | <div class="customer-list-scroller"> | ||
1122 | 771 | <ol id="customers-screen-ol" class="customer-list"> | ||
1123 | 772 | </ol> | ||
1124 | 773 | </div> | ||
1125 | 774 | </div> | ||
1126 | 775 | </t> | ||
1127 | 776 | |||
1128 | 777 | <t t-name="SelectCustomerPopupWidget"> | ||
1129 | 778 | <div class="modal-dialog"> | ||
1130 | 779 | <div class="popup-selection"> | ||
1131 | 780 | <div class="customer-title"> | ||
1132 | 781 | Customer Selection | ||
1133 | 782 | </div> | ||
1134 | 783 | |||
1135 | 784 | <div class="customer-searchbox"> | ||
1136 | 785 | <input placeholder="Search Customers" /> | ||
1137 | 786 | <img class="customer-search-clear" src="/point_of_sale/static/src/img/search_reset.gif" /> | ||
1138 | 787 | </div> | ||
1139 | 788 | |||
1140 | 789 | <div class="content-container"> | ||
1141 | 790 | <span class="placeholder-CustomerListWidget" /> | ||
1142 | 791 | </div> | ||
1143 | 792 | |||
1144 | 793 | <div id="customer-cancel" class = "button cancel"> | ||
1145 | 794 | Cancel | ||
1146 | 795 | </div> | ||
1147 | 796 | </div> | ||
1148 | 797 | </div> | ||
1149 | 798 | </t> | ||
1150 | 799 | |||
1151 | 800 | <t t-name="CustomerWidget"> | ||
1152 | 801 | <li class='customer'> | ||
1153 | 802 | <a href="#"> | ||
1154 | 803 | <span class="customer-field customer-name"> | ||
1155 | 804 | <t t-esc="widget.model.get('name')"/> | ||
1156 | 805 | </span> | ||
1157 | 806 | <span class="customer-field"> | ||
1158 | 807 | <t t-if="widget.model.get('vat')"> | ||
1159 | 808 | <t t-esc="widget.model.get('vat')"/> | ||
1160 | 809 | </t> | ||
1161 | 810 | </span> | ||
1162 | 811 | <span class="customer-field"> | ||
1163 | 812 | <t t-if="widget.model.get('email')"> | ||
1164 | 813 | <t t-esc="widget.model.get('email')"/> | ||
1165 | 814 | </t> | ||
1166 | 815 | </span> | ||
1167 | 816 | <span class="customer-field customer-phone"> | ||
1168 | 817 | <t t-if="widget.model.get('phone')"> | ||
1169 | 818 | <t t-esc="widget.model.get('phone')"/> | ||
1170 | 819 | </t> | ||
1171 | 820 | </span> | ||
1172 | 821 | <span class="customer-field customer-phone"> | ||
1173 | 822 | <t t-if="widget.model.get('mobile')"> | ||
1174 | 823 | <t t-esc="widget.model.get('mobile')"/> | ||
1175 | 824 | </t> | ||
1176 | 825 | </span> | ||
1177 | 826 | </a> | ||
1178 | 827 | </li> | ||
1179 | 828 | </t> | ||
1180 | 829 | |||
1181 | 750 | </templates> | 830 | </templates> |
Hi everyone,
How can this module be installed? I have a fresh installation of OpenERP 7 and I need to select a customer in POS. SO I found this module and I've tried to install it several times, but I can't.
I can't find it in Modules list (I must be doing something wrong). Somewhere I read I should replace the original module with this one, but I didn't work.
I guess it must be a very easy issue, but I'm unable to find it ou ... Sorry.
Thanks in advance,
Àlex