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