[IMP]: fix the problem of account_receivable and remove the Finish button form pos_pa...
[odoo/odoo.git] / addons / point_of_sale / pos.py
1 # -*- encoding: utf-8 -*-
2 ##############################################################################
3 #
4 #    OpenERP, Open Source Management Solution
5 #    Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>). All Rights Reserved
6 #    $Id$
7 #
8 #    This program is free software: you can redistribute it and/or modify
9 #    it under the terms of the GNU General Public License as published by
10 #    the Free Software Foundation, either version 3 of the License, or
11 #    (at your option) any later version.
12 #
13 #    This program is distributed in the hope that it will be useful,
14 #    but WITHOUT ANY WARRANTY; without even the implied warranty of
15 #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 #    GNU General Public License for more details.
17 #
18 #    You should have received a copy of the GNU Affero General Public License
19 #    along with this program.  If not, see <http://www.gnu.org/licenses/>.
20 #
21 ##############################################################################
22
23 import time
24 import netsvc
25 from osv import fields, osv
26 from mx import DateTime
27 from tools.translate import _
28 from decimal import Decimal
29 import tools
30 import re
31
32
33 class pos_config_journal(osv.osv):
34     _name = 'pos.config.journal'
35     _description = "Point of Sale journal configuration."
36     _columns = {
37         'name': fields.char('Description', size=64),
38         'code': fields.char('Code', size=64),
39         'journal_id': fields.many2one('account.journal', "Journal")
40     }
41
42 pos_config_journal()
43
44 class res_mode_contact(osv.osv):
45     _name = "res.mode.contact"
46     _description = "Contact mode"
47     _columns={
48         'name': fields.char('Mode', size=64, select=1),
49         'active': fields.boolean('Active', select=2),
50     }
51 res_mode_contact()
52
53 class contact_mode_partner(osv.osv):
54     _inherit = 'res.partner'
55     _columns = {
56         'contact_mode_id': fields.many2one('res.mode.contact','Contact Mode'),
57      }
58 contact_mode_partner()
59
60
61 class pos_company_discount(osv.osv):
62     _inherit = 'res.company'
63     _columns = {
64         'company_discount': fields.float('Max Discount(%)', digits=(16,2)),
65         'max_diff': fields.float('Max Difference for Cashboxes', digits=(16,2)),
66         'account_receivable': fields.many2one('account.account',
67             'Default Receivable', states={'draft': [('readonly', False)]}),
68      }
69
70 pos_company_discount()
71
72
73 class pos_order(osv.osv):
74     _name = "pos.order"
75     _description = "Point of Sale"
76     _order = "date_order, create_date desc"
77     _order = "date_order desc"
78
79     def unlink(self, cr, uid, ids, context={}):
80         for rec in self.browse(cr, uid, ids, context=context):
81             for rec_statement in rec.statement_ids:
82                 if (rec_statement.statement_id and rec_statement.statement_id.state=='confirm') or rec.state=='done':
83                     raise osv.except_osv(_('Invalid action !'), _('Cannot delete a point of sale which is closed or contains confirmed cashboxes!'))
84         return super(pos_order, self).unlink(cr, uid, ids, context=context)
85
86     def onchange_partner_pricelist(self, cr, uid, ids, part, context={}):
87         if not part:
88             return {}
89         pricelist = self.pool.get('res.partner').browse(cr, uid, part).property_product_pricelist.id
90         return {'value':{'pricelist_id': pricelist}}
91
92     def _amount_total(self, cr, uid, ids, field_name, arg, context):
93         cr.execute("""
94         SELECT
95             p.id,
96             COALESCE(SUM(
97                 l.price_unit*l.qty*(1-(l.discount/100.0)))::decimal(16,2), 0
98                 ) AS amount
99         FROM pos_order p
100             LEFT OUTER JOIN pos_order_line l ON (p.id=l.order_id)
101         WHERE p.id =ANY(%s) GROUP BY p.id """,(ids,))
102         res = dict(cr.fetchall())
103         for rec in self.browse(cr, uid, ids, context):
104             if rec.partner_id \
105                and rec.partner_id.property_account_position \
106                and rec.partner_id.property_account_position.tax_ids:
107                 res[rec.id] = res[rec.id] - rec.amount_tax
108             else :
109                 res[rec.id] = res[rec.id] + rec.amount_tax
110         return res
111
112     def _get_date_payment2(self, cr, uid, ids, context, *a):
113         res = {}
114         pay_obj = self.pool.get('account.bank.statement')
115         stat_obj_line = self.pool.get('account.bank.statement.line')
116         tot =0.0
117         val=None
118         for order in self.browse(cr, uid, ids):
119             cr.execute("select date_payment2 from pos_order where id=%d"%(order.id))
120             date_p=cr.fetchone()
121             date_p=date_p and date_p[0] or None
122             if date_p:
123                 res[order.id]=date_p
124                 return res
125             cr.execute(" SELECT max(l.date) from account_move_line l, account_move m, account_invoice i, account_move_reconcile r, pos_order o where i.move_id=m.id and l.move_id=m.id and l.reconcile_id=r.id and o.id=%d and o.invoice_id=i.id"%(order.id))
126             val=cr.fetchone()
127             val= val and val[0] or None
128             if not val:
129                 cr.execute("select max(date) from account_bank_statement_line l, account_bank_statement_reconcile s where l.pos_statement_id=%d and l.reconcile_id=s.id"%(order.id))
130                 val=cr.fetchone()
131                 val=val and val[0] or None
132             if val:
133                 res[order.id]=val
134         return res
135     def _get_date_payment(self, cr, uid, ids, context, *a):
136         res = {}
137         pay_obj = self.pool.get('pos.payment')
138         tot =0.0
139         val=None
140         for order in self.browse(cr, uid, ids):
141             cr.execute("select date_payment from pos_order where id=%d"%(order.id))
142             date_p=cr.fetchone()
143             date_p=date_p and date_p[0] or None
144             if date_p:
145                 res[order.id]=date_p
146                 return res
147             discount_allowed=order.company_id.company_discount
148             for line in order.lines:
149                 if line.discount > discount_allowed:
150                     return {order.id: None }
151             if order.amount_paid == order.amount_total and not date_p:
152                 cr.execute("select max(date) from account_bank_statement_line where pos_statement_id=%d"%(order.id))
153                 val=cr.fetchone()
154                 val=val and val[0] or None
155             if order.invoice_id and order.invoice_id.move_id and not date_p and not val:
156                 for o in order.invoice_id.move_id.line_id:
157                     if o.balance==0:
158                         if val<o.date_created:
159                             val=o.date_created
160             if val:
161                 res[order.id]=val
162         return res
163
164     def _amount_tax(self, cr, uid, ids, field_name, arg, context):
165         res = {}
166         tax_obj = self.pool.get('account.tax')
167         for order in self.browse(cr, uid, ids):
168             val = 0.0
169             for line in order.lines:
170                 if order.price_type!='tax_excluded':
171                     val = reduce(lambda x, y: x+round(y['amount'], 2),
172                         tax_obj.compute_inv(cr, uid, line.product_id.taxes_id,
173                             line.price_unit * \
174                             (1-(line.discount or 0.0)/100.0), line.qty),
175                             val)
176                 else:
177                     val = reduce(lambda x, y: x+round(y['amount'], 2),
178                         tax_obj.compute(cr, uid, line.product_id.taxes_id,
179                             line.price_unit * \
180                             (1-(line.discount or 0.0)/100.0), line.qty),
181                             val)
182
183             res[order.id] = val
184         return res
185
186     def _total_payment(self, cr, uid, ids, field_name, arg, context):
187         res = {}
188         i=0
189         for order in self.browse(cr, uid, ids):
190             val = 0.0
191             for payment in order.statement_ids:
192                 val += payment.amount
193             res[order.id] = val
194             return res
195         return {order.id:val}
196
197     def _total_return(self, cr, uid, ids, field_name, arg, context):
198         res = {}
199         for order in self.browse(cr, uid, ids):
200             val = 0.0
201             for payment in order.payments:
202                 val += (payment.amount < 0 and payment.amount or 0)
203             res[order.id] = val
204         return res
205
206     def payment_get(self, cr, uid, ids, context=None):
207         cr.execute("select id from pos_payment where order_id =ANY(%s)",(ids,))
208         return [i[0] for i in cr.fetchall()]
209
210     def _sale_journal_get(self, cr, uid, context):
211         journal_obj = self.pool.get('account.journal')
212         res = journal_obj.search(cr, uid,
213             [('type', '=', 'sale')], limit=1)
214         if res:
215             return res[0]
216         else:
217             return False
218
219     def _shop_get(self, cr, uid, context):
220         company = self.pool.get('res.users').browse(cr, uid, uid, context).company_id
221         res = self.pool.get('sale.shop').search(cr, uid, [])
222        # res = self.pool.get('sale.shop').search(cr, uid, [('company_id', '=', company.id)])
223         if res:
224             return res[0]
225         else:
226             return False
227
228     def _receivable_get(self, cr, uid, context=None):
229         prop_obj = self.pool.get('ir.property')
230         res = prop_obj.get(cr, uid, 'property_account_receivable', 'res.partner', context=context)
231         return res
232
233     def copy(self, cr, uid, id, default=None, context={}):
234         if not default:
235             default = {}
236         default.update({
237             'state': 'draft',
238             'payments': [],
239             'partner_id': False,
240             'invoice_id': False,
241             'account_move': False,
242             'last_out_picking': False,
243             'nb_print': 0,
244             'pickings': []
245         })
246         return super(pos_order, self).copy(cr, uid, id, default, context)
247
248     def _get_v( self, cr, uid, ids,*a):
249         flag=False
250         res_company = self.pool.get('res.company')
251         res_obj = self.pool.get('res.users')
252         company_disc=self.browse(cr,uid,ids)
253         if not company_disc:
254             comp=res_obj.browse(cr,uid,uid).company_id.company_discount or 0.0
255         else:
256             comp= company_disc[0] and company_disc[0].company_id and  company_disc[0].company_id.company_discount  or 0.0
257         cr.execute("select discount from pos_order_line where order_id=%s and discount <= %s"%(ids[0],comp))
258         res=cr.fetchone()
259         cr.execute("select discount from pos_order_line where order_id=%s and discount > %s"%(ids[0],comp))
260         res2=cr.fetchone()
261         cr.execute("select journal_id from account_bank_statement_line where pos_statement_id=%s "%(ids[0]))
262         res3=cr.fetchall()
263         list_jrnl=[]
264         for r in res3:
265             cr.execute("select id from account_journal where name= '%s' and special_journal='t'"%(r[0]))
266             res3=cr.fetchone()
267             is_special=res3 and res3[0] or None
268             if is_special:
269                 list_jrnl.append(is_special)
270         r = {}
271         for order in self.browse(cr, uid, ids):
272             if order.state in ('paid','done','invoiced') and res and not res2 and not len(list_jrnl):
273                 r[order.id] = 'accepted'
274         return r
275
276     _columns = {
277         'name': fields.char('Order Description', size=64, required=True,
278             states={'draft': [('readonly', False)]}, readonly=True),
279         'company_id':fields.many2one('res.company', 'Company', required=True, readonly=True),
280         'num_sale': fields.char('Internal Note', size=64),
281         'shop_id': fields.many2one('sale.shop', 'Shop', required=True,
282             states={'draft': [('readonly', False)]}, readonly=True),
283         'date_order': fields.datetime('Date Ordered', readonly=True),
284         'date_payment': fields.function(_get_date_payment, method=True, string='Validation Date', type='date',  store=True),
285         'date_payment2': fields.function(_get_date_payment2, method=True, string='Payment Date', type='date',  store=True),
286         'date_validity': fields.date('Validity Date', required=True),
287         'user_id': fields.many2one('res.users', 'Connected Salesman', readonly=True),
288         'user_id1': fields.many2one('res.users', 'Salesman', required=True),
289         'user_id2': fields.many2one('res.users', 'Salesman Manager'),
290         'amount_tax': fields.function(_amount_tax, method=True, string='Taxes'),
291         'amount_total': fields.function(_amount_total, method=True, string='Total'),
292         'amount_paid': fields.function(_total_payment, 'Paid', states={'draft': [('readonly', False)]}, readonly=True, method=True),
293         'amount_return': fields.function(_total_return, 'Returned', method=True),
294         'lines': fields.one2many('pos.order.line', 'order_id', 'Order Lines', states={'draft': [('readonly', False)]}, readonly=True),
295         'price_type': fields.selection([
296             ('tax_excluded','Tax excluded')
297         ], 'Price method', required=True),
298         'statement_ids': fields.one2many('account.bank.statement.line','pos_statement_id','Payments'),
299         'payments': fields.one2many('pos.payment', 'order_id', 'Order Payments', states={'draft': [('readonly', False)]}, readonly=True),
300         'pricelist_id': fields.many2one('product.pricelist', 'Pricelist', required=True, states={'draft': [('readonly', False)]}, readonly=True),
301         'partner_id': fields.many2one( 'res.partner', 'Customer', change_default=True, select=1, states={'draft': [('readonly', False)], 'paid': [('readonly', False)]}),
302         'state': fields.selection([('draft', 'Draft'), ('payment', 'Payment'),
303                                     ('advance','Advance'),
304                                    ('paid', 'Paid'), ('done', 'Done'), ('invoiced', 'Invoiced'), ('cancel', 'Cancel')],
305                                   'State', readonly=True, ),
306         'invoice_id': fields.many2one('account.invoice', 'Invoice'),
307         'account_move': fields.many2one('account.move', 'Account Entry', readonly=True),
308         'pickings': fields.one2many('stock.picking', 'pos_order', 'Picking', readonly=True),
309         'last_out_picking': fields.many2one('stock.picking', 'Last Output Picking', readonly=True),
310         'first_name': fields.char('First Name', size=64),
311         'state_2': fields.function(_get_v,type='selection',selection=[('to_verify', 'To Verify'), ('accepted', 'Accepted'),
312             ('refused', 'Refused')], string='State', readonly=True, method=True, store=True),
313         
314     #    'last_name': fields.char('Last Name', size=64),
315     #    'street': fields.char('Street', size=64),
316     #    'zip2': fields.char('Zip', size=64),
317     #    'city': fields.char('City', size=64),
318     #    'mobile': fields.char('Mobile', size=64),
319     #    'email': fields.char('Email', size=64),
320         'note': fields.text('Internal Notes'),
321         'nb_print': fields.integer('Number of Print', readonly=True),
322         'sale_journal': fields.many2one('account.journal', 'Journal', required=True, states={'draft': [('readonly', False)]}, readonly=True, ),
323       #  'account_receivable': fields.many2one('account.account',
324       #      'Default Receivable', required=True, states={'draft': [('readonly', False)]},
325       #      readonly=True, ),
326         'invoice_wanted': fields.boolean('Create Invoice'),
327         'note_2': fields.char('Customer Note',size=64),
328         'type_rec': fields.char('Type of Receipt',size=64),
329         'remboursed': fields.boolean('Remboursed'),
330         'contract_number': fields.char('Contract Number', size=512, select=1),
331         'journal_entry': fields.boolean('Journal Entry'),
332     }
333
334
335     def _select_pricelist(self, cr, uid, context):
336         pricelist = self.pool.get('product.pricelist').search(cr, uid, [('name', '=', 'Public Pricelist')])
337         if pricelist:
338             return pricelist[0]
339         else:
340             return False
341
342     def _journal_default(self, cr, uid, context={}):
343         journal_list = self.pool.get('account.journal').search(cr, uid, [('type', '=', 'cash')])
344         if journal_list:
345             return journal_list[0]
346         else:
347             return False
348
349     _defaults = {
350         'user_id': lambda self, cr, uid, context: uid,
351         'user_id2': lambda self, cr, uid, context: uid,
352         'state': lambda *a: 'draft',
353         'price_type': lambda *a: 'tax_excluded',
354         'state_2': lambda *a: 'to_verify',
355         'name': lambda obj, cr, uid, context: obj.pool.get('ir.sequence').get(cr, uid, 'pos.order'),
356         'date_order': lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'),
357         'date_validity': lambda *a: (DateTime.now() + DateTime.RelativeDateTime(months=+6)).strftime('%Y-%m-%d'),
358         'nb_print': lambda *a: 0,
359         'company_id': lambda self,cr,uid,c: self.pool.get('res.users').browse(cr, uid, uid, c).company_id.id,
360         'sale_journal': _sale_journal_get,
361         'invoice_wanted': lambda *a: False,
362         'shop_id': _shop_get,
363         'pricelist_id': _select_pricelist,
364     }
365
366
367     def test_order_lines(self, cr, uid, order, context={}):
368         if not order.lines:
369             raise osv.except_osv(_('Error'), _('No order lines defined for this sale.'))
370
371         wf_service = netsvc.LocalService("workflow")
372         wf_service.trg_validate(uid, 'pos.order', order.id, 'paid', cr)
373         return True
374
375     def dummy_button(self, cr, uid, order, context={}):
376         return True
377
378     def test_paid(self, cr, uid, ids, context=None):
379         for order in self.browse(cr, uid, ids, context):
380             if order.lines and not order.amount_total:
381                 return True
382             if (not order.lines) or (not order.statement_ids) or \
383                 Decimal(str(order.amount_total))!=Decimal(str(order.amount_paid)):
384                 return False
385         return True
386
387     def _get_qty_differences(self, orders, old_picking):
388         """check if the customer changed the product quantity"""
389         order_dict = {}
390         for order in orders:
391             for line in order.lines:
392                 order_dict[line.product_id.id] = line
393
394         # check the quantity differences:
395         diff_dict = {}
396         for line in old_picking.move_lines:
397             order_line = order_dict.get(line.product_id.id)
398             if not order_line:
399                 deleted = True
400                 qty_to_delete_from_original_picking = line.product_qty
401                 diff_dict[line.product_id.id] = (deleted, qty_to_delete_from_original_picking)
402             elif line.product_qty != order_line.qty:
403                 deleted = False
404                 qty_to_delete_from_original_picking = line.product_qty - order_line.qty
405                 diff_dict[line.product_id.id] = (deleted, qty_to_delete_from_original_picking)
406
407         return diff_dict
408
409     def _split_picking(self, cr, uid, ids, context, old_picking, diff_dict):
410         """if the customer changes the product quantity, split the picking in two"""
411         # create a copy of the original picking and adjust the product qty:
412         picking_model = self.pool.get('stock.picking')
413         defaults = {
414             'note': "Partial picking from customer", # add a note to tell why we create a new picking
415             'name': self.pool.get('ir.sequence').get(cr, uid, 'stock.picking.out'), # increment the sequence
416         }
417
418         new_picking_id = picking_model.copy(cr, uid, old_picking.id, defaults) # state = 'draft'
419         new_picking = picking_model.browse(cr, uid, new_picking_id, context)
420
421         for line in new_picking.move_lines:
422             p_id = line.product_id.id
423             if p_id in diff_dict:
424                 diff = diff_dict[p_id]
425                 deleted = diff[0]
426                 qty_to_del = diff[1]
427                 if deleted: # product has been deleted (customer didn't took it):
428                     # delete this product from old picking:
429                     for old_line in old_picking.move_lines:
430                         if old_line.product_id.id == p_id:
431                             old_line.write({'state': 'draft'}, context=context) # cannot delete if not draft
432                             old_line.unlink(context=context)
433                 elif qty_to_del > 0: # product qty has been modified (customer took less than the ordered quantity):
434                     # subtract qty from old picking:
435                     for old_line in old_picking.move_lines:
436                         if old_line.product_id.id == p_id:
437                             old_line.write({'product_qty': old_line.product_qty - qty_to_del}, context=context)
438                     # add qty to new picking:
439                     line.write({'product_qty': qty_to_del}, context=context)
440                 else: # product hasn't changed (customer took it without any change):
441                     # delete this product from new picking:
442                     line.unlink(context=context)
443             else:
444                 # delete it in the new picking:
445                 line.unlink(context=context)
446
447     def create_picking(self, cr, uid, ids, context={}):
448         """Create a picking for each order and validate it."""
449         picking_obj = self.pool.get('stock.picking')
450
451         orders = self.browse(cr, uid, ids, context)
452         for order in orders:
453             if not order.last_out_picking:
454                 new = True
455                 picking_id = picking_obj.create(cr, uid, {
456                     'origin': order.name,
457                     'type': 'out',
458                     'state': 'draft',
459                     'move_type': 'direct',
460                     'note': 'POS notes ' + (order.note or ""),
461                     'invoice_state': 'none',
462                     'auto_picking': True,
463                     'pos_order': order.id,
464                     })
465                 self.write(cr, uid, [order.id], {'last_out_picking': picking_id})
466             else:
467                 picking_id = order.last_out_picking.id
468                 picking_obj.write(cr, uid, [picking_id], {'auto_picking': True})
469                 picking = picking_obj.browse(cr, uid, [picking_id], context)[0]
470                 new = False
471
472                 # split the picking (if product quantity has changed):
473                 diff_dict = self._get_qty_differences(orders, picking)
474                 if diff_dict:
475                     self._split_picking(cr, uid, ids, context, picking, diff_dict)
476
477             if new:
478                 for line in order.lines:
479                     if line.product_id and line.product_id.type=='service':
480                         continue
481                     prop_ids = self.pool.get("ir.property").search(cr, uid, [('name', '=', 'property_stock_customer')])
482                     val = self.pool.get("ir.property").browse(cr, uid, prop_ids[0]).value
483                     cr.execute("select s.id from stock_location s, stock_warehouse w where w.lot_stock_id=s.id and w.id= %d "%(order.shop_id.warehouse_id.id))
484                     res=cr.fetchone()
485                     location_id=res and res[0] or None
486 #                    location_id = order and order.shop_id and order.shop_id.warehouse_id and order.shop_id.warehouse_id.lot_stock_id.id or None
487                     stock_dest_id = int(val.split(',')[1])
488                     if line.qty < 0:
489                         location_id, stock_dest_id = stock_dest_id, location_id
490
491                         self.pool.get('stock.move').create(cr, uid, {
492                             'name': 'Stock move (POS %d)' % (order.id, ),
493                             'product_uom': line.product_id.uom_id.id,
494                             'product_uos': line.product_id.uom_id.id,
495                             'picking_id': picking_id,
496                             'product_id': line.product_id.id,
497                             'product_uos_qty': abs(line.qty),
498                             'product_qty': abs(line.qty),
499                             'tracking_id': False,
500                             'pos_line_id': line.id,
501                             'state': 'waiting',
502                             'location_id': location_id,
503                             'location_dest_id': stock_dest_id,
504                         })
505
506             wf_service = netsvc.LocalService("workflow")
507             wf_service.trg_validate(uid, 'stock.picking', picking_id, 'button_confirm', cr)
508             self.pool.get('stock.picking').force_assign(cr, uid, [picking_id], context)
509
510         return True
511
512     def set_to_draft(self, cr, uid, ids, *args):
513         if not len(ids):
514             return False
515
516         self.write(cr, uid, ids, {'state': 'draft'})
517
518         wf_service = netsvc.LocalService("workflow")
519         for i in ids:
520             wf_service.trg_create(uid, 'pos.order', i, cr)
521         return True
522
523     def button_invalidate(self, cr, uid, ids, *args):
524         res_obj = self.pool.get('res.company')
525         try:
526             part_company=res_obj.browse(cr,uid,uid) and res_obj.browse(cr,uid,uid).parent_id and res_obj.browse(cr,uid,uid).parent_id.id or None
527         except Exception, e:
528             raise osv.except_osv(_('Error'), _('You don\'t have enough access to validate this sale!'))
529         if part_company:
530             raise osv.except_osv(_('Error'), _('You don\'t have enough access to validate this sale!'))
531         return True
532
533     def button_validate(self, cr, uid, ids, *args):
534         res_obj = self.pool.get('res.company')
535         try:
536             part_company=res_obj.browse(cr,uid,uid) and res_obj.browse(cr,uid,uid).parent_id and res_obj.browse(cr,uid,uid).parent_id.id or None
537         except Exception, e:
538             raise osv.except_osv(_('Error'), _('You don\'t have enough access to validate this sale!'))
539         if part_company:
540             raise osv.except_osv(_('Error'), _('You don\'t have enough access to validate this sale!'))
541         for order in self.browse(cr, uid, ids):
542             if not order.date_payment:
543                 cr.execute("select max(date) from account_bank_statement_line where pos_statement_id=%d"%(order.id))
544                 val=cr.fetchone()
545                 val=val and val[0] or None
546                 if val:
547                     cr.execute("Update pos_order set date_payment='%s' where id = %d"%(val, order.id))
548         return True
549
550
551     def cancel_order(self, cr, uid, ids, context=None):
552         self.write(cr, uid, ids, {'state': 'cancel'})
553         self.cancel_picking(cr, uid, ids, context={})
554         return True
555
556     def add_payment(self, cr, uid, order_id, data, context=None):
557         """Create a new payment for the order"""
558         res_obj = self.pool.get('res.company')
559         statementl_obj = self.pool.get('account.bank.statement.line')
560         prod_obj = self.pool.get('product.product')
561         flag=''
562         curr_c=self.pool.get('res.users').browse(cr, uid, uid).company_id
563         curr_company=curr_c.id
564         order = self.browse(cr, uid, order_id, context)
565         if not order.num_sale and data['num_sale']:
566             self.write(cr,uid,order_id,{'num_sale': data['num_sale']})
567         ids_new=[]
568         if order.invoice_wanted and not order.partner_id:
569             raise osv.except_osv(_('Error'), _('Cannot create invoice without a partner.'))
570         args = {
571             'amount': data['amount'],
572             }
573         if 'payment_date' in data.keys():
574             args['date'] = data['payment_date']
575         if 'payment_name' in data.keys():
576             args['name'] = data['payment_name'] + ' ' +order.name
577         account_def = self.pool.get('ir.property').get(cr, uid, 'property_account_receivable', 'res.partner', context=context)
578         args['account_id'] = order.partner_id and order.partner_id.property_account_receivable and order.partner_id.property_account_receivable.id or account_def or curr_c.account_receivable.id
579         if data.get('is_acc',False):
580             args['is_acc']=data['is_acc']
581             args['account_id']= prod_obj.browse(cr,uid, data['product_id']).property_account_income and prod_obj.browse(cr,uid, data['product_id']).property_account_income.id
582             if not args['account_id']:
583                 raise osv.except_osv(_('Error'), _('Please provide an account for the product: %s')%(prod_obj.browse(cr,uid, data['product_id']).name))
584         args['partner_id'] = order.partner_id and order.partner_id.id or None
585         args['ref'] = order.contract_number or None
586
587         statement_obj= self.pool.get('account.bank.statement')
588         statement_id = statement_obj.search(cr,uid, [
589                                                      ('journal_id', '=', data['journal']),
590                                                      ('company_id', '=', curr_company),
591                                                      ('user_id', '=', uid),
592                                                      ('state', '=', 'open')])
593         if len(statement_id)==0:
594             raise osv.except_osv(_('Error !'), _('You have to open at least one cashbox'))
595         if statement_id:
596             statement_id=statement_id[0]
597         args['statement_id']= statement_id
598         args['pos_statement_id']= order_id
599         args['journal_id']= data['journal']
600         statement_line_id = self.pool.get('account.bank.statement.line').create(cr, uid, args)
601         ids_new.append(statement_id)
602
603         wf_service = netsvc.LocalService("workflow")
604         wf_service.trg_validate(uid, 'pos.order', order_id, 'paid', cr)
605         wf_service.trg_write(uid, 'pos.order', order_id, cr)
606
607         return statement_id
608
609     def add_product(self, cr, uid, order_id, product_id, qty, context=None):
610         """Create a new order line the order"""
611         line_obj = self.pool.get('pos.order.line')
612         values = self.read(cr, uid, order_id, ['partner_id', 'pricelist_id'])
613
614         pricelist = values['pricelist_id'] and values['pricelist_id'][0]
615         product = values['partner_id'] and values['partner_id'][0]
616
617         price = line_obj.price_by_product(cr, uid, [],
618                 pricelist, product_id, qty, product)
619
620         order_line_id = line_obj.create(cr, uid, {
621             'order_id': order_id,
622             'product_id': product_id,
623             'qty': qty,
624             'price_unit': price,
625             })
626         wf_service = netsvc.LocalService("workflow")
627         wf_service.trg_write(uid, 'pos.order', order_id, cr)
628
629         return order_line_id
630
631     def refund(self, cr, uid, ids, context={}):
632         clone_list = []
633         line_obj = self.pool.get('pos.order.line')
634
635         for order in self.browse(cr, uid, ids):
636             clone_id = self.copy(cr, uid, order.id, {
637                 'name': order.name + ' REFUND',
638                 'date_order': time.strftime('%Y-%m-%d'),
639                 'state': 'draft',
640                 'note': 'REFUND\n'+ (order.note or ''),
641                 'invoice_id': False,
642                 'nb_print': 0,
643                 'statement_ids': False,
644                 })
645             clone_list.append(clone_id)
646
647
648         for clone in self.browse(cr, uid, clone_list):
649             for order_line in clone.lines:
650                 line_obj.write(cr, uid, [order_line.id], {
651                     'qty': -order_line.qty
652                     })
653         return clone_list
654
655     def action_invoice(self, cr, uid, ids, context={}):
656         res_obj = self.pool.get('res.company')
657         inv_ref = self.pool.get('account.invoice')
658         inv_line_ref = self.pool.get('account.invoice.line')
659         inv_ids = []
660
661         for order in self.browse(cr, uid, ids, context):
662             curr_c = order.user_id1.company_id
663             if order.invoice_id:
664                 inv_ids.append(order.invoice_id.id)
665                 continue
666
667             if not order.partner_id:
668                 raise osv.except_osv(_('Error'), _('Please provide a partner for the sale.'))
669
670             cr.execute('select a.id from account_account a, res_company p where p.account_receivable=a.id and p.id=%s', (curr_c.id, ))
671             res=cr.fetchone()
672             acc=res and res[0] or None
673             inv = {
674                 'name': 'Invoice from POS: '+order.name,
675                 'origin': order.name,
676                 'account_id':acc,
677                 'journal_id':order.sale_journal.id or None,
678                 'type': 'out_invoice',
679                 'reference': order.name,
680                 'partner_id': order.partner_id.id,
681                 'comment': order.note or '',
682             }
683             inv.update(inv_ref.onchange_partner_id(cr, uid, [], 'out_invoice', order.partner_id.id)['value'])
684             if not inv.get('account_id', None):
685                 inv['account_id'] = acc
686             inv_id = inv_ref.create(cr, uid, inv, context)
687
688             self.write(cr, uid, [order.id], {'invoice_id': inv_id, 'state': 'invoiced'})
689             inv_ids.append(inv_id)
690             for line in order.lines:
691                 inv_line = {
692                     'invoice_id': inv_id,
693                     'product_id': line.product_id.id,
694                     'quantity': line.qty,
695                 }
696                 inv_name = self.pool.get('product.product').name_get(cr, uid, [line.product_id.id], context=context)[0][1]
697                 
698                 inv_line.update(inv_line_ref.product_id_change(cr, uid, [],
699                                                                line.product_id.id,
700                                                                line.product_id.uom_id.id,
701                                                                line.qty, partner_id = order.partner_id.id, fposition_id=order.partner_id.property_account_position.id)['value'])
702                 inv_line['price_unit'] = line.price_unit
703                 inv_line['discount'] = line.discount
704                 inv_line['account_id'] = acc
705                 inv_line['name'] = inv_name
706
707                 inv_line['invoice_line_tax_id'] = ('invoice_line_tax_id' in inv_line)\
708                     and [(6, 0, inv_line['invoice_line_tax_id'])] or []
709                 inv_line_ref.create(cr, uid, inv_line, context)
710
711         for i in inv_ids:
712             wf_service = netsvc.LocalService("workflow")
713             wf_service.trg_validate(uid, 'account.invoice', i, 'invoice_open', cr)
714         return inv_ids
715
716     def create_account_move(self, cr, uid, ids, context=None):
717         account_move_obj = self.pool.get('account.move')
718         account_move_line_obj = self.pool.get('account.move.line')
719         account_period_obj = self.pool.get('account.period')
720         account_tax_obj = self.pool.get('account.tax')
721         period = account_period_obj.find(cr, uid, context=context)[0]
722
723         for order in self.browse(cr, uid, ids, context=context):
724             curr_c = self.pool.get('res.users').browse(cr, uid, uid).company_id
725             comp_id = self.pool.get('res.users').browse(cr, order.user_id.id, order.user_id.id).company_id
726             comp_id=comp_id and comp_id.id or False
727             to_reconcile = []
728             group_tax = {}
729             account_def = self.pool.get('ir.property').get(cr, uid, 'property_account_receivable', 'res.partner', context=context)
730             order_account = order.partner_id and order.partner_id.property_account_receivable and order.partner_id.property_account_receivable.id or account_def or curr_c.account_receivable.id
731
732             # Create an entry for the sale
733             move_id = account_move_obj.create(cr, uid, {
734                 'journal_id': order.sale_journal.id,
735                 'period_id': period,
736                 }, context=context)
737
738             # Create an move for each order line
739             for line in order.lines:
740
741                 tax_amount = 0
742                 taxes = [t for t in line.product_id.taxes_id]
743                 if order.price_type=='tax_excluded':
744                     computed_taxes = account_tax_obj.compute(
745                         cr, uid, taxes, line.price_unit, line.qty)
746                 else:
747                     computed_taxes = account_tax_obj.compute_inv(
748                         cr, uid, taxes, line.price_unit, line.qty)
749
750                 for tax in computed_taxes:
751                     tax_amount += round(tax['amount'], 2)
752                     group_key = (tax['tax_code_id'],
753                                 tax['base_code_id'],
754                                 tax['account_collected_id'])
755
756                     if group_key in group_tax:
757                         group_tax[group_key] += round(tax['amount'], 2)
758                     else:
759                         group_tax[group_key] = round(tax['amount'], 2)
760                 if order.price_type!='tax_excluded':
761                     amount = line.price_subtotal - tax_amount
762                 else:
763                     amount = line.price_subtotal
764
765                 # Search for the income account
766                 if  line.product_id.property_account_income.id:
767                     income_account = line.\
768                                     product_id.property_account_income.id
769                 elif line.product_id.categ_id.\
770                         property_account_income_categ.id:
771                     income_account = line.product_id.categ_id.\
772                                     property_account_income_categ.id
773                 else:
774                     raise osv.except_osv(_('Error !'), _('There is no income '\
775                         'account defined for this product: "%s" (id:%d)') \
776                         % (line.product_id.name, line.product_id.id, ))
777
778
779                 # Empty the tax list as long as there is no tax code:
780                 tax_code_id = False
781                 tax_amount = 0
782                 while computed_taxes:
783                     tax = computed_taxes.pop(0)
784                     if amount > 0:
785                         tax_code_id = tax['base_code_id']
786                         tax_amount = line.price_subtotal * tax['base_sign']
787                     else:
788                         tax_code_id = tax['ref_base_code_id']
789                         tax_amount = line.price_subtotal * tax['ref_base_sign']
790                     # If there is one we stop
791                     if tax_code_id:
792                         break
793
794                 # Create a move for the line
795                 account_move_line_obj.create(cr, uid, {
796                     'name': "aa"+order.name,
797                     'date': order.date_order,
798                     'ref': order.contract_number or order.name,
799                     'quantity': line.qty,
800                     'product_id':line.product_id.id,
801                     'move_id': move_id,
802                     'account_id': income_account,
803                     'company_id': comp_id,
804                     'credit': ((amount>0) and amount) or 0.0,
805                     'debit': ((amount<0) and -amount) or 0.0,
806                     'journal_id': order.sale_journal.id,
807                     'period_id': period,
808                     'tax_code_id': tax_code_id,
809                     'tax_amount': tax_amount,
810                 }, context=context)
811
812                 # For each remaining tax with a code, whe create a move line
813                 for tax in computed_taxes:
814                     if amount > 0:
815                         tax_code_id = tax['base_code_id']
816                         tax_amount = line.price_subtotal * tax['base_sign']
817                     else:
818                         tax_code_id = tax['ref_base_code_id']
819                         tax_amount = line.price_subtotal * tax['ref_base_sign']
820                     if not tax_code_id:
821                         continue
822
823                     account_move_line_obj.create(cr, uid, {
824                         'name': "bb"+order.name,
825                         'date': order.date_order,
826                         'ref': order.contract_number or order.name,
827                         'product_id':line.product_id.id,
828                         'quantity': line.qty,
829                         'move_id': move_id,
830                         'account_id': income_account,
831                         'company_id': comp_id,
832                         'credit': 0.0,
833                         'debit': 0.0,
834                         'journal_id': order.sale_journal.id,
835                         'period_id': period,
836                         'tax_code_id': tax_code_id,
837                         'tax_amount': tax_amount,
838                     }, context=context)
839
840
841             # Create a move for each tax group
842             (tax_code_pos, base_code_pos, account_pos)= (0, 1, 2)
843             for key, amount in group_tax.items():
844                 account_move_line_obj.create(cr, uid, {
845                     'name':"cc"+order.name,
846                     'date': order.date_order,
847                     'ref': order.contract_number or order.name,
848                     'move_id': move_id,
849                     'company_id': comp_id,
850                     'quantity': line.qty,
851                     'product_id':line.product_id.id,
852                     'account_id': key[account_pos],
853                     'credit': ((amount>0) and amount) or 0.0,
854                     'debit': ((amount<0) and -amount) or 0.0,
855                     'journal_id': order.sale_journal.id,
856                     'period_id': period,
857                     'tax_code_id': key[tax_code_pos],
858                     'tax_amount': amount,
859                 }, context=context)
860
861             # counterpart
862             to_reconcile.append(account_move_line_obj.create(cr, uid, {
863                 'name': "dd"+order.name,
864                 'date': order.date_order,
865                 'ref': order.contract_number or order.name,
866                 'move_id': move_id,
867                 'company_id': comp_id,
868                 'account_id': order_account,
869                 'credit': ((order.amount_total<0) and -order.amount_total)\
870                     or 0.0,
871                 'debit': ((order.amount_total>0) and order.amount_total)\
872                     or 0.0,
873                 'journal_id': order.sale_journal.id,
874                 'period_id': period,
875             }, context=context))
876
877
878             # search the account receivable for the payments:
879             account_receivable = order.sale_journal.default_credit_account_id.id
880             if not account_receivable:
881                 raise  osv.except_osv(_('Error !'),
882                     _('There is no receivable account defined for this journal:'\
883                     ' "%s" (id:%d)') % (order.sale_journal.name, order.sale_journal.id, ))
884             am=0.0
885             for payment in order.statement_ids:
886                 am+=payment.amount
887
888                 if am > 0:
889                     payment_account = \
890                         payment.statement_id.journal_id.default_debit_account_id.id
891                 else:
892                     payment_account = \
893                         payment.statement_id.journal_id.default_credit_account_id.id
894
895                 # Create one entry for the payment
896                 if payment.is_acc:
897                     continue
898                 payment_move_id = account_move_obj.create(cr, uid, {
899                     'journal_id': payment.statement_id.journal_id.id,
900                     'period_id': period,
901                 }, context=context)
902
903             for stat_l in order.statement_ids:
904                 if stat_l.is_acc and len(stat_l.move_ids):
905                     for st in stat_l.move_ids:
906                         for s in st.line_id:
907                             if s.credit:
908                                 account_move_line_obj.copy(cr, uid, s.id, { 'debit': s.credit,
909                                                                             'statement_id': False,
910                                                                             'credit': s.debit})
911                                 account_move_line_obj.copy(cr, uid, s.id, {
912                                                                         'statement_id': False,
913                                                                         'account_id':order_account
914                                                                      })
915            #     account_move_obj.button_validate(cr, uid, [move_id, payment_move_id], context=context)
916             self.write(cr,uid,order.id,{'state':'done'})
917          #       account_move_line_obj.reconcile(cr, uid, to_reconcile, type='manual', context=context)
918         return True
919
920     def cancel_picking(self, cr, uid, ids, context=None):
921         for order in self.browse(cr, uid, ids, context=context):
922             for picking in order.pickings:
923                 self.pool.get('stock.picking').unlink(cr, uid, [picking.id], context)
924         return True
925
926
927     def action_payment(self, cr, uid, ids, context=None):
928         vals = {'state': 'payment'}
929         for pos in self.browse(cr, uid, ids):
930             create_contract_nb = False
931             for line in pos.lines:
932                 if line.product_id.product_type == 'MD':
933                     create_contract_nb = True
934                     break
935             if create_contract_nb:
936                 seq = self.pool.get('ir.sequence').get(cr, uid, 'pos.user_%s' % pos.user_id1.login)
937                 vals['contract_number'] ='%s-%s' % (pos.user_id1.login, seq)
938         self.write(cr, uid, ids, vals)
939
940     def action_paid(self, cr, uid, ids, context=None):
941         if not context:
942             context = {}
943         if context.get('flag',False):
944             self.create_picking(cr, uid, ids, context={})
945             self.write(cr, uid, ids, {'state': 'paid'})
946         else:
947             context['flag']=True
948         return True
949
950     def action_cancel(self, cr, uid, ids, context=None):
951         self.write(cr, uid, ids, {'state': 'cancel'})
952         return True
953
954     def action_done(self, cr, uid, ids, context=None):
955         for order in self.browse(cr, uid, ids, context=context):
956             if not order.journal_entry:
957                 self.create_account_move(cr, uid, ids, context={})
958         #self.write(cr, uid, ids, {'state': 'done'})
959         return True
960
961     def compute_state(self, cr, uid, id):
962         cr.execute("select act.id, act.name from wkf_activity act "
963                    "inner join wkf_workitem item on act.id=item.act_id "
964                    "inner join wkf_instance inst on item.inst_id=inst.id "
965                    "inner join wkf on inst.wkf_id=wkf.id "
966                    "where wkf.osv='pos.order' and inst.res_id=%s "
967                    "order by act.name", (id,))
968         return [name for id, name in cr.fetchall()]
969
970 pos_order()
971
972 class account_bank_statement(osv.osv):
973     _inherit = 'account.bank.statement'
974     _columns={
975         'user_id': fields.many2one('res.users',ondelete='cascade',string='User', readonly=True),
976     }
977     _defaults = {
978         'user_id': lambda self,cr,uid,c: self.pool.get('res.users').browse(cr, uid, uid, c).id
979     }
980 account_bank_statement()
981
982 class account_bank_statement_line(osv.osv):
983     _inherit = 'account.bank.statement.line'
984     def _get_statement_journal(self, cr, uid, ids, context, *a):
985         res = {}
986         for line in self.browse(cr, uid, ids):
987             res[line.id] = line.statement_id and line.statement_id.journal_id and line.statement_id.journal_id.name or None
988         return res
989     _columns={
990         'journal_id': fields.function(_get_statement_journal, method=True,store=True, string='Journal', type='char', size=64),
991         'am_out':fields.boolean("To count"),
992         'is_acc':fields.boolean("Is accompte"),
993         'pos_statement_id': fields.many2one('pos.order',ondelete='cascade'),
994     }
995 account_bank_statement_line()
996
997 class pos_order_line(osv.osv):
998     _name = "pos.order.line"
999     _description = "Lines of Point of Sale"
1000
1001     def _get_amount(self, cr, uid, ids, field_name, arg, context):
1002         res = {}
1003         for line in self.browse(cr, uid, ids):
1004             price = self.price_by_product(cr, uid, ids, line.order_id.pricelist_id.id, line.product_id.id, line.qty, line.order_id.partner_id.id)
1005             res[line.id]=price
1006         return res
1007
1008     def _amount_line_ttc(self, cr, uid, ids, field_name, arg, context):
1009         res = {}
1010         account_tax_obj = self.pool.get('account.tax')
1011         for line in self.browse(cr, uid, ids):
1012             tax_amount = 0.0
1013             taxes = [t for t in line.product_id.taxes_id]
1014             computed_taxes = account_tax_obj.compute(cr, uid, taxes, line.price_unit, line.qty)
1015             for tax in computed_taxes:
1016                 tax_amount += tax['amount']
1017             price = self.price_by_product(cr, uid, ids, line.order_id.pricelist_id.id, line.product_id.id, line.qty, line.order_id.partner_id.id)
1018             if line.discount!=0.0:
1019                 res[line.id] = line.price_unit * line.qty * (1 - (line.discount or 0.0) / 100.0)
1020             else:
1021                 res[line.id]=line.price_unit*line.qty
1022             res[line.id] = res[line.id] + tax_amount
1023             
1024         return res
1025     def _amount_line(self, cr, uid, ids, field_name, arg, context):
1026         res = {}
1027
1028         for line in self.browse(cr, uid, ids):
1029             price = self.price_by_product(cr, uid, ids, line.order_id.pricelist_id.id, line.product_id.id, line.qty, line.order_id.partner_id.id)
1030             if line.discount!=0.0:
1031                 res[line.id] = line.price_unit * line.qty * (1 - (line.discount or 0.0) / 100.0)
1032 #                res[line.id] = price * line.qty * (1 - (line.discount or 0.0) / 100.0)
1033             else:
1034                 res[line.id]=line.price_unit*line.qty
1035         return res
1036
1037     def price_by_product(self, cr, uid, ids, pricelist, product_id, qty=0, partner_id=False):
1038         if not product_id:
1039             return 0.0
1040         if not pricelist:
1041             raise osv.except_osv(_('No Pricelist !'),
1042                 _('You have to select a pricelist in the sale form !\n' \
1043                 'Please set one before choosing a product.'))
1044         p_obj = self.pool.get('product.product').browse(cr,uid,product_id).list_price
1045         price = self.pool.get('product.pricelist').price_get(cr, uid,
1046             [pricelist], product_id, qty or 1.0, partner_id)[pricelist] 
1047         if price is False:
1048             raise osv.except_osv(_('No valid pricelist line found !'),
1049                 _("Couldn't find a pricelist line matching this product" \
1050                 " and quantity.\nYou have to change either the product," \
1051                 " the quantity or the pricelist."))
1052         return price
1053
1054     def onchange_product_id(self, cr, uid, ids, pricelist, product_id, qty=0, partner_id=False):
1055         price = self.price_by_product(cr, uid, ids, pricelist, product_id, qty, partner_id)
1056         self.write(cr,uid,ids,{'price_unit':price})
1057         return {'value': {'price_unit': price}, 'qty': 1}
1058
1059     def onchange_subtotal(self, cr, uid, ids, discount, price, pricelist,qty,partner_id, product_id,*a):
1060         prod_obj = self.pool.get('product.product')
1061         price_f = self.price_by_product(cr, uid, ids, pricelist, product_id, qty, partner_id)
1062         prod_id=''
1063         if product_id:
1064             prod_id=prod_obj.browse(cr,uid,product_id).disc_controle
1065         disc=0.0
1066         if (disc != 0.0 or prod_id) and price_f>0:
1067             disc=100-(price/price_f*100)
1068             return {'value':{'discount':disc, 'price_unit':price_f}}#,'notice':''}}#, 'price_subtotal':(price_f*qty*(1-disc))}}
1069         return {}
1070
1071     def onchange_ded(self, cr, uid,ids, val_ded,price_u,*a):
1072         pos_order = self.pool.get('pos.order.line')
1073         res_obj = self.pool.get('res.users')
1074         res_company = self.pool.get('res.company')
1075         comp = res_obj.browse(cr,uid,uid).company_id.company_discount or 0.0
1076         val=0.0
1077         if val_ded and price_u:
1078             val=100.0*val_ded/price_u
1079         if val > comp:
1080             return {'value': {'discount':val, 'notice':'' }}
1081         return {'value': {'discount':val}}
1082
1083     def onchange_discount(self, cr, uid,ids, discount,price,*a):
1084         pos_order = self.pool.get('pos.order.line')
1085         res_obj = self.pool.get('res.users')
1086         res_company = self.pool.get('res.company')
1087         company_disc = pos_order.browse(cr,uid,ids)
1088         if discount:
1089             if not company_disc:
1090                 comp=res_obj.browse(cr,uid,uid).company_id.company_discount or 0.0
1091             else:
1092                 comp= company_disc[0] and company_disc[0].order_id.company_id and  company_disc[0].order_id.company_id.company_discount  or 0.0
1093
1094             if discount > comp :
1095                 return {'value': {'notice':'','price_ded':price*discount*0.01 or 0.0  }}
1096             else:
1097                 return {'value': {'notice':'Minimum Discount','price_ded':price*discount*0.01 or 0.0  }}
1098         else :
1099             return {'value': {'notice':'No Discount', 'price_ded':price*discount*0.01 or 0.0 }}
1100     _columns = {
1101         'name': fields.char('Line Description', size=512),
1102         'company_id':fields.many2one('res.company', 'Company', required=True),
1103         'notice': fields.char('Discount Notice', size=128, required=True),
1104         'serial_number': fields.char('Serial Number', size=128),
1105 #        'contract_number': fields.char('Contract Number', size=512),
1106         'product_id': fields.many2one('product.product', 'Product', domain=[('sale_ok', '=', True)], required=True, change_default=True),
1107 #        'price_unit': fields.float('Unit Price'),
1108         'price_unit': fields.function(_get_amount, method=True, string='Unit Price', store=True),
1109         'price_ded': fields.float('Discount(Amount)'),
1110         'qty': fields.float('Quantity'),
1111         'qty_rfd': fields.float('Refunded Quantity'),
1112         'price_subtotal': fields.function(_amount_line, method=True, string='Subtotal w/o Tax'),
1113         'price_subtotal_incl': fields.function(_amount_line_ttc, method=True, string='Subtotal'),
1114         'discount': fields.float('Discount (%)', digits=(16, 2)),
1115         'order_id': fields.many2one('pos.order', 'Order Ref', ondelete='cascade'),
1116         'create_date': fields.datetime('Creation Date', readonly=True),
1117         }
1118
1119     _defaults = {
1120         'name': lambda obj, cr, uid, context: obj.pool.get('ir.sequence').get(cr, uid, 'pos.order.line'),
1121         'qty': lambda *a: 1,
1122         'discount': lambda *a: 0.0,
1123         'price_ded': lambda *a: 0.0,
1124         'notice': lambda *a: 'No Discount',
1125         'company_id': lambda self,cr,uid,c: self.pool.get('res.users').browse(cr, uid, uid, c).company_id.id,
1126         }
1127
1128     def create(self, cr, user, vals, context={}):
1129         if vals.get('product_id'):
1130             return super(pos_order_line, self).create(cr, user, vals, context)
1131         return False
1132
1133     def write(self, cr, user, ids, values, context={}):
1134         if 'product_id' in values and not values['product_id']:
1135             return False
1136         return super(pos_order_line, self).write(cr, user, ids, values, context)
1137
1138     def _scan_product(self, cr, uid, ean, qty, order):
1139         # search pricelist_id
1140         pricelist_id = self.pool.get('pos.order').read(cr, uid, [order], ['pricelist_id'] )
1141         if not pricelist_id:
1142             return False
1143
1144         new_line = True
1145
1146         product_id = self.pool.get('product.product').search(cr, uid, [('ean13','=', ean)])
1147         if not product_id:
1148            return False
1149
1150         # search price product
1151         product = self.pool.get('product.product').read(cr, uid, product_id)
1152         product_name = product[0]['name']
1153         price = self.price_by_product(cr, uid, 0, pricelist_id[0]['pricelist_id'][0], product_id[0], 1)
1154
1155         order_line_ids = self.search(cr, uid, [('name','=',product_name),('order_id','=',order)])
1156         if order_line_ids:
1157             new_line = False
1158             order_line_id = order_line_ids[0]
1159             qty += self.read(cr, uid, order_line_ids[0], ['qty'])['qty']
1160
1161         if new_line:
1162             vals = {'product_id': product_id[0],
1163                     'price_unit': price,
1164                     'qty': qty,
1165                     'name': product_name,
1166                     'order_id': order,
1167                    }
1168             line_id = self.create(cr, uid, vals)
1169             if not line_id:
1170                 raise wizard.except_wizard(_('Error'), _('Create line failed !'))
1171         else:
1172             vals = {
1173                 'qty': qty,
1174                 'price_unit': price
1175             }
1176             line_id = self.write(cr, uid, order_line_id, vals)
1177             if not line_id:
1178                 raise wizard.except_wizard(_('Error'), _('Modify line failed !'))
1179             line_id = order_line_id
1180
1181         price_line = float(qty)*float(price)
1182         return {'name': product_name, 'product_id': product_id[0], 'price': price, 'price_line': price_line ,'qty': qty }
1183
1184 pos_order_line()
1185
1186
1187 class pos_payment(osv.osv):
1188     _name = 'pos.payment'
1189     _description = 'Pos Payment'
1190
1191     def _journal_get(self, cr, uid, context={}):
1192         obj = self.pool.get('account.journal')
1193         ids = obj.search(cr, uid, [('type', '=', 'cash')])
1194         res = obj.read(cr, uid, ids, ['id', 'name'], context)
1195         res = [(r['id'], r['name']) for r in res]
1196         return res
1197
1198     def _journal_default(self, cr, uid, context={}):
1199         journal_list = self.pool.get('account.journal').search(cr, uid, [('type', '=', 'cash')])
1200         if journal_list:
1201             return journal_list[0]
1202         else:
1203             return False
1204
1205     _columns = {
1206         'name': fields.char('Description', size=64),
1207         'order_id': fields.many2one('pos.order', 'Order Ref', required=True, ondelete='cascade'),
1208         'journal_id': fields.many2one('account.journal', "Journal", required=True),
1209         'payment_id': fields.many2one('account.payment.term','Payment Term', select=True),
1210         'payment_nb': fields.char('Piece Number', size=32),
1211         'payment_name': fields.char('Payment Name', size=32),
1212         'payment_date': fields.date('Payment Date', required=True),
1213         'amount': fields.float('Amount', required=True),
1214     }
1215     _defaults = {
1216         'name': lambda obj, cr, uid, context: obj.pool.get('ir.sequence').get(cr, uid, 'pos.payment'),
1217         'journal_id': _journal_default,
1218         'payment_date':  lambda *a: time.strftime('%Y-%m-%d'),
1219     }
1220
1221     def create(self, cr, user, vals, context={}):
1222         if vals.get('journal_id') and vals.get('amount'):
1223             return super(pos_payment, self).create(cr, user, vals, context)
1224         return False
1225
1226     def write(self, cr, user, ids, values, context={}):
1227         if 'amount' in values and not values['amount']:
1228             return False
1229         if 'journal_id' in values and not values['journal_id']:
1230             return False
1231         return super(pos_payment, self).write(cr, user, ids, values, context)
1232
1233 pos_payment()
1234
1235
1236 class report_transaction_pos(osv.osv):
1237     _name = "report.transaction.pos"
1238     _description = "transaction for the pos"
1239     _auto = False
1240     _columns = {
1241         'date_create': fields.char('Date', size=16, readonly=True),
1242         'journal_id': fields.many2one('account.journal', 'Sales Journal', readonly=True),
1243         'jl_id': fields.many2one('account.journal', 'Cash Journals', readonly=True),
1244         'user_id': fields.many2one('res.users', 'User', readonly=True),
1245         'no_trans': fields.float('Number of Transaction', readonly=True),
1246         'amount': fields.float('Amount', readonly=True),
1247         'invoice_id': fields.float('Nbr Invoice', readonly=True),
1248         'invoice_am': fields.float('Invoice Amount', readonly=True),
1249         'product_nb': fields.float('Product Nb.', readonly=True),
1250         'disc': fields.float('Disc.', readonly=True),
1251     }
1252
1253     def init(self, cr):
1254         tools.drop_view_if_exists(cr, 'report_transaction_pos')
1255         cr.execute("""
1256             create or replace view report_transaction_pos as (
1257                select
1258                     min(absl.id) as id,
1259                     count(absl.id) as no_trans,
1260                     sum(absl.amount) as amount,
1261                     sum(line.price_ded) as disc,
1262                     to_char(date_trunc('day',absl.create_date),'YYYY-MM-DD')::text as date_create,
1263                     po.user_id as user_id,
1264                     po.sale_journal as journal_id,
1265                     abs.journal_id as jl_id,
1266                     count(po.invoice_id) as invoice_id,
1267                     count(p.id) as product_nb
1268                 from
1269                     account_bank_statement_line as absl,
1270                     account_bank_statement as abs,
1271                     product_product as p,
1272                     pos_order_line as line,
1273                     pos_order as po
1274                 where
1275                     absl.pos_statement_id = po.id and
1276                     line.order_id=po.id and
1277                     line.product_id=p.id and
1278                     absl.statement_id=abs.id
1279
1280                 group by
1281                     po.user_id,po.sale_journal, abs.journal_id,
1282                     to_char(date_trunc('day',absl.create_date),'YYYY-MM-DD')::text
1283                 )
1284         """)
1285                     #to_char(date_trunc('day',absl.create_date),'YYYY-MM-DD')
1286                     #to_char(date_trunc('day',absl.create_date),'YYYY-MM-DD')::text as date_create,
1287 report_transaction_pos()
1288
1289 class report_sales_by_user_pos(osv.osv):
1290     _name = "report.sales.by.user.pos"
1291     _description = "Sales by user"
1292     _auto = False
1293     _columns = {
1294         'date_order': fields.date('Order Date',required=True, select=True),
1295         'amount': fields.float('Total', readonly=True, select=True),
1296         'qty': fields.float('Quantity', readonly=True, select=True),
1297         'user_id': fields.many2one('res.users', 'User', readonly=True, select=True),
1298     }
1299
1300     def init(self, cr):
1301         tools.drop_view_if_exists(cr, 'report_sales_by_user_pos')
1302         cr.execute("""
1303             create or replace view report_sales_by_user_pos as (
1304                 select
1305                     min(po.id) as id,
1306                     to_char(date_trunc('day',po.date_order),'YYYY-MM-DD')::text as date_order,
1307                     po.user_id as user_id,
1308                     sum(pol.qty)as qty,
1309                     sum((pol.price_unit * pol.qty * (1 - (pol.discount) / 100.0))) as amount
1310                 from
1311                     pos_order as po,pos_order_line as pol,product_product as pp,product_template as pt
1312                 where
1313                     pt.id=pp.product_tmpl_id and pp.id=pol.product_id and po.id = pol.order_id
1314                group by
1315                     to_char(date_trunc('day',po.date_order),'YYYY-MM-DD')::text,
1316                     po.user_id
1317
1318                 )
1319         """)
1320 report_sales_by_user_pos()
1321
1322 class report_sales_by_user_pos_month(osv.osv):
1323     _name = "report.sales.by.user.pos.month"
1324     _description = "Sales by user monthly"
1325     _auto = False
1326     _columns = {
1327         'date_order': fields.date('Order Date',required=True, select=True),
1328         'amount': fields.float('Total', readonly=True, select=True),
1329         'qty': fields.float('Quantity', readonly=True, select=True),
1330         'user_id': fields.many2one('res.users', 'User', readonly=True, select=True),
1331     }
1332
1333     def init(self, cr):
1334         tools.drop_view_if_exists(cr, 'report_sales_by_user_pos_month')
1335         cr.execute("""
1336             create or replace view report_sales_by_user_pos_month as (
1337                 select
1338                     min(po.id) as id,
1339                     to_char(date_trunc('month',po.date_order),'YYYY-MM-DD')::text as date_order,
1340                     po.user_id as user_id,
1341                     sum(pol.qty)as qty,
1342                     sum((pol.price_unit * pol.qty * (1 - (pol.discount) / 100.0))) as amount
1343                 from
1344                     pos_order as po,pos_order_line as pol,product_product as pp,product_template as pt
1345                 where
1346                     pt.id=pp.product_tmpl_id and pp.id=pol.product_id and po.id = pol.order_id
1347                group by
1348                     to_char(date_trunc('month',po.date_order),'YYYY-MM-DD')::text,
1349                     po.user_id
1350
1351                 )
1352         """)
1353 report_sales_by_user_pos_month()
1354
1355 class report_sales_by_margin_pos(osv.osv):
1356     _name = "report.sales.by.margin.pos"
1357     _description = "Sales by margin"
1358     _auto = False
1359     _columns = {
1360 #        'pos_name': fields.char('POS Order', size=64, readonly=True),
1361         'product_name':fields.char('Product Name', size=64, readonly=True),
1362         'date_order': fields.date('Order Date',required=True, select=True),
1363      #   'amount': fields.float('Total', readonly=True, select=True),
1364         'user_id': fields.many2one('res.users', 'User', readonly=True, select=True),
1365         'qty': fields.float('Qty', readonly=True, select=True),
1366         'net_margin_per_qty':fields.float('Net margin per Qty', readonly=True, select=True),
1367         'total':fields.float('Margin', readonly=True, select=True),
1368
1369     }
1370
1371     def init(self, cr):
1372         tools.drop_view_if_exists(cr, 'report_sales_by_margin_pos')
1373         cr.execute("""
1374             create or replace view report_sales_by_margin_pos as (
1375                 select
1376                     min(pol.id) as id,
1377                     po.user_id as user_id,
1378                     pt.name as product_name,
1379                     to_char(date_trunc('day',po.date_order),'YYYY-MM-DD')::text as date_order,
1380                     sum(pol.qty) as qty,
1381                     pt.list_price-pt.standard_price as net_margin_per_qty,
1382                     (pt.list_price-pt.standard_price) *sum(pol.qty) as total
1383                 from
1384                     product_template as pt,
1385                     product_product as pp,
1386                     pos_order_line as pol,
1387                     pos_order as po
1388                 where
1389                     pol.product_id = pp.product_tmpl_id and
1390                     pp.product_tmpl_id = pt.id and
1391                     po.id = pol.order_id
1392
1393                 group by
1394                     pt.name,
1395                     pt.list_price,
1396                     pt.standard_price,
1397                     po.user_id,
1398                     to_char(date_trunc('day',po.date_order),'YYYY-MM-DD')::text
1399
1400                 )
1401         """)
1402 report_sales_by_margin_pos()
1403
1404 class report_sales_by_margin_pos_month(osv.osv):
1405     _name = "report.sales.by.margin.pos.month"
1406     _description = "Sales by margin monthly"
1407     _auto = False
1408     _columns = {
1409 #        'pos_name': fields.char('POS Order', size=64, readonly=True),
1410         'product_name':fields.char('Product Name', size=64, readonly=True),
1411         'date_order': fields.date('Order Date',required=True, select=True),
1412         #'amount': fields.float('Total', readonly=True, select=True),
1413         'user_id': fields.many2one('res.users', 'User', readonly=True, select=True),
1414         'qty': fields.float('Qty', readonly=True, select=True),
1415         'net_margin_per_qty':fields.float('Net margin per Qty', readonly=True, select=True),
1416         'total':fields.float('Margin', readonly=True, select=True),
1417
1418     }
1419
1420     def init(self, cr):
1421         tools.drop_view_if_exists(cr, 'report_sales_by_margin_pos_month')
1422         cr.execute("""
1423             create or replace view report_sales_by_margin_pos_month as (
1424                 select
1425                     min(pol.id) as id,
1426                     po.user_id as user_id,
1427                     pt.name as product_name,
1428                     to_char(date_trunc('month',po.date_order),'YYYY-MM-DD')::text as date_order,
1429                     sum(pol.qty) as qty,
1430                     pt.list_price-pt.standard_price as net_margin_per_qty,
1431                     (pt.list_price-pt.standard_price) *sum(pol.qty) as total
1432                 from
1433                     product_template as pt,
1434                     product_product as pp,
1435                     pos_order_line as pol,
1436                     pos_order as po
1437                 where
1438                     pol.product_id = pp.product_tmpl_id and
1439                     pp.product_tmpl_id = pt.id and
1440                     po.id = pol.order_id
1441
1442                 group by
1443                     pt.name,
1444                     pt.list_price,
1445                     pt.standard_price,
1446                     po.user_id,
1447                     to_char(date_trunc('month',po.date_order),'YYYY-MM-DD')::text
1448
1449                 )
1450         """)
1451 report_sales_by_margin_pos_month()
1452
1453
1454 class account_move_line(osv.osv):
1455     _inherit = 'account.move.line'
1456     def create(self, cr, user, vals, context={}):
1457         pos_obj = self.pool.get('pos.order')
1458         val_name = vals.get('name', '')
1459         val_ref = vals.get('ref', '')
1460         if (val_name and 'POS' in val_name) and (val_ref and 'PACK' in val_ref):
1461             aaa = re.search(r'Stock move.\((.*)\)', vals.get('name'))
1462             name_pos = aaa.groups()[0]
1463             pos_id = name_pos.replace('POS ','')
1464             if pos_id and pos_id.isdigit():
1465                 pos_curr = pos_obj.browse(cr,user,int(pos_id))
1466                 pos_curr = pos_curr  and pos_curr.contract_number or ''
1467                 vals['ref'] = pos_curr or vals.get('ref')
1468         return super(account_move_line, self).create(cr, user, vals, context)
1469
1470 account_move_line()
1471
1472
1473 class account_move(osv.osv):
1474     _inherit = 'account.move'
1475     def create(self, cr, user, vals, context={}):
1476         pos_obj = self.pool.get('pos.order')
1477         val_name = vals.get('name', '')
1478         val_ref = vals.get('ref', '')
1479         if (val_name and 'POS' in val_name) and (val_ref and 'PACK' in val_ref):
1480             aaa = re.search(r'Stock move.\((.*)\)', vals.get('name'))
1481             name_pos = aaa.groups()[0]
1482             pos_id = name_pos.replace('POS ','')
1483             if pos_id and pos_id.isdigit():
1484                 pos_curr = pos_obj.browse(cr,user,int(pos_id))
1485                 pos_curr = pos_curr  and pos_curr.contract_number or ''
1486                 vals['ref'] = pos_curr or vals.get('ref')
1487         return super(account_move, self).create(cr, user, vals, context)
1488
1489 account_move()
1490
1491
1492 class product_product(osv.osv):
1493     _inherit = 'product.product'
1494     _columns = {
1495         'income_pdt': fields.boolean('Product for Incoming'),
1496         'expense_pdt': fields.boolean('Product for expenses'),
1497         'am_out': fields.boolean('Controle for Outgoing Operations'),
1498         'disc_controle': fields.boolean('Discount Controle '),
1499 }
1500     _defaults = {
1501         'disc_controle': lambda *a: True,
1502 }
1503 product_product()
1504 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: