Merge lp:~andrei-levin/openerp-pos/openerp-pos into lp:openerp-pos
- openerp-pos
- Merge into 7.0
Status: | Needs review |
---|---|
Proposed branch: | lp:~andrei-levin/openerp-pos/openerp-pos |
Merge into: | lp:openerp-pos |
Diff against target: |
857 lines (+791/-0) 12 files modified
pos_fiscal_printer/__init__.py (+23/-0) pos_fiscal_printer/__openerp__.py (+47/-0) pos_fiscal_printer/ecr.py (+75/-0) pos_fiscal_printer/fiscal_printer.py (+211/-0) pos_fiscal_printer/fiscal_printer_view.xml (+99/-0) pos_fiscal_printer/readme.txt (+52/-0) pos_fiscal_printer/security/ir.model.access.csv (+7/-0) pos_fiscal_printer/static/src/js/pos_fiscal_printer.js (+65/-0) pos_fp_dummy/__init__.py (+22/-0) pos_fp_dummy/__openerp__.py (+41/-0) pos_fp_dummy/driver.py (+139/-0) pos_fp_dummy/dummy.xml (+10/-0) |
To merge this branch: | bzr merge lp:~andrei-levin/openerp-pos/openerp-pos |
Related bugs: | |
Related blueprints: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Yannick Vaucher @ Camptocamp | description | Needs Fixing | |
Review via email: mp+196201@code.launchpad.net |
Commit message
Description of the change
Added modules that gives possibility to print on a Fiscal Printer
Yannick Vaucher @ Camptocamp (yvaucher-c2c) wrote : | # |
Andrei Levin (andrei-levin) wrote : | # |
This module creates a base for printing to fiscal printer. It contains
all the functions that can be useful for creating a "driver" - a
simple module which contains functions that should be different for
various printer models.
The second module is a "dummy" driver that shows how to write a driver.
2014-03-14 10:49 GMT+01:00 Yannick Vaucher @ Camptocamp
<email address hidden>:
> Review: Needs Fixing description
>
> Please improve the description
>
> Anything to do to setup the fiscol printer ?
> --
> https:/
> You are the owner of lp:~andrei-levin/openerp-pos/openerp-pos.
--
Didotech Srl
Via T.Aspetti, 248
35133 Padova (PD)
Tel 049 8592286
Cell.: 347-2426694
www.didotech.com
- 3. By Andrei Levin
-
Fix: Resolved problems with non administrator user
- 4. By Andrei Levin
-
[pos_fiscal_
printer] : Refactoring: removed duplicated code, added new function dry_print() to base Ecr class. This function save receipt as text file every time "dry run" is selected. - 5. By Andrei Levin
-
[pos_fp_dummy]: Changed to use payments instead of journals
Unmerged revisions
- 5. By Andrei Levin
-
[pos_fp_dummy]: Changed to use payments instead of journals
- 4. By Andrei Levin
-
[pos_fiscal_
printer] : Refactoring: removed duplicated code, added new function dry_print() to base Ecr class. This function save receipt as text file every time "dry run" is selected. - 3. By Andrei Levin
-
Fix: Resolved problems with non administrator user
- 2. By Andrei Levin
-
Added readme file
- 1. By Andrei Levin
-
Initial commit
Preview Diff
1 | === added directory 'pos_fiscal_printer' | |||
2 | === added file 'pos_fiscal_printer/__init__.py' | |||
3 | --- pos_fiscal_printer/__init__.py 1970-01-01 00:00:00 +0000 | |||
4 | +++ pos_fiscal_printer/__init__.py 2014-07-10 14:31:27 +0000 | |||
5 | @@ -0,0 +1,23 @@ | |||
6 | 1 | # -*- coding: utf-8 -*- | ||
7 | 2 | ############################################################################## | ||
8 | 3 | # | ||
9 | 4 | # Copyright (C) 2013 Didotech srl (<http://www.didotech.com>) | ||
10 | 5 | # All Rights Reserved | ||
11 | 6 | # | ||
12 | 7 | # This program is free software: you can redistribute it and/or modify | ||
13 | 8 | # it under the terms of the GNU Affero General Public License as published | ||
14 | 9 | # by the Free Software Foundation, either version 3 of the License, or | ||
15 | 10 | # (at your option) any later version. | ||
16 | 11 | # | ||
17 | 12 | # This program is distributed in the hope that it will be useful, | ||
18 | 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
19 | 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
20 | 15 | # GNU General Public License for more details. | ||
21 | 16 | # | ||
22 | 17 | # You should have received a copy of the GNU Affero General Public License | ||
23 | 18 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
24 | 19 | # | ||
25 | 20 | ############################################################################## | ||
26 | 21 | |||
27 | 22 | import fiscal_printer | ||
28 | 23 | |||
29 | 0 | 24 | ||
30 | === added file 'pos_fiscal_printer/__openerp__.py' | |||
31 | --- pos_fiscal_printer/__openerp__.py 1970-01-01 00:00:00 +0000 | |||
32 | +++ pos_fiscal_printer/__openerp__.py 2014-07-10 14:31:27 +0000 | |||
33 | @@ -0,0 +1,47 @@ | |||
34 | 1 | # -*- coding: utf-8 -*- | ||
35 | 2 | ############################################################################## | ||
36 | 3 | # | ||
37 | 4 | # Copyright (C) 2013-2014 Didotech srl (<http://www.didotech.com>) | ||
38 | 5 | # All Rights Reserved | ||
39 | 6 | # | ||
40 | 7 | # This program is free software: you can redistribute it and/or modify | ||
41 | 8 | # it under the terms of the GNU Affero General Public License as published | ||
42 | 9 | # by the Free Software Foundation, either version 3 of the License, or | ||
43 | 10 | # (at your option) any later version. | ||
44 | 11 | # | ||
45 | 12 | # This program is distributed in the hope that it will be useful, | ||
46 | 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
47 | 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
48 | 15 | # GNU General Public License for more details. | ||
49 | 16 | # | ||
50 | 17 | # You should have received a copy of the GNU Affero General Public License | ||
51 | 18 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
52 | 19 | # | ||
53 | 20 | ############################################################################## | ||
54 | 21 | { | ||
55 | 22 | 'name': 'Fiscal Printer Management', | ||
56 | 23 | 'version': '0.0.3', | ||
57 | 24 | 'category': 'Point Of Sale', | ||
58 | 25 | 'description': """ | ||
59 | 26 | This module adds possibility to print a receipt on fiscal printer | ||
60 | 27 | It prepares all values you may need when writing driver for your printer. | ||
61 | 28 | |||
62 | 29 | """, | ||
63 | 30 | 'author': 'Andrei Levin', | ||
64 | 31 | 'depends': [ | ||
65 | 32 | 'point_of_sale', | ||
66 | 33 | ], | ||
67 | 34 | 'init_xml': [ | ||
68 | 35 | ], | ||
69 | 36 | 'update_xml': [ | ||
70 | 37 | 'security/ir.model.access.csv', # load access rights after groups | ||
71 | 38 | 'fiscal_printer_view.xml', | ||
72 | 39 | ], | ||
73 | 40 | 'demo_xml': [], | ||
74 | 41 | 'installable': True, | ||
75 | 42 | 'active': False, | ||
76 | 43 | 'js': [ | ||
77 | 44 | 'static/src/js/pos_fiscal_printer.js', | ||
78 | 45 | ], | ||
79 | 46 | } | ||
80 | 47 | |||
81 | 0 | 48 | ||
82 | === added file 'pos_fiscal_printer/ecr.py' | |||
83 | --- pos_fiscal_printer/ecr.py 1970-01-01 00:00:00 +0000 | |||
84 | +++ pos_fiscal_printer/ecr.py 2014-07-10 14:31:27 +0000 | |||
85 | @@ -0,0 +1,75 @@ | |||
86 | 1 | # -*- coding: utf-8 -*- | ||
87 | 2 | ############################################################################### | ||
88 | 3 | # | ||
89 | 4 | # Copyright (c) 2013-2014 Andrei Levin (andrei.levin at didotech.com) | ||
90 | 5 | # All Rights Reserved. | ||
91 | 6 | # | ||
92 | 7 | # This program is free software: you can redistribute it and/or modify | ||
93 | 8 | # it under the terms of the GNU General Public License as published by | ||
94 | 9 | # the Free Software Foundation, either version 3 of the License, or | ||
95 | 10 | # (at your option) any later version. | ||
96 | 11 | # | ||
97 | 12 | # This program is distributed in the hope that it will be useful, | ||
98 | 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
99 | 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
100 | 15 | # GNU General Public License for more details. | ||
101 | 16 | # | ||
102 | 17 | # You should have received a copy of the GNU General Public License | ||
103 | 18 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
104 | 19 | # | ||
105 | 20 | ############################################################################### | ||
106 | 21 | |||
107 | 22 | import tempfile | ||
108 | 23 | import os | ||
109 | 24 | from datetime import datetime | ||
110 | 25 | |||
111 | 26 | |||
112 | 27 | class Ecr(): | ||
113 | 28 | reference = '' | ||
114 | 29 | |||
115 | 30 | def __init__(self, config, cash_register_name): | ||
116 | 31 | self.__name__ = config.name | ||
117 | 32 | self.config = config | ||
118 | 33 | self.cash_register_name = cash_register_name | ||
119 | 34 | |||
120 | 35 | def get_product_line(self): | ||
121 | 36 | if self.product_lines: | ||
122 | 37 | line = self.product_lines.pop(0) | ||
123 | 38 | self.subtotal += line.price_subtotal | ||
124 | 39 | if line.discount: | ||
125 | 40 | self.discount += line.product_id.list_price * line.qty - line.price_subtotal | ||
126 | 41 | return line | ||
127 | 42 | else: | ||
128 | 43 | return False | ||
129 | 44 | |||
130 | 45 | def create(self, receipt): | ||
131 | 46 | self.discount = 0.0 | ||
132 | 47 | self.subtotal = 0.0 | ||
133 | 48 | self.product_lines = receipt.lines | ||
134 | 49 | self.receipt_data = receipt | ||
135 | 50 | self.receipt_data.cash_register_name = self.cash_register_name | ||
136 | 51 | |||
137 | 52 | self.receipt = self.compose() | ||
138 | 53 | |||
139 | 54 | def __unicode__(self): | ||
140 | 55 | return u'\r\n'.join(self.receipt) + u'\r\n\r\n' | ||
141 | 56 | |||
142 | 57 | def __str__(self): | ||
143 | 58 | return u'\r\n'.join(self.receipt) + u'\r\n\r\n' | ||
144 | 59 | |||
145 | 60 | def compose(self): | ||
146 | 61 | pass | ||
147 | 62 | |||
148 | 63 | def print_receipt(self): | ||
149 | 64 | pass | ||
150 | 65 | |||
151 | 66 | def dry_print(self): | ||
152 | 67 | destination = os.path.join(tempfile.gettempdir(), 'fp_tmp') | ||
153 | 68 | if not os.path.exists(destination): | ||
154 | 69 | os.makedirs(destination) | ||
155 | 70 | ticket = datetime.now().strftime("%Y%m%d.%H%M") + '.txt' | ||
156 | 71 | |||
157 | 72 | file(os.path.join(destination, ticket), 'w').write( | ||
158 | 73 | unicode(self).encode('utf8')) | ||
159 | 74 | |||
160 | 75 | return True | ||
161 | 0 | 76 | ||
162 | === added file 'pos_fiscal_printer/fiscal_printer.py' | |||
163 | --- pos_fiscal_printer/fiscal_printer.py 1970-01-01 00:00:00 +0000 | |||
164 | +++ pos_fiscal_printer/fiscal_printer.py 2014-07-10 14:31:27 +0000 | |||
165 | @@ -0,0 +1,211 @@ | |||
166 | 1 | # -*- coding: utf-8 -*- | ||
167 | 2 | ############################################################################### | ||
168 | 3 | # | ||
169 | 4 | # Copyright (c) 2013-2014 Andrei Levin (andrei.levin at didotech.com) | ||
170 | 5 | # All Rights Reserved. | ||
171 | 6 | # | ||
172 | 7 | # This program is free software: you can redistribute it and/or modify | ||
173 | 8 | # it under the terms of the GNU General Public License as published by | ||
174 | 9 | # the Free Software Foundation, either version 3 of the License, or | ||
175 | 10 | # (at your option) any later version. | ||
176 | 11 | # | ||
177 | 12 | # This program is distributed in the hope that it will be useful, | ||
178 | 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
179 | 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
180 | 15 | # GNU General Public License for more details. | ||
181 | 16 | # | ||
182 | 17 | # You should have received a copy of the GNU General Public License | ||
183 | 18 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
184 | 19 | # | ||
185 | 20 | ############################################################################### | ||
186 | 21 | |||
187 | 22 | from openerp import netsvc | ||
188 | 23 | from openerp.osv import fields, osv | ||
189 | 24 | from openerp.tools.translate import _ | ||
190 | 25 | |||
191 | 26 | import time | ||
192 | 27 | import datetime | ||
193 | 28 | import pooler | ||
194 | 29 | |||
195 | 30 | import logging | ||
196 | 31 | _logger = logging.getLogger(__name__) | ||
197 | 32 | _logger.setLevel(logging.INFO) | ||
198 | 33 | |||
199 | 34 | |||
200 | 35 | class pos_order(osv.osv): | ||
201 | 36 | _inherit = "pos.order" | ||
202 | 37 | |||
203 | 38 | def create_from_ui(self, cr, uid, orders, context=None): | ||
204 | 39 | #_logger.info("orders: %r", orders) | ||
205 | 40 | order_ids = [] | ||
206 | 41 | # print orders | ||
207 | 42 | |||
208 | 43 | for tmp_order in orders: | ||
209 | 44 | order = tmp_order['data'] | ||
210 | 45 | order_id = self.create(cr, uid, { | ||
211 | 46 | 'name': order['name'], | ||
212 | 47 | 'user_id': order['user_id'] or False, | ||
213 | 48 | 'session_id': order['pos_session_id'], | ||
214 | 49 | 'lines': order['lines'], | ||
215 | 50 | 'pos_reference': order['name'] | ||
216 | 51 | }, context) | ||
217 | 52 | |||
218 | 53 | for payments in order['statement_ids']: | ||
219 | 54 | payment = payments[2] | ||
220 | 55 | self.add_payment(cr, uid, order_id, { | ||
221 | 56 | 'amount': payment['amount'] or 0.0, | ||
222 | 57 | 'payment_date': payment['name'], | ||
223 | 58 | 'statement_id': payment['statement_id'], | ||
224 | 59 | 'payment_name': payment.get('note', False), | ||
225 | 60 | 'journal': payment['journal_id'] | ||
226 | 61 | }, context=context) | ||
227 | 62 | |||
228 | 63 | if order['amount_return']: | ||
229 | 64 | session = self.pool['pos.session'].browse( | ||
230 | 65 | cr, uid, order['pos_session_id'], context=context) | ||
231 | 66 | cash_journal = session.cash_journal_id | ||
232 | 67 | |||
233 | 68 | if not cash_journal: | ||
234 | 69 | cash_journal_ids = filter( | ||
235 | 70 | lambda st: st.journal_id.type == 'cash', session.statement_ids) | ||
236 | 71 | if not len(cash_journal_ids): | ||
237 | 72 | raise osv.except_osv(_('error!'), | ||
238 | 73 | _("No cash statement found for this session. Unable to record returned cash.")) | ||
239 | 74 | cash_journal = cash_journal_ids[0].journal_id | ||
240 | 75 | self.add_payment(cr, uid, order_id, { | ||
241 | 76 | 'amount': -order['amount_return'], | ||
242 | 77 | 'payment_date': time.strftime('%Y-%m-%d %H:%M:%S'), | ||
243 | 78 | 'payment_name': _('return'), | ||
244 | 79 | 'journal': cash_journal.id, | ||
245 | 80 | }, context=context) | ||
246 | 81 | order_ids.append(order_id) | ||
247 | 82 | wf_service = netsvc.LocalService("workflow") | ||
248 | 83 | wf_service.trg_validate(uid, 'pos.order', order_id, 'paid', cr) | ||
249 | 84 | |||
250 | 85 | self.print_receipt(cr, uid, order_id, order['pos_session_id'], context) | ||
251 | 86 | |||
252 | 87 | return order_ids | ||
253 | 88 | |||
254 | 89 | def print_receipt(self, cr, uid, order_id, pos_session_id, context): | ||
255 | 90 | receipt = Receipt(cr, uid, order_id, context) | ||
256 | 91 | printer = fiscal_printer(cr, uid, pos_session_id) | ||
257 | 92 | printer.print_receipt(receipt) | ||
258 | 93 | |||
259 | 94 | |||
260 | 95 | class Receipt(): | ||
261 | 96 | def __init__(self, cr, uid, order_id, context=None): | ||
262 | 97 | self.pool = pooler.get_pool(cr.dbname) | ||
263 | 98 | |||
264 | 99 | self.order = self.pool['pos.order'].browse(cr, uid, order_id, context) | ||
265 | 100 | |||
266 | 101 | utc_date_order = datetime.datetime.strptime( | ||
267 | 102 | self.order.date_order, '%Y-%m-%d %H:%M:%S') | ||
268 | 103 | self.date = fields.datetime.context_timestamp( | ||
269 | 104 | cr, uid, utc_date_order, context=context) | ||
270 | 105 | self.name = self.order.name | ||
271 | 106 | self.reference = self.order.pos_reference | ||
272 | 107 | self.user = self.pool['res.users'].browse(cr, uid, uid) | ||
273 | 108 | |||
274 | 109 | self.lines = self.order.lines | ||
275 | 110 | self.amount_total = self.order.amount_total | ||
276 | 111 | self.amount_paid = self.order.amount_paid | ||
277 | 112 | self.amount_tax = self.order.amount_tax | ||
278 | 113 | self.amount_return = self.order.amount_return | ||
279 | 114 | self.payments = self.order.statement_ids | ||
280 | 115 | |||
281 | 116 | |||
282 | 117 | class fiscal_printer(): | ||
283 | 118 | def __init__(self, cr, uid, pos_session_id): | ||
284 | 119 | self.pool = pooler.get_pool(cr.dbname) | ||
285 | 120 | fp_config_obj = self.pool['fp.config'] | ||
286 | 121 | pos_session = self.pool['pos.session'].browse(cr, uid, pos_session_id) | ||
287 | 122 | cash_register_id = pos_session.config_id.id | ||
288 | 123 | |||
289 | 124 | printer_configs = fp_config_obj.search( | ||
290 | 125 | cr, uid, [('cash_register_id', '=', cash_register_id)]) | ||
291 | 126 | self.cash_register = self.pool['pos.config'].browse(cr, uid, cash_register_id) | ||
292 | 127 | |||
293 | 128 | if printer_configs: | ||
294 | 129 | self.config = fp_config_obj.browse(cr, uid, printer_configs[0]) | ||
295 | 130 | else: | ||
296 | 131 | raise osv.except_osv( | ||
297 | 132 | 'Warning', _('Cash register {cash_register} has no fiscal printers'.format(cash_register=self.cash_register.name))) | ||
298 | 133 | |||
299 | 134 | module = __import__(self.config.driver_id.module + '.driver', fromlist=[str(self.config.driver_id.class_name)]) | ||
300 | 135 | self.driver = getattr(module, str(self.config.driver_id.class_name))(self.config, self.cash_register.name) | ||
301 | 136 | |||
302 | 137 | def print_receipt(self, receipt): | ||
303 | 138 | self.driver.create(receipt) | ||
304 | 139 | if self.config.dry: | ||
305 | 140 | _logger.info('Discarding receipt for order {0}'.format(receipt.reference)) | ||
306 | 141 | self.driver.dry_print() | ||
307 | 142 | else: | ||
308 | 143 | if self.driver.print_receipt(): | ||
309 | 144 | _logger.info('Receipt for {0} printed successfully'.format(receipt.reference)) | ||
310 | 145 | else: | ||
311 | 146 | _logger.error('There are problems with printing Receipt for {0}'.format(receipt.reference)) | ||
312 | 147 | |||
313 | 148 | |||
314 | 149 | class fp_config(osv.osv): | ||
315 | 150 | _name = 'fp.config' | ||
316 | 151 | _description = "Fiscal Printer configuration" | ||
317 | 152 | |||
318 | 153 | def _get_name(self, cr, uid, ids, field_name, arg, context=None): | ||
319 | 154 | if not ids: | ||
320 | 155 | return {} | ||
321 | 156 | |||
322 | 157 | res = {} | ||
323 | 158 | |||
324 | 159 | configs = self.browse(cr, uid, ids) | ||
325 | 160 | |||
326 | 161 | for config in configs: | ||
327 | 162 | res[config.id] = config.cash_register_id.name + \ | ||
328 | 163 | ' : ' + config.driver_id.name | ||
329 | 164 | |||
330 | 165 | return res | ||
331 | 166 | |||
332 | 167 | _columns = { | ||
333 | 168 | "name": fields.function(_get_name, string='Name', type='char', method=True), | ||
334 | 169 | 'cash_register_id': fields.many2one('pos.config', 'Cash Register', required=True), | ||
335 | 170 | "driver_id": fields.many2one('fp.driver', "Driver", required=True, help="Printer driver used for this client"), | ||
336 | 171 | 'ecr_password': fields.char("Password ECR", size=16, required=False, help="Password, if needed to print to fiscal printer"), | ||
337 | 172 | 'host': fields.char("Fiscal Printer Host Address", size=15, required=False, help="Fiscal Printer IP Address or Hostname (if Hostname can be resolved)"), | ||
338 | 173 | 'port': fields.integer("Port", required=False, help="Fiscal Printer Port"), | ||
339 | 174 | 'user': fields.char("Username", size=15, required=False, help="Username, if needed to access fiscal printer"), | ||
340 | 175 | 'password': fields.char("Password", size=15, required=False, help="Password, if needed to access fiscal printer"), | ||
341 | 176 | 'destination': fields.char("Destination", size=256, required=False, help="Destination directory, where receipt should be placed"), | ||
342 | 177 | 'dry': fields.boolean('Dry Run') | ||
343 | 178 | } | ||
344 | 179 | |||
345 | 180 | _sql_constraints = [ | ||
346 | 181 | ('cash_register_uniq', 'unique(cash_register_id)', 'Cash register must be unique!') | ||
347 | 182 | ] | ||
348 | 183 | |||
349 | 184 | |||
350 | 185 | class fp_driver(osv.osv): | ||
351 | 186 | _name = 'fp.driver' | ||
352 | 187 | _description = "Fiscal Printer Drivers" | ||
353 | 188 | |||
354 | 189 | _columns = { | ||
355 | 190 | 'name': fields.char("FP Description", size=32, required=True, help="The Description of the Fiscal Printer"), | ||
356 | 191 | 'class_name': fields.char("Class Name", size=32, required=True, help="Name of the class that contain driver for printing on Fiscal Printer"), | ||
357 | 192 | 'module': fields.char("Driver module name", size=32, required=True, help="The name of the driver module"), | ||
358 | 193 | } | ||
359 | 194 | |||
360 | 195 | |||
361 | 196 | class department(osv.osv): | ||
362 | 197 | _name = 'department' | ||
363 | 198 | _description = "Tax department" | ||
364 | 199 | |||
365 | 200 | _columns = { | ||
366 | 201 | 'name': fields.char("Department description", size=32, required=True, help="The Description of the department"), | ||
367 | 202 | 'department': fields.integer("Department", required=True) | ||
368 | 203 | } | ||
369 | 204 | |||
370 | 205 | |||
371 | 206 | class account_tax(osv.osv): | ||
372 | 207 | _inherit = 'account.tax' | ||
373 | 208 | |||
374 | 209 | _columns = { | ||
375 | 210 | 'department': fields.many2one('department', 'Department', required=False) | ||
376 | 211 | } | ||
377 | 0 | 212 | ||
378 | === added file 'pos_fiscal_printer/fiscal_printer_view.xml' | |||
379 | --- pos_fiscal_printer/fiscal_printer_view.xml 1970-01-01 00:00:00 +0000 | |||
380 | +++ pos_fiscal_printer/fiscal_printer_view.xml 2014-07-10 14:31:27 +0000 | |||
381 | @@ -0,0 +1,99 @@ | |||
382 | 1 | <openerp> | ||
383 | 2 | <data> | ||
384 | 3 | |||
385 | 4 | <record id="view_fp_config_form" model="ir.ui.view"> | ||
386 | 5 | <field name="name">fp.config.form</field> | ||
387 | 6 | <field name="model">fp.config</field> | ||
388 | 7 | <field name="type">form</field> | ||
389 | 8 | <field name="arch" type="xml"> | ||
390 | 9 | <form string="Configure Fiscal Printer"> | ||
391 | 10 | <field name="name"/> | ||
392 | 11 | <field name="cash_register_id"/> | ||
393 | 12 | <field name="driver_id" /> | ||
394 | 13 | <field name="ecr_password" /> | ||
395 | 14 | <field name="host" /> | ||
396 | 15 | <field name="port" /> | ||
397 | 16 | <field name="user" /> | ||
398 | 17 | <field name="password" /> | ||
399 | 18 | <field name="destination" /> | ||
400 | 19 | <separator colspan="4" /> | ||
401 | 20 | <field name="dry" /> | ||
402 | 21 | </form> | ||
403 | 22 | </field> | ||
404 | 23 | </record> | ||
405 | 24 | |||
406 | 25 | <record id="view_fp_config_tree" model="ir.ui.view"> | ||
407 | 26 | <field name="name">fp.config.tree</field> | ||
408 | 27 | <field name="model">fp.config</field> | ||
409 | 28 | <field name="type">tree</field> | ||
410 | 29 | <field name="arch" type="xml"> | ||
411 | 30 | <tree string="Cash Registers"> | ||
412 | 31 | <field name="name"/> | ||
413 | 32 | <field name="host" /> | ||
414 | 33 | <field name="cash_register_id"/> | ||
415 | 34 | <field name="driver_id" /> | ||
416 | 35 | </tree> | ||
417 | 36 | </field> | ||
418 | 37 | </record> | ||
419 | 38 | |||
420 | 39 | <record id="action_fiscal_printer" model="ir.actions.act_window"> | ||
421 | 40 | <field name="name">Fiscal Printer Configuration</field> | ||
422 | 41 | <field name="res_model">fp.config</field> | ||
423 | 42 | <field name="view_type">form</field> | ||
424 | 43 | <field name="view_mode">tree,form</field> | ||
425 | 44 | <field name="view_id" ref="view_fp_config_tree"/> | ||
426 | 45 | </record> | ||
427 | 46 | |||
428 | 47 | <!-- Menu Definitions --> | ||
429 | 48 | <menuitem id="cash_register" name="Fiscal Printer" parent="point_of_sale.menu_point_config_product" action="action_fiscal_printer"/> | ||
430 | 49 | |||
431 | 50 | <!-- Add field department to account.tax --> | ||
432 | 51 | <record id="view_account_tax_department_form" model="ir.ui.view"> | ||
433 | 52 | <field name="name">account.tax.department.form</field> | ||
434 | 53 | <field name="model">account.tax</field> | ||
435 | 54 | <field name="inherit_id" ref="account.view_tax_form" /> | ||
436 | 55 | <field name="type">form</field> | ||
437 | 56 | <field name="arch" type="xml"> | ||
438 | 57 | <field name="description" position="after"> | ||
439 | 58 | <field name="department"/> | ||
440 | 59 | </field> | ||
441 | 60 | </field> | ||
442 | 61 | </record> | ||
443 | 62 | |||
444 | 63 | <record id="view_department_form" model="ir.ui.view"> | ||
445 | 64 | <field name="name">department.form</field> | ||
446 | 65 | <field name="model">department</field> | ||
447 | 66 | <field name="type">form</field> | ||
448 | 67 | <field name="arch" type="xml"> | ||
449 | 68 | <form string="Configure Department"> | ||
450 | 69 | <field name="name"/> | ||
451 | 70 | <field name="department"/> | ||
452 | 71 | </form> | ||
453 | 72 | </field> | ||
454 | 73 | </record> | ||
455 | 74 | |||
456 | 75 | <record id="view_department_tree" model="ir.ui.view"> | ||
457 | 76 | <field name="name">department.tree</field> | ||
458 | 77 | <field name="model">department</field> | ||
459 | 78 | <field name="type">tree</field> | ||
460 | 79 | <field name="arch" type="xml"> | ||
461 | 80 | <tree string="Departments"> | ||
462 | 81 | <field name="name"/> | ||
463 | 82 | <field name="department"/> | ||
464 | 83 | </tree> | ||
465 | 84 | </field> | ||
466 | 85 | </record> | ||
467 | 86 | |||
468 | 87 | <record id="action_department" model="ir.actions.act_window"> | ||
469 | 88 | <field name="name">Department Configuration</field> | ||
470 | 89 | <field name="res_model">department</field> | ||
471 | 90 | <field name="view_type">form</field> | ||
472 | 91 | <field name="view_mode">tree,form</field> | ||
473 | 92 | <field name="view_id" ref="view_department_tree"/> | ||
474 | 93 | </record> | ||
475 | 94 | |||
476 | 95 | <!-- Menu Definitions --> | ||
477 | 96 | <menuitem id="department" name="Department Configuration" parent="point_of_sale.menu_point_config_product" action="action_department"/> | ||
478 | 97 | |||
479 | 98 | </data> | ||
480 | 99 | </openerp> | ||
481 | 0 | \ No newline at end of file | 100 | \ No newline at end of file |
482 | 1 | 101 | ||
483 | === added file 'pos_fiscal_printer/readme.txt' | |||
484 | --- pos_fiscal_printer/readme.txt 1970-01-01 00:00:00 +0000 | |||
485 | +++ pos_fiscal_printer/readme.txt 2014-07-10 14:31:27 +0000 | |||
486 | @@ -0,0 +1,52 @@ | |||
487 | 1 | This module creates all that is necessary to print on a Fiscal Printer. | ||
488 | 2 | |||
489 | 3 | The solution is composed of 2 modules: | ||
490 | 4 | - pos_fiscal_printer - a core module | ||
491 | 5 | - pos_fp_<driver name> - driver module | ||
492 | 6 | |||
493 | 7 | Core module: | ||
494 | 8 | - disable printing from browser | ||
495 | 9 | - create configuration menu inside POS | ||
496 | 10 | - add departments | ||
497 | 11 | - load driver | ||
498 | 12 | - prepare all the data needed for creating a receipt | ||
499 | 13 | |||
500 | 14 | Driver module: | ||
501 | 15 | - compose a receipt | ||
502 | 16 | - sent receipt to printer | ||
503 | 17 | |||
504 | 18 | Menu Fiscal Printer gives you a possibility to match a Fiscal Printer | ||
505 | 19 | to a POS. Very often Fiscal Printers has only program for Windows, | ||
506 | 20 | which they call "Driver". This resident program expects to find a | ||
507 | 21 | receipt file in some directory. When it finds a new receipt it is | ||
508 | 22 | interpreted and send to a printer. We can't write to a file system | ||
509 | 23 | directly from JavaScript, so Client start printing and then Server | ||
510 | 24 | send a receipt to a computer which has fiscal printer attached. | ||
511 | 25 | Sometimes we also need to wright a password for the Fiscal Printer | ||
512 | 26 | inside receipt. This is the reason for 2 password fields. | ||
513 | 27 | |||
514 | 28 | Another type of printers accept printing via ethernet. Again we need | ||
515 | 29 | host/user/password. | ||
516 | 30 | |||
517 | 31 | An ECR (Electronic Cash Register) should register a | ||
518 | 32 | "department". Department depends on tax paid, so inside my Dummy | ||
519 | 33 | driver you will see: | ||
520 | 34 | reparto = line.product_id.taxes_id[0].department.department | ||
521 | 35 | |||
522 | 36 | Core module creates Department table, which should be compiled (in | ||
523 | 37 | Italy there are 3 main Departments: Reparto 1, Reparto 2, Reparto 3) | ||
524 | 38 | with corresponding numbers. These numbers are used to tell ECR to | ||
525 | 39 | which department a product belongs. After compiling the table, one | ||
526 | 40 | should go to Accounting / Configuration and select a right department | ||
527 | 41 | for the tax. | ||
528 | 42 | |||
529 | 43 | There is another option which can sound strange - "Dry Run". When we | ||
530 | 44 | print a receipt and something goes wrong, receipt will not disappear, | ||
531 | 45 | every time we try to print it will be send to a Fiscal Printer, so if | ||
532 | 46 | for any reason a receipt can't be printed it will block our POS until | ||
533 | 47 | the problem is resolved (a red ball appears in the right upper angle | ||
534 | 48 | of the POS). In this case one can select "Dry Run" option and when one | ||
535 | 49 | enters POS the queue will be emptied and a red ball became green. This | ||
536 | 50 | option can also be used if some receipts were printed directly from | ||
537 | 51 | ECR and one want to register this selling without reprinting a | ||
538 | 52 | receipt. | ||
539 | 0 | 53 | ||
540 | === added directory 'pos_fiscal_printer/security' | |||
541 | === added file 'pos_fiscal_printer/security/ir.model.access.csv' | |||
542 | --- pos_fiscal_printer/security/ir.model.access.csv 1970-01-01 00:00:00 +0000 | |||
543 | +++ pos_fiscal_printer/security/ir.model.access.csv 2014-07-10 14:31:27 +0000 | |||
544 | @@ -0,0 +1,7 @@ | |||
545 | 1 | id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink | ||
546 | 2 | access_fp_config_user,fp.config_user,model_fp_config,point_of_sale.group_pos_user,1,1,1,1 | ||
547 | 3 | access_fp_config_manager,fp.config_manager,model_fp_config,point_of_sale.group_pos_manager,1,1,1,1 | ||
548 | 4 | access_fp_driver_user,fp.driver_user,model_fp_driver,point_of_sale.group_pos_user,1,1,1,1 | ||
549 | 5 | access_fp_driver_manager,fp.driver_manager,model_fp_driver,point_of_sale.group_pos_manager,1,1,1,1 | ||
550 | 6 | access_department_user,fp.department_user,model_department,point_of_sale.group_pos_user,1,1,1,1 | ||
551 | 7 | access_department,fp.department_manager,model_department,point_of_sale.group_pos_manager,1,1,1,1 | ||
552 | 0 | 8 | ||
553 | === added directory 'pos_fiscal_printer/static' | |||
554 | === added directory 'pos_fiscal_printer/static/src' | |||
555 | === added directory 'pos_fiscal_printer/static/src/js' | |||
556 | === added file 'pos_fiscal_printer/static/src/js/pos_fiscal_printer.js' | |||
557 | --- pos_fiscal_printer/static/src/js/pos_fiscal_printer.js 1970-01-01 00:00:00 +0000 | |||
558 | +++ pos_fiscal_printer/static/src/js/pos_fiscal_printer.js 2014-07-10 14:31:27 +0000 | |||
559 | @@ -0,0 +1,65 @@ | |||
560 | 1 | openerp.pos_fiscal_printer = function(instance) { | ||
561 | 2 | // loading the namespace of the 'point_of_sale' module | ||
562 | 3 | var module = instance.point_of_sale; | ||
563 | 4 | var _t = instance.web._t; | ||
564 | 5 | |||
565 | 6 | module.ReceiptScreenWidget.include({ | ||
566 | 7 | |||
567 | 8 | show: function(){ | ||
568 | 9 | var self = this; | ||
569 | 10 | |||
570 | 11 | this.hidden = false; | ||
571 | 12 | if(this.$el){ | ||
572 | 13 | this.$el.show(); | ||
573 | 14 | } | ||
574 | 15 | |||
575 | 16 | if(this.pos_widget.action_bar.get_button_count() > 0){ | ||
576 | 17 | this.show_action_bar(); | ||
577 | 18 | }else{ | ||
578 | 19 | this.hide_action_bar(); | ||
579 | 20 | } | ||
580 | 21 | |||
581 | 22 | // we add the help button by default. we do this because the buttons are cleared on each refresh so that | ||
582 | 23 | // the button stay local to each screen | ||
583 | 24 | this.pos_widget.left_action_bar.add_new_button({ | ||
584 | 25 | label: _t('Help'), | ||
585 | 26 | icon: '/point_of_sale/static/src/img/icons/png48/help.png', | ||
586 | 27 | click: function(){ self.help_button_action(); }, | ||
587 | 28 | }); | ||
588 | 29 | |||
589 | 30 | var self = this; | ||
590 | 31 | var cashier_mode = this.pos_widget.screen_selector.get_user_mode() === 'cashier'; | ||
591 | 32 | |||
592 | 33 | this.pos_widget.set_numpad_visible(this.show_numpad && cashier_mode); | ||
593 | 34 | this.pos_widget.set_leftpane_visible(this.show_leftpane); | ||
594 | 35 | this.pos_widget.set_left_action_bar_visible(this.show_leftpane && !cashier_mode); | ||
595 | 36 | this.pos_widget.set_cashier_controls_visible(cashier_mode); | ||
596 | 37 | |||
597 | 38 | if(cashier_mode && this.pos.iface_self_checkout){ | ||
598 | 39 | this.pos_widget.client_button.show(); | ||
599 | 40 | }else{ | ||
600 | 41 | this.pos_widget.client_button.hide(); | ||
601 | 42 | } | ||
602 | 43 | if(cashier_mode){ | ||
603 | 44 | this.pos_widget.close_button.show(); | ||
604 | 45 | }else{ | ||
605 | 46 | this.pos_widget.close_button.hide(); | ||
606 | 47 | } | ||
607 | 48 | |||
608 | 49 | this.pos_widget.username.set_user_mode(this.pos_widget.screen_selector.get_user_mode()); | ||
609 | 50 | |||
610 | 51 | this.pos.barcode_reader.set_action_callback({ | ||
611 | 52 | 'cashier': self.barcode_cashier_action ? function(ean){ self.barcode_cashier_action(ean); } : undefined , | ||
612 | 53 | 'product': self.barcode_product_action ? function(ean){ self.barcode_product_action(ean); } : undefined , | ||
613 | 54 | 'client' : self.barcode_client_action ? function(ean){ self.barcode_client_action(ean); } : undefined , | ||
614 | 55 | 'discount': self.barcode_discount_action ? function(ean){ self.barcode_discount_action(ean); } : undefined, | ||
615 | 56 | }); | ||
616 | 57 | |||
617 | 58 | this.add_action_button({ | ||
618 | 59 | label: _t('Next Order'), | ||
619 | 60 | icon: '/point_of_sale/static/src/img/icons/png48/go-next.png', | ||
620 | 61 | click: function() { self.finishOrder(); }, | ||
621 | 62 | }); | ||
622 | 63 | }, | ||
623 | 64 | }); | ||
624 | 65 | } | ||
625 | 0 | 66 | ||
626 | === added directory 'pos_fp_dummy' | |||
627 | === added file 'pos_fp_dummy/__init__.py' | |||
628 | --- pos_fp_dummy/__init__.py 1970-01-01 00:00:00 +0000 | |||
629 | +++ pos_fp_dummy/__init__.py 2014-07-10 14:31:27 +0000 | |||
630 | @@ -0,0 +1,22 @@ | |||
631 | 1 | # -*- coding: utf-8 -*- | ||
632 | 2 | ############################################################################## | ||
633 | 3 | # | ||
634 | 4 | # Copyright (C) 2013 Didotech srl (<http://www.didotech.com>) | ||
635 | 5 | # All Rights Reserved | ||
636 | 6 | # | ||
637 | 7 | # This program is free software: you can redistribute it and/or modify | ||
638 | 8 | # it under the terms of the GNU Affero General Public License as published | ||
639 | 9 | # by the Free Software Foundation, either version 3 of the License, or | ||
640 | 10 | # (at your option) any later version. | ||
641 | 11 | # | ||
642 | 12 | # This program is distributed in the hope that it will be useful, | ||
643 | 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
644 | 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
645 | 15 | # GNU General Public License for more details. | ||
646 | 16 | # | ||
647 | 17 | # You should have received a copy of the GNU Affero General Public License | ||
648 | 18 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
649 | 19 | # | ||
650 | 20 | ############################################################################## | ||
651 | 21 | |||
652 | 22 | |||
653 | 0 | 23 | ||
654 | === added file 'pos_fp_dummy/__openerp__.py' | |||
655 | --- pos_fp_dummy/__openerp__.py 1970-01-01 00:00:00 +0000 | |||
656 | +++ pos_fp_dummy/__openerp__.py 2014-07-10 14:31:27 +0000 | |||
657 | @@ -0,0 +1,41 @@ | |||
658 | 1 | # -*- coding: utf-8 -*- | ||
659 | 2 | ############################################################################## | ||
660 | 3 | # | ||
661 | 4 | # Copyright (C) 2013-2014 Didotech.com (<http://www.didotech.com>) | ||
662 | 5 | # All Rights Reserved | ||
663 | 6 | # | ||
664 | 7 | # This program is free software: you can redistribute it and/or modify | ||
665 | 8 | # it under the terms of the GNU Affero General Public License as published | ||
666 | 9 | # by the Free Software Foundation, either version 3 of the License, or | ||
667 | 10 | # (at your option) any later version. | ||
668 | 11 | # | ||
669 | 12 | # This program is distributed in the hope that it will be useful, | ||
670 | 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
671 | 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
672 | 15 | # GNU General Public License for more details. | ||
673 | 16 | # | ||
674 | 17 | # You should have received a copy of the GNU Affero General Public License | ||
675 | 18 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
676 | 19 | # | ||
677 | 20 | ############################################################################## | ||
678 | 21 | { | ||
679 | 22 | 'name': 'Dummy FP driver', | ||
680 | 23 | 'version': '0.0.2', | ||
681 | 24 | 'category': 'Point Of Sale', | ||
682 | 25 | 'description': """ | ||
683 | 26 | This module contains Fiscal Printer "Dummy" driver. | ||
684 | 27 | It can be used as an example and for testing purpose. | ||
685 | 28 | """, | ||
686 | 29 | 'author': 'Andrei Levin', | ||
687 | 30 | 'depends': [ | ||
688 | 31 | 'pos_fiscal_printer', | ||
689 | 32 | ], | ||
690 | 33 | 'init_xml': [ | ||
691 | 34 | 'dummy.xml', | ||
692 | 35 | ], | ||
693 | 36 | 'update_xml': [ | ||
694 | 37 | ], | ||
695 | 38 | 'demo_xml': [], | ||
696 | 39 | 'installable': True, | ||
697 | 40 | 'active': False, | ||
698 | 41 | } | ||
699 | 0 | 42 | ||
700 | === added file 'pos_fp_dummy/driver.py' | |||
701 | --- pos_fp_dummy/driver.py 1970-01-01 00:00:00 +0000 | |||
702 | +++ pos_fp_dummy/driver.py 2014-07-10 14:31:27 +0000 | |||
703 | @@ -0,0 +1,139 @@ | |||
704 | 1 | # -*- coding: utf-8 -*- | ||
705 | 2 | ############################################################################### | ||
706 | 3 | # | ||
707 | 4 | # Copyright (c) 2013-2014 Andrei Levin (andrei.levin at didotech.com) | ||
708 | 5 | # All Rights Reserved. | ||
709 | 6 | # | ||
710 | 7 | # This program is free software: you can redistribute it and/or modify | ||
711 | 8 | # it under the terms of the GNU General Public License as published by | ||
712 | 9 | # the Free Software Foundation, either version 3 of the License, or | ||
713 | 10 | # (at your option) any later version. | ||
714 | 11 | # | ||
715 | 12 | # This program is distributed in the hope that it will be useful, | ||
716 | 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
717 | 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
718 | 15 | # GNU General Public License for more details. | ||
719 | 16 | # | ||
720 | 17 | # You should have received a copy of the GNU General Public License | ||
721 | 18 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
722 | 19 | # | ||
723 | 20 | ############################################################################### | ||
724 | 21 | |||
725 | 22 | import os | ||
726 | 23 | import tempfile | ||
727 | 24 | |||
728 | 25 | from pos_fiscal_printer.ecr import Ecr | ||
729 | 26 | |||
730 | 27 | |||
731 | 28 | def italian_number(number, precision=1, no_zero=False): | ||
732 | 29 | if not number: | ||
733 | 30 | return '0,00' | ||
734 | 31 | |||
735 | 32 | if number < 0: | ||
736 | 33 | sign = '-' | ||
737 | 34 | else: | ||
738 | 35 | sign = '' | ||
739 | 36 | # Requires Python >= 2.7: | ||
740 | 37 | # before, after = "{:.{digits}f}".format(number, digits=precision).split('.') | ||
741 | 38 | # Works with Python 2.6: | ||
742 | 39 | before, after = "{0:10.{digits}f}".format( | ||
743 | 40 | number, digits=precision).strip('- ').split('.') | ||
744 | 41 | |||
745 | 42 | belist = [] | ||
746 | 43 | end = len(before) | ||
747 | 44 | for i in range(3, len(before) + 3, 3): | ||
748 | 45 | start = len(before) - i | ||
749 | 46 | if start < 0: | ||
750 | 47 | start = 0 | ||
751 | 48 | belist.append(before[start: end]) | ||
752 | 49 | end = len(before) - i | ||
753 | 50 | before = '.'.join(reversed(belist)) | ||
754 | 51 | |||
755 | 52 | if no_zero and int(number) == float(number): | ||
756 | 53 | return sign + before | ||
757 | 54 | else: | ||
758 | 55 | return sign + before + ',' + after | ||
759 | 56 | |||
760 | 57 | |||
761 | 58 | class Dummy(Ecr): | ||
762 | 59 | def compose(self): | ||
763 | 60 | ''' | ||
764 | 61 | line attributes: | ||
765 | 62 | product_id - Product | ||
766 | 63 | order_id - Order Ref | ||
767 | 64 | price_unit - Unit Price | ||
768 | 65 | price_subtotal - Subtotal w/o Tax | ||
769 | 66 | company_id - Company | ||
770 | 67 | price_subtotal_incl - Subtotal | ||
771 | 68 | qty - Quantity | ||
772 | 69 | discount - Discount (%) | ||
773 | 70 | name - Line No | ||
774 | 71 | receipt attributes: | ||
775 | 72 | payments | ||
776 | 73 | ''' | ||
777 | 74 | receipt = self.receipt_data | ||
778 | 75 | ticket = [] | ||
779 | 76 | ticket.append(receipt.date.strftime( | ||
780 | 77 | '%d/%m/%Y %H:%M:%S') + ' ' + receipt.reference) | ||
781 | 78 | ticket.append(u'') | ||
782 | 79 | ticket.append(receipt.user.company_id.name) | ||
783 | 80 | ticket.append(u'Phone: ' + str(receipt.user.company_id.phone)) | ||
784 | 81 | ticket.append(u'User: ' + receipt.user.name) | ||
785 | 82 | ticket.append(u'POS: ' + receipt.cash_register_name) | ||
786 | 83 | ticket.append(u'') | ||
787 | 84 | |||
788 | 85 | line = self.get_product_line() | ||
789 | 86 | while line: | ||
790 | 87 | qty = italian_number(line.qty, 1, True) | ||
791 | 88 | |||
792 | 89 | if line.product_id and line.product_id.taxes_id and line.product_id.taxes_id[0]: | ||
793 | 90 | reparto = line.product_id.taxes_id[0].department.department | ||
794 | 91 | else: | ||
795 | 92 | reparto = 1 | ||
796 | 93 | |||
797 | 94 | ticket.append(u"rep={reparto} {name:<24}{qty: ^3}{price:>6.2f} €".format( | ||
798 | 95 | reparto=reparto, name=line.product_id.name, qty=qty, price=line.price_subtotal_incl)) | ||
799 | 96 | |||
800 | 97 | if line.discount: | ||
801 | 98 | ticket.append( | ||
802 | 99 | u'With a {discount}% discount'.format(discount=line.discount)) | ||
803 | 100 | |||
804 | 101 | line = self.get_product_line() | ||
805 | 102 | |||
806 | 103 | ticket.append(u'') | ||
807 | 104 | |||
808 | 105 | ticket.append(u'{text:<30}{subtotal:>9.2f} €'.format( | ||
809 | 106 | text='Subtotal: ', subtotal=self.subtotal)) | ||
810 | 107 | ticket.append( | ||
811 | 108 | u'Tax: {tax:9.2f} €'.format(tax=receipt.amount_tax)) | ||
812 | 109 | ticket.append( | ||
813 | 110 | u'Discount: {discount:9.2f} €'.format(discount=self.discount)) | ||
814 | 111 | ticket.append( | ||
815 | 112 | u'Total: {total:9.2f} €'.format(total=receipt.amount_total)) | ||
816 | 113 | |||
817 | 114 | ticket.append(u'') | ||
818 | 115 | |||
819 | 116 | for payment in receipt.payments: | ||
820 | 117 | ticket.append(u'{name:<30}{amount:9.2f} €'.format( | ||
821 | 118 | name=payment.name_get()[0][1], amount=payment.amount)) | ||
822 | 119 | |||
823 | 120 | ticket.append(u'') | ||
824 | 121 | |||
825 | 122 | ticket.append( | ||
826 | 123 | u'Change: {a_return:9.2f} €'.format(a_return=receipt.amount_return)) | ||
827 | 124 | |||
828 | 125 | return ticket | ||
829 | 126 | |||
830 | 127 | def print_receipt(self): | ||
831 | 128 | if self.config.destination: | ||
832 | 129 | destination = self.config.destination | ||
833 | 130 | else: | ||
834 | 131 | destination = os.path.join(tempfile.gettempdir(), 'opentmp') | ||
835 | 132 | if not os.path.exists(destination): | ||
836 | 133 | os.makedirs(destination) | ||
837 | 134 | ticket = 'ticket.txt' | ||
838 | 135 | |||
839 | 136 | file(os.path.join(destination, ticket), 'w').write( | ||
840 | 137 | unicode(self).encode('utf8')) | ||
841 | 138 | |||
842 | 139 | return True | ||
843 | 0 | 140 | ||
844 | === added file 'pos_fp_dummy/dummy.xml' | |||
845 | --- pos_fp_dummy/dummy.xml 1970-01-01 00:00:00 +0000 | |||
846 | +++ pos_fp_dummy/dummy.xml 2014-07-10 14:31:27 +0000 | |||
847 | @@ -0,0 +1,10 @@ | |||
848 | 1 | <?xml version="1.0"?> | ||
849 | 2 | <openerp> | ||
850 | 3 | <data> | ||
851 | 4 | <record id="fp_driver" model="fp.driver"> | ||
852 | 5 | <field name="class_name">Dummy</field> | ||
853 | 6 | <field name="name">Dummy driver</field> | ||
854 | 7 | <field name="module">pos_fp_dummy</field> | ||
855 | 8 | </record> | ||
856 | 9 | </data> | ||
857 | 10 | </openerp> |
Please improve the description
Anything to do to setup the fiscol printer ?