From 594e01f1064dafa63d2b77433aa0d782c1abef4c Mon Sep 17 00:00:00 2001 From: "Ujjvala Collins (OpenERP)" Date: Wed, 22 Jun 2011 11:25:58 +0530 Subject: [PATCH 001/409] [FIX] sale: Fixed problem of unselect/make empty packaging field in the sale order line. lp bug: https://launchpad.net/bugs/795487 fixed bzr revid: uco@tinyerp.com-20110622055558-sk5lidkpklygqws5 --- addons/sale/sale.py | 9 ++++++--- addons/sale/sale_view.xml | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/addons/sale/sale.py b/addons/sale/sale.py index 0b436f0dc91..431e0a3d63a 100644 --- a/addons/sale/sale.py +++ b/addons/sale/sale.py @@ -1013,9 +1013,11 @@ class sale_order_line(osv.osv): def product_id_change(self, cr, uid, ids, pricelist, product, qty=0, uom=False, qty_uos=0, uos=False, name='', partner_id=False, - lang=False, update_tax=True, date_order=False, packaging=False, fiscal_position=False, flag=False): + lang=False, update_tax=True, date_order=False, packaging=False, fiscal_position=False, flag=False, context=None): if not partner_id: raise osv.except_osv(_('No Customer Defined !'), _('You have to select a customer in the sales form !\nPlease set one customer before choosing a product.')) + if not context: + context = {} warning = {} warning_msgs = '' product_uom_obj = self.pool.get('product.uom') @@ -1023,7 +1025,7 @@ class sale_order_line(osv.osv): product_obj = self.pool.get('product.product') if partner_id: lang = partner_obj.browse(cr, uid, partner_id).lang - context = {'lang': lang, 'partner_id': partner_id} + context.update({'lang': lang, 'partner_id': partner_id}) if not product: return {'value': {'th_weight': 0, 'product_packaging': False, @@ -1034,7 +1036,8 @@ class sale_order_line(osv.osv): result = {} product_obj = product_obj.browse(cr, uid, product, context=context) - if not packaging and product_obj.packaging: + from_packaging = context.get('from_packaging', False) + if not packaging and product_obj.packaging and not from_packaging: packaging = product_obj.packaging[0].id result['product_packaging'] = packaging diff --git a/addons/sale/sale_view.xml b/addons/sale/sale_view.xml index 6c444ad174d..efaca80c221 100644 --- a/addons/sale/sale_view.xml +++ b/addons/sale/sale_view.xml @@ -142,7 +142,7 @@ From e8ed589a8786193e6e8eac6ccc527505cd87ee5c Mon Sep 17 00:00:00 2001 From: "Ujjvala Collins (OpenERP)" Date: Wed, 22 Jun 2011 15:14:13 +0530 Subject: [PATCH 002/409] [FIX] sale: Fixed problem of on change product when product does not have package. bzr revid: uco@tinyerp.com-20110622094413-ijesue23tagbju0r --- addons/sale/sale.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/addons/sale/sale.py b/addons/sale/sale.py index 431e0a3d63a..9e4675bfe05 100644 --- a/addons/sale/sale.py +++ b/addons/sale/sale.py @@ -1040,6 +1040,9 @@ class sale_order_line(osv.osv): if not packaging and product_obj.packaging and not from_packaging: packaging = product_obj.packaging[0].id result['product_packaging'] = packaging + elif not product_obj.packaging: + packaging = False + result['product_packaging'] = False if packaging: default_uom = product_obj.uom_id and product_obj.uom_id.id From 2f5ada053acc9bf83c77b9a369f46a4eb8889e1a Mon Sep 17 00:00:00 2001 From: "Ujjvala Collins (OpenERP)" Date: Fri, 24 Jun 2011 15:09:06 +0530 Subject: [PATCH 003/409] [FIX] sale, sale_layout: Added onchange method on product packaging field. bzr revid: uco@tinyerp.com-20110624093906-2oonk5dge6f2ond7 --- addons/sale/sale.py | 76 +++++++++++++++---------- addons/sale/sale_view.xml | 2 +- addons/sale_layout/sale_layout_view.xml | 2 +- 3 files changed, 48 insertions(+), 32 deletions(-) diff --git a/addons/sale/sale.py b/addons/sale/sale.py index 9e4675bfe05..e5afac4d87e 100644 --- a/addons/sale/sale.py +++ b/addons/sale/sale.py @@ -1010,43 +1010,26 @@ class sale_order_line(osv.osv): default = {} default.update({'state': 'draft', 'move_ids': [], 'invoiced': False, 'invoice_lines': []}) return super(sale_order_line, self).copy_data(cr, uid, id, default, context=context) - - def product_id_change(self, cr, uid, ids, pricelist, product, qty=0, - uom=False, qty_uos=0, uos=False, name='', partner_id=False, - lang=False, update_tax=True, date_order=False, packaging=False, fiscal_position=False, flag=False, context=None): - if not partner_id: - raise osv.except_osv(_('No Customer Defined !'), _('You have to select a customer in the sales form !\nPlease set one customer before choosing a product.')) - if not context: - context = {} - warning = {} - warning_msgs = '' - product_uom_obj = self.pool.get('product.uom') - partner_obj = self.pool.get('res.partner') + + def onchange_product_packaging(self, cr, uid, ids, pricelist, product, qty, uom, packaging, flag): product_obj = self.pool.get('product.product') - if partner_id: - lang = partner_obj.browse(cr, uid, partner_id).lang - context.update({'lang': lang, 'partner_id': partner_id}) - - if not product: - return {'value': {'th_weight': 0, 'product_packaging': False, - 'product_uos_qty': qty}, 'domain': {'product_uom': [], - 'product_uos': []}} - if not date_order: - date_order = time.strftime('%Y-%m-%d') - + product_uom_obj = self.pool.get('product.uom') + warning = {} result = {} - product_obj = product_obj.browse(cr, uid, product, context=context) - from_packaging = context.get('from_packaging', False) - if not packaging and product_obj.packaging and not from_packaging: - packaging = product_obj.packaging[0].id + warning_msgs = '' + if not product: + return {'value': {'product_packaging': False}} + products = product_obj.browse(cr, uid, product) + if not packaging and products.packaging and not flag: + packaging = products.packaging[0].id result['product_packaging'] = packaging - elif not product_obj.packaging: + elif not products.packaging: packaging = False result['product_packaging'] = False if packaging: - default_uom = product_obj.uom_id and product_obj.uom_id.id - pack = self.pool.get('product.packaging').browse(cr, uid, packaging, context=context) + default_uom = products.uom_id and products.uom_id.id + pack = self.pool.get('product.packaging').browse(cr, uid, packaging) q = product_uom_obj._compute_qty(cr, uid, uom, pack.qty, default_uom) # qty = qty - qty % q + q if qty and (q and not (qty % q) == 0): @@ -1061,6 +1044,39 @@ class sale_order_line(osv.osv): warning_msgs += _("Picking Information ! : ") + warn_msg + "\n\n" result['product_uom_qty'] = qty + if warning_msgs: + warning = { + 'title': _('Configuration Error !'), + 'message' : warning_msgs + } + + return {'value': result, 'warning': warning} + + def product_id_change(self, cr, uid, ids, pricelist, product, qty=0, + uom=False, qty_uos=0, uos=False, name='', partner_id=False, + lang=False, update_tax=True, date_order=False, packaging=False, fiscal_position=False, flag=False): + if not partner_id: + raise osv.except_osv(_('No Customer Defined !'), _('You have to select a customer in the sales form !\nPlease set one customer before choosing a product.')) + warning_msgs = '' + product_uom_obj = self.pool.get('product.uom') + partner_obj = self.pool.get('res.partner') + product_obj = self.pool.get('product.product') + if partner_id: + lang = partner_obj.browse(cr, uid, partner_id).lang + context = {'lang': lang, 'partner_id': partner_id} + + if not product: + return {'value': {'th_weight': 0, 'product_packaging': False, + 'product_uos_qty': qty}, 'domain': {'product_uom': [], + 'product_uos': []}} + if not date_order: + date_order = time.strftime('%Y-%m-%d') + + res = self.onchange_product_packaging(cr, uid, ids, pricelist, product, qty, uom, packaging, False) + result = res.get('value', {}) + warning = res.get('warning', {}) + product_obj = product_obj.browse(cr, uid, product, context=context) + uom2 = False if uom: uom2 = product_uom_obj.browse(cr, uid, uom) diff --git a/addons/sale/sale_view.xml b/addons/sale/sale_view.xml index efaca80c221..0b17b5ca71c 100644 --- a/addons/sale/sale_view.xml +++ b/addons/sale/sale_view.xml @@ -142,7 +142,7 @@ diff --git a/addons/sale_layout/sale_layout_view.xml b/addons/sale_layout/sale_layout_view.xml index 6dee4ddd62f..25c56a03ae6 100644 --- a/addons/sale_layout/sale_layout_view.xml +++ b/addons/sale_layout/sale_layout_view.xml @@ -38,7 +38,7 @@ name="product_packaging" attrs="{'readonly':[('layout_type','!=','article')]}" context="{'partner_id':parent.partner_id,'quantity':product_uom_qty,'pricelist':parent.pricelist_id,'shop':parent.shop_id,'uom':product_uom}" - on_change="product_id_change(parent.pricelist_id,product_id,product_uom_qty,product_uom,product_uos_qty,product_uos,name,parent.partner_id, 'lang' in context and context['lang'], False, parent.date_order, product_packaging, parent.fiscal_position)" + on_change="onchange_product_packaging(parent.pricelist_id, product_id, product_uom_qty, product_uom, product_packaging, True)" domain="[('product_id','=',product_id)]" groups="base.group_extended"/> From 4aa687cf3fad299a4cc6098f7fc12f35c36d5338 Mon Sep 17 00:00:00 2001 From: "ron@tinyerp.com" <> Date: Wed, 29 Jun 2011 10:48:00 +0530 Subject: [PATCH 004/409] [FIX]purchase,procurement:Fixed=>Generate Sale order via Non admin User Po generated with wrong warehouse and company ,For information Traceback Generated after run the ORDERPOINT it is a sequence problem and it has been fixed in lp:787052 : lp bug: https://launchpad.net/bugs/802322 fixed bzr revid: ron@tinyerp.com-20110629051800-u71iztvvtbgsyzuq --- addons/procurement/schedulers.py | 1 + addons/purchase/purchase.py | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/addons/procurement/schedulers.py b/addons/procurement/schedulers.py index 3a6e3336472..d2787185c18 100644 --- a/addons/procurement/schedulers.py +++ b/addons/procurement/schedulers.py @@ -183,6 +183,7 @@ class procurement_order(osv.osv): 'product_qty': -product.virtual_available, 'product_uom': product.uom_id.id, 'location_id': location_id, + 'company_id': warehouse.company_id.id, 'procure_method': 'make_to_order', }) wf_service.trg_validate(uid, 'procurement.order', proc_id, 'button_confirm', cr) diff --git a/addons/purchase/purchase.py b/addons/purchase/purchase.py index 6f6ab0c1b44..8867cb7f3a7 100644 --- a/addons/purchase/purchase.py +++ b/addons/purchase/purchase.py @@ -775,6 +775,8 @@ class procurement_order(osv.osv): prod_obj = self.pool.get('product.product') acc_pos_obj = self.pool.get('account.fiscal.position') po_obj = self.pool.get('purchase.order') + wharehouse_obj = self.pool.get('stock.warehouse') + for procurement in self.browse(cr, uid, ids, context=context): res_id = procurement.move_id.id partner = procurement.product_id.seller_id # Taken Main Supplier of Product of Procurement. @@ -821,6 +823,8 @@ class procurement_order(osv.osv): 'partner_id': partner_id, 'partner_address_id': address_id, 'location_id': procurement.location_id.id, + 'warehouse_id': wharehouse_obj.search(cr, uid, [('company_id','=',procurement.company_id.id or company)])[0], + 'company_id': procurement.company_id.id or company, 'pricelist_id': pricelist_id, 'order_line': [(0,0,line)], 'company_id': procurement.company_id.id, From 6c27f121fb6621d0964a163ae6a62b1b363a9c8f Mon Sep 17 00:00:00 2001 From: "ron@tinyerp.com" <> Date: Wed, 29 Jun 2011 18:55:39 +0530 Subject: [PATCH 005/409] [IMP]purchase:fixed=>Traceback Generated after run the ORDERPOINT bzr revid: ron@tinyerp.com-20110629132539-9y8zxts1v39olmrt --- addons/purchase/purchase.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/purchase/purchase.py b/addons/purchase/purchase.py index 8867cb7f3a7..e06a5ef988c 100644 --- a/addons/purchase/purchase.py +++ b/addons/purchase/purchase.py @@ -776,7 +776,6 @@ class procurement_order(osv.osv): acc_pos_obj = self.pool.get('account.fiscal.position') po_obj = self.pool.get('purchase.order') wharehouse_obj = self.pool.get('stock.warehouse') - for procurement in self.browse(cr, uid, ids, context=context): res_id = procurement.move_id.id partner = procurement.product_id.seller_id # Taken Main Supplier of Product of Procurement. @@ -819,6 +818,7 @@ class procurement_order(osv.osv): 'taxes_id': [(6,0,taxes)] }) purchase_id = po_obj.create(cr, uid, { + 'name': self.pool.get('ir.sequence').get(cr, uid, 'purchase.order') or _('PO:%s') %procurement.name, 'origin': procurement.origin, 'partner_id': partner_id, 'partner_address_id': address_id, From f059576ba8c7bc2ca8e8f15b96b1438b502f67b7 Mon Sep 17 00:00:00 2001 From: "ron@tinyerp.com" <> Date: Tue, 5 Jul 2011 11:47:50 +0530 Subject: [PATCH 006/409] [IMP]procurment:Fixed=>changed the all select queries which bypassed the record rules in procurment.py and schedular.py,and change the output location configuaration with company_id=False because of that it will be access by child company bzr revid: ron@tinyerp.com-20110705061750-7g73nancsfijk0re --- addons/procurement/procurement.py | 4 ++-- addons/procurement/schedulers.py | 12 +++--------- addons/purchase/purchase.py | 1 - addons/stock/stock_data.xml | 2 +- 4 files changed, 6 insertions(+), 13 deletions(-) diff --git a/addons/procurement/procurement.py b/addons/procurement/procurement.py index 743ef85a9ea..f4988afedc0 100644 --- a/addons/procurement/procurement.py +++ b/addons/procurement/procurement.py @@ -371,8 +371,8 @@ class procurement_order(osv.osv): id = procurement.move_id.id if not (procurement.move_id.state in ('done','assigned','cancel')): ok = ok and self.pool.get('stock.move').action_assign(cr, uid, [id]) - cr.execute('select count(id) from stock_warehouse_orderpoint where product_id=%s', (procurement.product_id.id,)) - res = cr.fetchone()[0] + order_point_id = self.pool.get('stock.warehouse.orderpoint').search(cr, uid, [('product_id', '=', procurement.product_id.id)]) + res = len(order_point_id) if not res and not ok: message = _("Not enough stock and no minimum orderpoint rule defined.") elif not res: diff --git a/addons/procurement/schedulers.py b/addons/procurement/schedulers.py index d2787185c18..33abeab34af 100644 --- a/addons/procurement/schedulers.py +++ b/addons/procurement/schedulers.py @@ -67,8 +67,7 @@ class procurement_order(osv.osv): report_except = 0 report_later = 0 while True: - cr.execute("select id from procurement_order where state='confirmed' and procure_method='make_to_order' order by priority,date_planned limit 500 offset %s", (offset,)) - ids = map(lambda x: x[0], cr.fetchall()) + ids = procurement_obj.search(cr, uid, [('state', '=', 'confirmed'), ('procure_method', '=', 'make_to_order')], order='priority,date_planned') for proc in procurement_obj.browse(cr, uid, ids, context=context): if maxdate >= proc.date_planned: wf_service.trg_validate(uid, 'procurement.order', proc.id, 'button_check', cr) @@ -156,11 +155,7 @@ class procurement_order(osv.osv): wf_service = netsvc.LocalService("workflow") warehouse_ids = warehouse_obj.search(cr, uid, [], context=context) - - cr.execute('select p.id from product_product p \ - join product_template t on (p.product_tmpl_id=t.id) \ - where p.active=True and t.purchase_ok=True') - products_id = [x for x, in cr.fetchall()] + products_id = product_obj.search(cr, uid, [('active', '=', True), ('purchase_ok', '=', True)], order='id') for warehouse in warehouse_obj.browse(cr, uid, warehouse_ids, context=context): context['warehouse'] = warehouse @@ -242,8 +237,7 @@ class procurement_order(osv.osv): if op.procurement_draft_ids: # Check draft procurement related to this order point pro_ids = [x.id for x in op.procurement_draft_ids] - cr.execute('select id, product_qty from procurement_order where id in %s order by product_qty desc', (tuple(pro_ids), )) - procure_datas = cr.dictfetchall() + procure_datas = procurement_obj.read(cr, uid, pro_ids, ['id','product_qty'], context=context) to_generate = qty for proc_data in procure_datas: if to_generate >= proc_data['product_qty']: diff --git a/addons/purchase/purchase.py b/addons/purchase/purchase.py index e06a5ef988c..137e8125c06 100644 --- a/addons/purchase/purchase.py +++ b/addons/purchase/purchase.py @@ -824,7 +824,6 @@ class procurement_order(osv.osv): 'partner_address_id': address_id, 'location_id': procurement.location_id.id, 'warehouse_id': wharehouse_obj.search(cr, uid, [('company_id','=',procurement.company_id.id or company)])[0], - 'company_id': procurement.company_id.id or company, 'pricelist_id': pricelist_id, 'order_line': [(0,0,line)], 'company_id': procurement.company_id.id, diff --git a/addons/stock/stock_data.xml b/addons/stock/stock_data.xml index bcde818c78a..3c75615dfae 100644 --- a/addons/stock/stock_data.xml +++ b/addons/stock/stock_data.xml @@ -91,8 +91,8 @@ Output - internal + customer transparent out From 14daa261888a6ee92db0bbd24fefee9a6a15270c Mon Sep 17 00:00:00 2001 From: "Ujjvala Collins (OpenERP)" Date: Tue, 5 Jul 2011 18:41:02 +0530 Subject: [PATCH 007/409] [REF] sale: Refactored the code, optimization and fixed according to coding standards. bzr revid: uco@tinyerp.com-20110705131102-0r9etxoro001iem4 --- addons/sale/sale.py | 55 ++++++++++++++----------- addons/sale/sale_view.xml | 2 +- addons/sale_layout/sale_layout_view.xml | 2 +- 3 files changed, 33 insertions(+), 26 deletions(-) diff --git a/addons/sale/sale.py b/addons/sale/sale.py index 398ad00b467..85e507bddd3 100644 --- a/addons/sale/sale.py +++ b/addons/sale/sale.py @@ -1011,53 +1011,60 @@ class sale_order_line(osv.osv): default.update({'state': 'draft', 'move_ids': [], 'invoiced': False, 'invoice_lines': []}) return super(sale_order_line, self).copy_data(cr, uid, id, default, context=context) - def onchange_product_packaging(self, cr, uid, ids, pricelist, product, qty, uom, packaging, flag): + def product_packaging_change(self, cr, uid, ids, pricelist, product, qty=0, uom=False, + partner_id=False, packaging=False, flag=False, context=None): product_obj = self.pool.get('product.product') product_uom_obj = self.pool.get('product.uom') + pack_obj = self.pool.get('product.packaging') warning = {} result = {} warning_msgs = '' if not product: return {'value': {'product_packaging': False}} - products = product_obj.browse(cr, uid, product) - if not packaging and products.packaging and not flag: + + if flag: + res = self.product_id_change(cr, uid, ids, pricelist=pricelist, + product=product, qty=qty, uom=uom, partner_id=partner_id, + packaging=packaging, flag=False) + warning_msgs = res.get('warning') and res['warning']['message'] + + products = product_obj.browse(cr, uid, product, context=context) + if not products.packaging: + packaging = result['product_packaging'] = False + elif not packaging and products.packaging and not flag: packaging = products.packaging[0].id result['product_packaging'] = packaging - elif not products.packaging: - packaging = False - result['product_packaging'] = False - + if packaging: default_uom = products.uom_id and products.uom_id.id - pack = self.pool.get('product.packaging').browse(cr, uid, packaging) + pack = pack_obj.browse(cr, uid, packaging, context=context) q = product_uom_obj._compute_qty(cr, uid, uom, pack.qty, default_uom) # qty = qty - qty % q + q if qty and (q and not (qty % q) == 0): ean = pack.ean or _('(n/a)') qty_pack = pack.qty type_ul = pack.ul - warn_msg = _("You selected a quantity of %d Units.\n" - "But it's not compatible with the selected packaging.\n" - "Here is a proposition of quantities according to the packaging:\n" - "EAN: %s Quantity: %s Type of ul: %s") % \ - (qty, ean, qty_pack, type_ul.name) - warning_msgs += _("Picking Information ! : ") + warn_msg + "\n\n" - result['product_uom_qty'] = qty - - if warning_msgs: - warning = { + if not warning_msgs: + warn_msg = _("You selected a quantity of %d Units.\n" + "But it's not compatible with the selected packaging.\n" + "Here is a proposition of quantities according to the packaging:\n" + "EAN: %s Quantity: %s Type of ul: %s") % \ + (qty, ean, qty_pack, type_ul.name) + warning_msgs += _("Picking Information ! : ") + warn_msg + "\n\n" + warning = { 'title': _('Configuration Error !'), - 'message' : warning_msgs - } + 'message': warning_msgs + } + result['product_uom_qty'] = qty return {'value': result, 'warning': warning} def product_id_change(self, cr, uid, ids, pricelist, product, qty=0, uom=False, qty_uos=0, uos=False, name='', partner_id=False, lang=False, update_tax=True, date_order=False, packaging=False, fiscal_position=False, flag=False): - if not partner_id: + if not partner_id: raise osv.except_osv(_('No Customer Defined !'), _('You have to select a customer in the sales form !\nPlease set one customer before choosing a product.')) - warning_msgs = '' + warning = {} product_uom_obj = self.pool.get('product.uom') partner_obj = self.pool.get('res.partner') product_obj = self.pool.get('product.product') @@ -1072,9 +1079,9 @@ class sale_order_line(osv.osv): if not date_order: date_order = time.strftime('%Y-%m-%d') - res = self.onchange_product_packaging(cr, uid, ids, pricelist, product, qty, uom, packaging, False) + res = self.product_packaging_change(cr, uid, ids, pricelist, product, qty, uom, partner_id, packaging) result = res.get('value', {}) - warning = res.get('warning', {}) + warning_msgs = res.get('warning') and res['warning']['message'] or '' product_obj = product_obj.browse(cr, uid, product, context=context) uom2 = False diff --git a/addons/sale/sale_view.xml b/addons/sale/sale_view.xml index 0b17b5ca71c..34e7dc292bc 100644 --- a/addons/sale/sale_view.xml +++ b/addons/sale/sale_view.xml @@ -142,7 +142,7 @@ diff --git a/addons/sale_layout/sale_layout_view.xml b/addons/sale_layout/sale_layout_view.xml index 25c56a03ae6..12d07432861 100644 --- a/addons/sale_layout/sale_layout_view.xml +++ b/addons/sale_layout/sale_layout_view.xml @@ -38,7 +38,7 @@ name="product_packaging" attrs="{'readonly':[('layout_type','!=','article')]}" context="{'partner_id':parent.partner_id,'quantity':product_uom_qty,'pricelist':parent.pricelist_id,'shop':parent.shop_id,'uom':product_uom}" - on_change="onchange_product_packaging(parent.pricelist_id, product_id, product_uom_qty, product_uom, product_packaging, True)" + on_change="product_packaging_change(parent.pricelist_id, product_id, product_uom_qty, product_uom, parent.partner_id, product_packaging, True)" domain="[('product_id','=',product_id)]" groups="base.group_extended"/> From 867aa1a56f113da5748a0b44d0dff0a94cefc66b Mon Sep 17 00:00:00 2001 From: Mustufa Rangwala Date: Wed, 6 Jul 2011 14:59:17 +0530 Subject: [PATCH 008/409] [IMP] sale: onchange package minor improvement bzr revid: mra@mra-laptop-20110706092917-44yznbfqyeklrzdn --- addons/sale/sale.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/addons/sale/sale.py b/addons/sale/sale.py index 85e507bddd3..057ed530218 100644 --- a/addons/sale/sale.py +++ b/addons/sale/sale.py @@ -1010,31 +1010,30 @@ class sale_order_line(osv.osv): default = {} default.update({'state': 'draft', 'move_ids': [], 'invoiced': False, 'invoice_lines': []}) return super(sale_order_line, self).copy_data(cr, uid, id, default, context=context) - - def product_packaging_change(self, cr, uid, ids, pricelist, product, qty=0, uom=False, + + def product_packaging_change(self, cr, uid, ids, pricelist, product, qty=0, uom=False, partner_id=False, packaging=False, flag=False, context=None): + if not product: + return {'value': {'product_packaging': False}} product_obj = self.pool.get('product.product') product_uom_obj = self.pool.get('product.uom') pack_obj = self.pool.get('product.packaging') warning = {} result = {} warning_msgs = '' - if not product: - return {'value': {'product_packaging': False}} - if flag: - res = self.product_id_change(cr, uid, ids, pricelist=pricelist, + res = self.product_id_change(cr, uid, ids, pricelist=pricelist, product=product, qty=qty, uom=uom, partner_id=partner_id, packaging=packaging, flag=False) warning_msgs = res.get('warning') and res['warning']['message'] - + products = product_obj.browse(cr, uid, product, context=context) if not products.packaging: packaging = result['product_packaging'] = False elif not packaging and products.packaging and not flag: packaging = products.packaging[0].id result['product_packaging'] = packaging - + if packaging: default_uom = products.uom_id and products.uom_id.id pack = pack_obj.browse(cr, uid, packaging, context=context) From b1dc5544d244fbfea6f535e49b30b0c717012665 Mon Sep 17 00:00:00 2001 From: Mustufa Rangwala Date: Wed, 6 Jul 2011 15:05:40 +0530 Subject: [PATCH 009/409] [IMP] sale: onchange package minor improvement bzr revid: mra@mra-laptop-20110706093540-ncsl94dqhd1n4c3m --- addons/sale/sale.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/addons/sale/sale.py b/addons/sale/sale.py index 057ed530218..555b83d4616 100644 --- a/addons/sale/sale.py +++ b/addons/sale/sale.py @@ -105,11 +105,11 @@ class sale_order(osv.osv): procurement_order mp on (mp.move_id=m.id) WHERE p.sale_id IN %s GROUP BY m.state, mp.state, p.sale_id, p.type''', (tuple(ids),)) - + for item in cr.dictfetchall(): if item['move_state'] == 'cancel': continue - + if item['picking_type'] == 'in':#this is a returned picking tmp[item['sale_order_id']]['total'] -= item['nbr'] or 0.0 # Deducting the return picking qty if item['procurement_state'] == 'done' or item['move_state'] == 'done': @@ -118,7 +118,7 @@ class sale_order(osv.osv): tmp[item['sale_order_id']]['total'] += item['nbr'] or 0.0 if item['procurement_state'] == 'done' or item['move_state'] == 'done': tmp[item['sale_order_id']]['picked'] += item['nbr'] or 0.0 - + for order in self.browse(cr, uid, ids, context=context): if order.shipped: res[order.id] = 100.0 @@ -710,7 +710,7 @@ class sale_order(osv.osv): 'note': line.notes, 'company_id': order.company_id.id, }) - + if line.product_id: proc_id = self.pool.get('procurement.order').create(cr, uid, { 'name': line.name, @@ -1078,7 +1078,7 @@ class sale_order_line(osv.osv): if not date_order: date_order = time.strftime('%Y-%m-%d') - res = self.product_packaging_change(cr, uid, ids, pricelist, product, qty, uom, partner_id, packaging) + res = self.product_packaging_change(cr, uid, ids, pricelist, product, qty, uom, partner_id, packaging, context=context) result = res.get('value', {}) warning_msgs = res.get('warning') and res['warning']['message'] or '' product_obj = product_obj.browse(cr, uid, product, context=context) @@ -1147,7 +1147,7 @@ class sale_order_line(osv.osv): max(0,product_obj.qty_available), product_obj.uom_id.name) warning_msgs += _("Not enough stock ! : ") + warn_msg + "\n\n" # get unit price - + if not pricelist: warn_msg = _('You have to select a pricelist or a customer in the sales form !\n' 'Please set one before choosing a product.') @@ -1168,7 +1168,7 @@ class sale_order_line(osv.osv): if warning_msgs: warning = { 'title': _('Configuration Error !'), - 'message' : warning_msgs + 'message' : warning_msgs } return {'value': result, 'domain': domain, 'warning': warning} @@ -1211,7 +1211,7 @@ class sale_config_picking_policy(osv.osv_memory): ('picking', 'Invoice Based on Deliveries'), ], 'Shipping Default Policy', required=True, help="""The Shipping Policy is used to synchronise invoice and delivery operations. - - The "Invoice Based on Sales Orders" option will create the picking order directly and wait for the user to manually click on the 'Invoice' button to generate the draft invoice. + - The "Invoice Based on Sales Orders" option will create the picking order directly and wait for the user to manually click on the 'Invoice' button to generate the draft invoice. - The "Invoice Based on Deliveries" option is used to create an invoice during the picking process."""), 'step': fields.selection([ ('one', 'Delivery Order Only'), From 7044314fbc63e81d1716e2b428f6292b78052069 Mon Sep 17 00:00:00 2001 From: "Ujjvala Collins (OpenERP)" Date: Thu, 7 Jul 2011 14:16:02 +0530 Subject: [PATCH 010/409] [IMP] account: Improved reports to print translated terms correctly for filters. bzr revid: uco@tinyerp.com-20110707084602-vur1w0pnadrfgktt --- addons/account/report/account_balance.rml | 8 ++++---- addons/account/report/account_balance_sheet.rml | 8 ++++---- .../report/account_balance_sheet_horizontal.rml | 8 ++++---- addons/account/report/account_central_journal.rml | 8 ++++---- addons/account/report/account_general_journal.rml | 8 ++++---- addons/account/report/account_general_ledger.rml | 12 ++++++------ .../report/account_general_ledger_landscape.rml | 12 ++++++------ addons/account/report/account_journal.rml | 6 +++--- addons/account/report/account_partner_balance.rml | 8 ++++---- addons/account/report/account_partner_ledger.rml | 8 ++++---- .../account/report/account_partner_ledger_other.rml | 8 ++++---- addons/account/report/account_profit_horizontal.rml | 8 ++++---- addons/account/report/account_profit_loss.rml | 8 ++++---- 13 files changed, 55 insertions(+), 55 deletions(-) diff --git a/addons/account/report/account_balance.rml b/addons/account/report/account_balance.rml index 3d971862bdc..62eecf548b7 100644 --- a/addons/account/report/account_balance.rml +++ b/addons/account/report/account_balance.rml @@ -220,7 +220,7 @@ Display Account - Filter By [[ get_filter(data)!='No Filter' and get_filter(data) ]] + Filter By [[ data['form']['filter']!='filter_no' and get_filter(data) ]] Target Moves @@ -234,8 +234,8 @@ [[ get_fiscalyear(data) or '' ]] [[ (data['form']['display_account']=='all' and 'All') or (data['form']['display_account']=='movement' and 'With movements') or 'With balance is not equal to 0']] - [[ get_filter(data)=='No Filter' and get_filter(data) or removeParentNode('para') ]] - [[ get_filter(data)=='Date' or removeParentNode('blockTable') ]] + [[ data['form']['filter']=='filter_no' and get_filter(data) or removeParentNode('para') ]] + [[ data['form']['filter']=='filter_date' or removeParentNode('blockTable') ]] Start Date @@ -253,7 +253,7 @@ - [[ get_filter(data)=='Periods' or removeParentNode('blockTable') ]] + [[ data['form']['filter']=='filter_period' or removeParentNode('blockTable') ]] Start Period diff --git a/addons/account/report/account_balance_sheet.rml b/addons/account/report/account_balance_sheet.rml index 205bfff497b..d6acc0348f0 100644 --- a/addons/account/report/account_balance_sheet.rml +++ b/addons/account/report/account_balance_sheet.rml @@ -174,15 +174,15 @@ Chart of Account Fiscal Year - Filter By [[ get_filter(data)!='No Filter' and '' or removeParentNode('font') ]] + Filter By [[ data['form']['filter']!='filter_no' and '' or removeParentNode('font') ]] Display Account Target Moves [[ get_account(data) or removeParentNode('para') ]] [[ get_fiscalyear(data) or '' ]] - [[ get_filter(data)=='No Filter' and get_filter(data) or removeParentNode('para') ]] - [[ get_filter(data)=='Date' or removeParentNode('blockTable') ]] + [[ data['form']['filter']=='filter_no' and get_filter(data) or removeParentNode('para') ]] + [[ data['form']['filter']=='filter_date' or removeParentNode('blockTable') ]] Start Date End Date @@ -192,7 +192,7 @@ [[ formatLang(get_end_date(data),date=True) ]] - [[ get_filter(data)=='Periods' or removeParentNode('blockTable') ]] + [[ data['form']['filter']=='filter_period' or removeParentNode('blockTable') ]] Start Period End Period diff --git a/addons/account/report/account_balance_sheet_horizontal.rml b/addons/account/report/account_balance_sheet_horizontal.rml index 7e4f9f844a5..d34a0f053d8 100644 --- a/addons/account/report/account_balance_sheet_horizontal.rml +++ b/addons/account/report/account_balance_sheet_horizontal.rml @@ -134,15 +134,15 @@ Chart of Account Fiscal Year - Filter By [[ get_filter(data)!='No Filter' and get_filter(data) ]] + Filter By [[ data['form']['filter']!='filter_no' and get_filter(data) ]] Display Account Target Moves [[ get_account(data) or removeParentNode('para') ]] [[ get_fiscalyear(data) or '' ]] - [[ get_filter(data)=='No Filter' and get_filter(data) or removeParentNode('para') ]] - [[ get_filter(data)=='Date' or removeParentNode('blockTable') ]] + [[ data['form']['filter']=='filter_no' and get_filter(data) or removeParentNode('para') ]] + [[ data['form']['filter']=='filter_date' or removeParentNode('blockTable') ]] Start Date End Date @@ -152,7 +152,7 @@ [[ formatLang(get_end_date(data),date=True) ]] - [[ get_filter(data)=='Periods' or removeParentNode('blockTable') ]] + [[ data['form']['filter']=='filter_period' or removeParentNode('blockTable') ]] Start Period End Period diff --git a/addons/account/report/account_central_journal.rml b/addons/account/report/account_central_journal.rml index 33566083652..956e2e2b6a5 100644 --- a/addons/account/report/account_central_journal.rml +++ b/addons/account/report/account_central_journal.rml @@ -239,15 +239,15 @@ [[ data['model']=='ir.ui.menu' and 'Chart of Account' or removeParentNode('para') ]] Fiscal Year Journal - Filter By [[get_filter(data)!='No Filter' and get_filter(data) ]] + Filter By [[ data['form']['filter']!='filter_no' and get_filter(data) ]] Target Moves [[ get_account(data) or removeParentNode('para') ]] [[ get_fiscalyear(data) or '' ]] [[o.journal_id.name ]] - [[ get_filter(data)=='No Filter' and get_filter(data) or removeParentNode('para') ]] - [[ get_filter(data)=='Date' or removeParentNode('blockTable') ]] + [[ data['form']['filter']=='filter_no' and get_filter(data) or removeParentNode('para') ]] + [[ data['form']['filter']=='filter_date' or removeParentNode('blockTable') ]] Start Date End Date @@ -257,7 +257,7 @@ [[ formatLang(get_end_date(data),date=True) ]] - [[ get_filter(data)=='Periods' or removeParentNode('blockTable') ]] + [[ data['form']['filter']=='filter_period' or removeParentNode('blockTable') ]] Start Period End Period diff --git a/addons/account/report/account_general_journal.rml b/addons/account/report/account_general_journal.rml index f4cc13069c8..5123d7ee19b 100644 --- a/addons/account/report/account_general_journal.rml +++ b/addons/account/report/account_general_journal.rml @@ -224,15 +224,15 @@ [[ data['model']=='ir.ui.menu' and 'Chart of Account' or removeParentNode('para') ]] Fiscal Year Journals - Filter By [[ get_filter(data)!='No Filter' and get_filter(data) ]] + Filter By [[ data['form']['filter']!='filter_no' and get_filter(data) ]] Target Moves [[ get_account(data) or removeParentNode('para') ]] [[ get_fiscalyear(data) or '' ]] [[', '.join([ lt or '' for lt in get_journal(data) ]) ]] - [[ get_filter(data)=='No Filter' and get_filter(data) or removeParentNode('para') ]] - [[ get_filter(data)=='Date' or removeParentNode('blockTable') ]] + [[ data['form']['filter']=='filter_no' and get_filter(data) or removeParentNode('para') ]] + [[ data['form']['filter']=='filter_date' or removeParentNode('blockTable') ]] Start Date End Date @@ -242,7 +242,7 @@ [[ formatLang(get_end_date(data),date=True) ]] - [[ get_filter(data)=='Periods' or removeParentNode('blockTable') ]] + [[ data['form']['filter']=='filter_period' or removeParentNode('blockTable') ]] Start Period End Period diff --git a/addons/account/report/account_general_ledger.rml b/addons/account/report/account_general_ledger.rml index 15614444822..14c4ce59371 100644 --- a/addons/account/report/account_general_ledger.rml +++ b/addons/account/report/account_general_ledger.rml @@ -353,7 +353,7 @@ Journal - Filters By [[ get_filter(data)!='No Filter' and get_filter(data) ]] + Filters By [[ data['form']['filter']!='filter_no' and get_filter(data) ]] Target Moves @@ -372,8 +372,8 @@ [[', '.join([ lt or '' for lt in get_journal(data) ]) ]] - [[ get_filter(data)=='No Filter' and get_filter(data) or removeParentNode('para') ]] - [[ get_filter(data)=='Date' or removeParentNode('blockTable') ]] + [[ data['form']['filter']=='filter_no' and get_filter(data) or removeParentNode('para') ]] + [[ data['form']['filter']=='filter_date' or removeParentNode('blockTable') ]] Start Date @@ -383,7 +383,7 @@ - [[ get_filter(data)=='Date' or removeParentNode('blockTable') ]] + [[ data['form']['filter']=='filter_date' or removeParentNode('blockTable') ]] [[ formatLang(get_start_date(data),date=True) ]] @@ -393,7 +393,7 @@ - [[ get_filter(data)=='Periods' or removeParentNode('blockTable') ]] + [[ data['form']['filter']=='filter_period' or removeParentNode('blockTable') ]] Start Period @@ -403,7 +403,7 @@ - [[ get_filter(data)=='Periods' or removeParentNode('blockTable') ]] + [[ data['form']['filter']=='filter_period' or removeParentNode('blockTable') ]] [[ get_start_period(data) or removeParentNode('para') ]] diff --git a/addons/account/report/account_general_ledger_landscape.rml b/addons/account/report/account_general_ledger_landscape.rml index ac100c3ecd0..fc2e482a3b1 100644 --- a/addons/account/report/account_general_ledger_landscape.rml +++ b/addons/account/report/account_general_ledger_landscape.rml @@ -373,7 +373,7 @@ Display Account - Filter By [[ get_filter(data)!='No Filter' and get_filter(data) ]] + Filter By [[ data['form']['filter']!='filter_no' and get_filter(data) ]] Entries Sorted By @@ -398,8 +398,8 @@ [[ (data['form']['display_account']=='all' and 'All') or (data['form']['display_account']=='movement' and 'With movements') or 'With balance is not equal to 0']] - [[ get_filter(data)=='No Filter' and get_filter(data) or removeParentNode('para') ]] - [[ get_filter(data)=='Date' or removeParentNode('blockTable') ]] + [[ data['form']['filter']=='filter_no' and get_filter(data) or removeParentNode('para') ]] + [[ data['form']['filter']=='filter_date' or removeParentNode('blockTable') ]] Start Date @@ -409,7 +409,7 @@ - [[ get_filter(data)=='Date' or removeParentNode('blockTable') ]] + [[ data['form']['filter']=='filter_date' or removeParentNode('blockTable') ]] [[ formatLang(get_start_date(data),date=True) ]] @@ -419,7 +419,7 @@ - [[ get_filter(data)=='Periods' or removeParentNode('blockTable') ]] + [[ data['form']['filter']=='filter_period' or removeParentNode('blockTable') ]] Start Period @@ -429,7 +429,7 @@ - [[ get_filter(data)=='Periods' or removeParentNode('blockTable') ]] + [[ data['form']['filter']=='filter_period' or removeParentNode('blockTable') ]] [[ get_start_period(data) or removeParentNode('para') ]] diff --git a/addons/account/report/account_journal.rml b/addons/account/report/account_journal.rml index 9b0ac55bb56..c5c22800cbd 100644 --- a/addons/account/report/account_journal.rml +++ b/addons/account/report/account_journal.rml @@ -197,8 +197,8 @@ [[ get_account(data) or '' ]] [[ get_fiscalyear(data) or '' ]] [[o.journal_id.name ]] - [[ get_filter(data)=='No Filter' and get_filter(data) or removeParentNode('para') ]] - [[ get_filter(data)=='Date' or removeParentNode('blockTable') ]] + [[ data['form']['filter']=='filter_no' and get_filter(data) or removeParentNode('para') ]] + [[ data['form']['filter']=='filter_date' or removeParentNode('blockTable') ]] Start Date End Date @@ -208,7 +208,7 @@ [[ formatLang(get_end_date(data),date=True) ]] - [[ get_filter(data)=='Periods' or removeParentNode('blockTable') ]] + [[ data['form']['filter']=='filter_period' or removeParentNode('blockTable') ]] Start Period End Period diff --git a/addons/account/report/account_partner_balance.rml b/addons/account/report/account_partner_balance.rml index 6425d6a4db6..f913a07df7a 100644 --- a/addons/account/report/account_partner_balance.rml +++ b/addons/account/report/account_partner_balance.rml @@ -187,7 +187,7 @@ Chart of Account Fiscal Year Journals - Filter By [[ get_filter(data)!='No Filter' and get_filter(data) ]] + Filter By [[ data['form']['filter']!='filter_no' and get_filter(data) ]] Partner's Target Moves @@ -196,8 +196,8 @@ [[ get_fiscalyear(data) or '' ]] [[', '.join([ lt or '' for lt in get_journal(data) ]) ]] - [[ get_filter(data)=='No Filter' and get_filter(data) or removeParentNode('para') ]] - [[ get_filter(data)=='Date' or removeParentNode('blockTable') ]] + [[ data['form']['filter']=='filter_no' and get_filter(data) or removeParentNode('para') ]] + [[ data['form']['filter']=='filter_date' or removeParentNode('blockTable') ]] Start Date End Date @@ -207,7 +207,7 @@ [[ formatLang(get_end_date(data),date=True) ]] - [[ get_filter(data)=='Periods' or removeParentNode('blockTable') ]] + [[ data['form']['filter']=='filter_period' or removeParentNode('blockTable') ]] Start Period End Period diff --git a/addons/account/report/account_partner_ledger.rml b/addons/account/report/account_partner_ledger.rml index c1ef9b22502..3af9c247d42 100644 --- a/addons/account/report/account_partner_ledger.rml +++ b/addons/account/report/account_partner_ledger.rml @@ -378,7 +378,7 @@ [[ data['form']['filter'] in ('filter_no','unreconciled') and get_filter(data) or removeParentNode('para') ]] - [[ get_filter(data)=='Date' or removeParentNode('blockTable') ]] + [[ data['form']['filter']=='filter_date' or removeParentNode('blockTable') ]] Start Date @@ -388,7 +388,7 @@ - [[ get_filter(data)=='Date' or removeParentNode('blockTable') ]] + [[ data['form']['filter']=='filter_date' or removeParentNode('blockTable') ]] [[ formatLang(get_start_date(data),date=True) ]] @@ -398,7 +398,7 @@ - [[ get_filter(data)=='Periods' or removeParentNode('blockTable') ]] + [[ data['form']['filter']=='filter_period' or removeParentNode('blockTable') ]] Start Period @@ -408,7 +408,7 @@ - [[ get_filter(data)=='Periods' or removeParentNode('blockTable') ]] + [[ data['form']['filter']=='filter_period' or removeParentNode('blockTable') ]] [[ get_start_period(data) or removeParentNode('para') ]] diff --git a/addons/account/report/account_partner_ledger_other.rml b/addons/account/report/account_partner_ledger_other.rml index 2f58fc8ec95..1a19844c180 100644 --- a/addons/account/report/account_partner_ledger_other.rml +++ b/addons/account/report/account_partner_ledger_other.rml @@ -379,7 +379,7 @@ [[ data['form']['filter'] in ('filter_no','unreconciled') and get_filter(data) or removeParentNode('para') ]] - [[ get_filter(data)=='Date' or removeParentNode('blockTable') ]] + [[ data['form']['filter']=='filter_date' or removeParentNode('blockTable') ]] Start Date @@ -389,7 +389,7 @@ - [[ get_filter(data)=='Date' or removeParentNode('blockTable') ]] + [[ data['form']['filter']=='filter_date' or removeParentNode('blockTable') ]] [[ formatLang(get_start_date(data),date=True) ]] @@ -399,7 +399,7 @@ - [[ get_filter(data)=='Periods' or removeParentNode('blockTable') ]] + [[ data['form']['filter']=='filter_period' or removeParentNode('blockTable') ]] Start Period @@ -409,7 +409,7 @@ - [[ get_filter(data)=='Periods' or removeParentNode('blockTable') ]] + [[ data['form']['filter']=='filter_period' or removeParentNode('blockTable') ]] [[ get_start_period(data) or removeParentNode('para') ]] diff --git a/addons/account/report/account_profit_horizontal.rml b/addons/account/report/account_profit_horizontal.rml index 6c70bb47310..ea03e8f3a9f 100644 --- a/addons/account/report/account_profit_horizontal.rml +++ b/addons/account/report/account_profit_horizontal.rml @@ -151,15 +151,15 @@ Chart of Account Fiscal Year - Filter By [[ get_filter(data)!='No Filter' and get_filter(data) ]] + Filter By [[ data['form']['filter']!='filter_no' and get_filter(data) ]] Display Account Target Moves [[ get_account(data) or removeParentNode('para') ]] [[ get_fiscalyear(data) or '' ]] - [[ get_filter(data)=='No Filter' and get_filter(data) or removeParentNode('para') ]] - [[ get_filter(data)=='Date' or removeParentNode('blockTable') ]] + [[ data['form']['filter']=='filter_no' and get_filter(data) or removeParentNode('para') ]] + [[ data['form']['filter']=='filter_date' or removeParentNode('blockTable') ]] Start Date End Date @@ -169,7 +169,7 @@ [[ formatLang(get_end_date(data),date=True) ]] - [[ get_filter(data)=='Periods' or removeParentNode('blockTable') ]] + [[ data['form']['filter']=='filter_period' or removeParentNode('blockTable') ]] Start Period End Period diff --git a/addons/account/report/account_profit_loss.rml b/addons/account/report/account_profit_loss.rml index 2b29f479478..e31da90e46e 100644 --- a/addons/account/report/account_profit_loss.rml +++ b/addons/account/report/account_profit_loss.rml @@ -174,15 +174,15 @@ Chart of Account Fiscal Year - Filter By [[ get_filter(data)!='No Filter' and get_filter(data) ]] + Filter By [[ data['form']['filter']!='filter_no' and get_filter(data) ]] Display Account Target Moves [[ get_account(data) or removeParentNode('para') ]] [[ get_fiscalyear(data) or '' ]] - [[ get_filter(data)=='No Filter' and get_filter(data) or removeParentNode('para') ]] - [[ get_filter(data)=='Date' or removeParentNode('blockTable') ]] + [[ data['form']['filter']=='filter_no' and get_filter(data) or removeParentNode('para') ]] + [[ data['form']['filter']=='filter_date' or removeParentNode('blockTable') ]] Start Date End Date @@ -192,7 +192,7 @@ [[ formatLang(get_end_date(data),date=True) ]] - [[ get_filter(data)=='Periods' or removeParentNode('blockTable') ]] + [[ data['form']['filter']=='filter_period' or removeParentNode('blockTable') ]] Start Period End Period From e84d292ceb36d53f40aba7c0a842b4f779ea4006 Mon Sep 17 00:00:00 2001 From: "Ujjvala Collins (OpenERP)" Date: Mon, 18 Jul 2011 15:23:44 +0530 Subject: [PATCH 011/409] [IMP] sale: Improved code by adding context argument wherever required. * product_visible_discount, warning: Added remaining context argument on product_id_change method. bzr revid: uco@tinyerp.com-20110718095344-tdlsys7uueqp9kdy --- addons/product_visible_discount/product_visible_discount.py | 5 +++-- addons/sale/sale.py | 2 +- addons/sale/sale_view.xml | 2 +- addons/sale_layout/sale_layout_view.xml | 2 +- addons/warning/warning.py | 4 ++-- 5 files changed, 8 insertions(+), 7 deletions(-) diff --git a/addons/product_visible_discount/product_visible_discount.py b/addons/product_visible_discount/product_visible_discount.py index c2f939c1618..2571855ec89 100644 --- a/addons/product_visible_discount/product_visible_discount.py +++ b/addons/product_visible_discount/product_visible_discount.py @@ -40,7 +40,8 @@ class sale_order_line(osv.osv): def product_id_change(self, cr, uid, ids, pricelist, product, qty=0, uom=False, qty_uos=0, uos=False, name='', partner_id=False, - lang=False, update_tax=True,date_order=False,packaging=False,fiscal_position=False, flag=False): + lang=False, update_tax=True, date_order=False, packaging=False, + fiscal_position=False, flag=False, context=None): def get_real_price(res_dict, product_id, qty, uom, pricelist): item_obj = self.pool.get('product.pricelist.item') @@ -70,7 +71,7 @@ class sale_order_line(osv.osv): res=super(sale_order_line, self).product_id_change(cr, uid, ids, pricelist, product, qty, uom, qty_uos, uos, name, partner_id, - lang, update_tax,date_order,fiscal_position=fiscal_position,flag=flag) + lang, update_tax, date_order, fiscal_position=fiscal_position, flag=flag, context=context) context = {'lang': lang, 'partner_id': partner_id} result=res['value'] diff --git a/addons/sale/sale.py b/addons/sale/sale.py index 06bdbf44a10..ad79506f8a8 100644 --- a/addons/sale/sale.py +++ b/addons/sale/sale.py @@ -1024,7 +1024,7 @@ class sale_order_line(osv.osv): if flag: res = self.product_id_change(cr, uid, ids, pricelist=pricelist, product=product, qty=qty, uom=uom, partner_id=partner_id, - packaging=packaging, flag=False) + packaging=packaging, flag=False, context=context) warning_msgs = res.get('warning') and res['warning']['message'] products = product_obj.browse(cr, uid, product, context=context) diff --git a/addons/sale/sale_view.xml b/addons/sale/sale_view.xml index 72314f945da..d191cd3553f 100644 --- a/addons/sale/sale_view.xml +++ b/addons/sale/sale_view.xml @@ -142,7 +142,7 @@ diff --git a/addons/sale_layout/sale_layout_view.xml b/addons/sale_layout/sale_layout_view.xml index 407d15a5c04..c4e20b979ab 100644 --- a/addons/sale_layout/sale_layout_view.xml +++ b/addons/sale_layout/sale_layout_view.xml @@ -38,7 +38,7 @@ name="product_packaging" attrs="{'readonly':[('layout_type','!=','article')]}" context="{'partner_id':parent.partner_id,'quantity':product_uom_qty,'pricelist':parent.pricelist_id,'shop':parent.shop_id,'uom':product_uom}" - on_change="product_packaging_change(parent.pricelist_id, product_id, product_uom_qty, product_uom, parent.partner_id, product_packaging, True)" + on_change="product_packaging_change(parent.pricelist_id, product_id, product_uom_qty, product_uom, parent.partner_id, product_packaging, True, context)" domain="[('product_id','=',product_id)]" groups="base.group_extended"/> diff --git a/addons/warning/warning.py b/addons/warning/warning.py index ab7a5926653..e5bdf07aef3 100644 --- a/addons/warning/warning.py +++ b/addons/warning/warning.py @@ -197,7 +197,7 @@ class sale_order_line(osv.osv): def product_id_change(self, cr, uid, ids, pricelist, product, qty=0, uom=False, qty_uos=0, uos=False, name='', partner_id=False, lang=False, update_tax=True, date_order=False, packaging=False, - fiscal_position=False, flag=False): + fiscal_position=False, flag=False, context=None): warning = {} if not product: return {'value': {'th_weight' : 0, 'product_packaging': False, @@ -218,7 +218,7 @@ class sale_order_line(osv.osv): result = super(sale_order_line, self).product_id_change( cr, uid, ids, pricelist, product, qty, uom, qty_uos, uos, name, partner_id, - lang, update_tax, date_order, packaging, fiscal_position, flag) + lang, update_tax, date_order, packaging, fiscal_position, flag, context=context) if result.get('warning',False): warning['title'] = title and title +' & '+result['warning']['title'] or result['warning']['title'] From 43f07464800836a8da3f3eacd13ddf8dba9e5658 Mon Sep 17 00:00:00 2001 From: Minh Tran Date: Mon, 18 Jul 2011 18:17:46 +0200 Subject: [PATCH 012/409] [imp] Added POS category bzr revid: mit@openerp.com-20110718161746-megdu9xb1jeu19g9 --- addons/point_of_sale/point_of_sale.py | 23 +++++++---- addons/point_of_sale/point_of_sale_view.xml | 46 +++++++++++++++++---- 2 files changed, 52 insertions(+), 17 deletions(-) diff --git a/addons/point_of_sale/point_of_sale.py b/addons/point_of_sale/point_of_sale.py index 90bfa975a76..62c5438389f 100644 --- a/addons/point_of_sale/point_of_sale.py +++ b/addons/point_of_sale/point_of_sale.py @@ -425,15 +425,12 @@ class pos_order(osv.osv): move_obj = self.pool.get('stock.move') pick_name = self.pool.get('ir.sequence').get(cr, uid, 'stock.picking.out') orders = self.browse(cr, uid, ids, context=context) - partner_obj = self.pool.get('res.partner') for order in orders: - addr = order.partner_id and partner_obj.address_get(cr, uid, [order.partner_id.id], ['delivery']) or {} if not order.picking_id: new = True picking_id = picking_obj.create(cr, uid, { 'name': pick_name, 'origin': order.name, - 'address_id': addr.get('delivery',False), 'type': 'out', 'state': 'draft', 'move_type': 'direct', @@ -479,7 +476,6 @@ class pos_order(osv.osv): 'state': 'waiting', 'location_id': location_id, 'location_dest_id': stock_dest_id, - 'prodlot_id': line.prodlot_id and line.prodlot_id.id or False }, context=context) wf_service = netsvc.LocalService("workflow") @@ -650,7 +646,6 @@ class pos_order(osv.osv): 'reference': order.name, 'partner_id': order.partner_id.id, 'comment': order.note or '', - 'currency_id': order.pricelist_id.currency_id.id, # considering partner's sale pricelist's currency } inv.update(inv_ref.onchange_partner_id(cr, uid, [], 'out_invoice', order.partner_id.id)['value']) if not inv.get('account_id', None): @@ -897,13 +892,13 @@ class pos_order(osv.osv): for pos in self.browse(cr, uid, ids, context=context): create_contract_nb = False for line in pos.lines: - if line.product_id.product_type == 'MD': + if line.product_id.type == 'MD': create_contract_nb = True break if create_contract_nb: seq = sequence_obj.get(cr, uid, 'pos.user_%s' % pos.user_salesman_id.login) vals['contract_number'] = '%s-%s' % (pos.user_salesman_id.login, seq) - self.write(cr, uid, ids, vals, context=context) + return self.write(cr, uid, ids, vals, context=context) def action_paid(self, cr, uid, ids, context=None): if context is None: @@ -1149,7 +1144,6 @@ class pos_order_line(osv.osv): 'discount': fields.float('Discount (%)', digits=(16, 2)), 'order_id': fields.many2one('pos.order', 'Order Ref', ondelete='cascade'), 'create_date': fields.datetime('Creation Date', readonly=True), - 'prodlot_id': fields.many2one('stock.production.lot', 'Production Lot', help="You can specify Production lot for stock move created when you validate the pos order"), } _defaults = { @@ -1226,6 +1220,15 @@ class pos_order_line(osv.osv): pos_order_line() +class pos_category(osv.osv): + _name = 'pos.category' + _inherit = 'product.category' + _columns = { + 'parent_id': fields.many2one('pos.category','Parent Category', select=True), + 'child_id': fields.one2many('pos.category', 'parent_id', string='Child Categories'), + } +pos_category() + class product_product(osv.osv): _inherit = 'product.product' _columns = { @@ -1233,9 +1236,13 @@ class product_product(osv.osv): 'expense_pdt': fields.boolean('Product for expenses'), 'am_out': fields.boolean('Control for Output Operations'), 'disc_controle': fields.boolean('Discount Control'), + 'img': fields.binary('Pos Image, must be 50x50'), + 'pos_ok': fields.boolean('Can be POS-sold'), + 'pos_categ_id': fields.many2one('pos.category','POS Category', change_default=True, domain="[('type','=','normal')]" ,help="Select a pos category for the current product") } _defaults = { 'disc_controle': True, + 'pos_ok': True, } product_product() diff --git a/addons/point_of_sale/point_of_sale_view.xml b/addons/point_of_sale/point_of_sale_view.xml index 1435f6cc2b6..80db7ece330 100644 --- a/addons/point_of_sale/point_of_sale_view.xml +++ b/addons/point_of_sale/point_of_sale_view.xml @@ -41,7 +41,6 @@ - @@ -89,7 +88,7 @@ - + @@ -639,11 +638,12 @@ form - - - - - + + + + + + @@ -653,16 +653,44 @@ form - - + + + + + + + + + pos.category.tree + pos.category + tree + child_id + + + + + + + + Products by POS-Category + ir.actions.act_window + pos.category + [('parent_id','=',False)] + tree + + Here is a list of all your products classified by POS category. You can click a category to get the list of all products linked to this category or to a child of this category. + + + + product.normal.form.inherit product.product From c38c7d055cbb198f0696048e5b2fdac6aac12592 Mon Sep 17 00:00:00 2001 From: Manu <> Date: Tue, 19 Jul 2011 18:37:40 +0530 Subject: [PATCH 013/409] [FIX] account : improved the dates in recurring entries lp bug: https://launchpad.net/bugs/800767 fixed bzr revid: bde@tinyerp.com-20110719130740-eq87ant192zguo0n --- addons/account/account.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/addons/account/account.py b/addons/account/account.py index 03a9b628922..64aab95ee29 100644 --- a/addons/account/account.py +++ b/addons/account/account.py @@ -1331,7 +1331,7 @@ class account_move(osv.osv): def _centralise(self, cr, uid, move, mode, context=None): assert mode in ('debit', 'credit'), 'Invalid Mode' #to prevent sql injection - currency_obj = self.pool.get('res.currency') + currency_obj = self.pool.get('res.currency') if context is None: context = {} @@ -1593,7 +1593,7 @@ class account_tax_code(osv.osv): (parent_ids,) + where_params) res=dict(cr.fetchall()) obj_precision = self.pool.get('decimal.precision') - res2 = {} + res2 = {} for record in self.browse(cr, uid, ids, context=context): def _rec_get(record): amount = res.get(record.id, 0.0) @@ -2120,7 +2120,7 @@ class account_model(osv.osv): period_id = period_id[0] for model in self.browse(cr, uid, ids, context=context): - entry['name'] = model.name%{'year':time.strftime('%Y'), 'month':time.strftime('%m'), 'date':time.strftime('%Y-%m')} + entry['name'] = model.name%{'year':context.get('date',time.strftime('%Y')), 'month':context.get('date',time.strftime('%m')), 'date':context.get('date',time.strftime('%d'))} move_id = account_move_obj.create(cr, uid, { 'ref': entry['name'], 'period_id': period_id, @@ -2141,7 +2141,7 @@ class account_model(osv.osv): 'analytic_account_id': analytic_account_id } - date_maturity = time.strftime('%Y-%m-%d') + date_maturity = context.get('date',time.strftime('%Y-%m-%d')) if line.date_maturity == 'partner': if not line.partner_id: raise osv.except_osv(_('Error !'), _("Maturity date of entry line generated by model line '%s' of model '%s' is based on partner payment term!" \ From e955394d4503d207c135a5476c684e2f32e2a96c Mon Sep 17 00:00:00 2001 From: "Bharat (OpenERP)" Date: Wed, 20 Jul 2011 17:09:53 +0530 Subject: [PATCH 014/409] [IMP] account : improved the context date in account model bzr revid: bde@tinyerp.com-20110720113953-h6774pxnk7gqpeu7 --- addons/account/account.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/addons/account/account.py b/addons/account/account.py index 64aab95ee29..0364858fc58 100644 --- a/addons/account/account.py +++ b/addons/account/account.py @@ -21,6 +21,7 @@ import time from datetime import datetime +from time import mktime from dateutil.relativedelta import relativedelta from operator import itemgetter @@ -2120,7 +2121,10 @@ class account_model(osv.osv): period_id = period_id[0] for model in self.browse(cr, uid, ids, context=context): - entry['name'] = model.name%{'year':context.get('date',time.strftime('%Y')), 'month':context.get('date',time.strftime('%m')), 'date':context.get('date',time.strftime('%d'))} + context_date = context.get('date') + str_date = time.strptime(context_date,"%Y-%m-%d") + date = datetime.fromtimestamp(mktime(str_date)) + entry['name'] = model.name%{'year':date.strftime("%Y"), 'month':date.strftime("%m"), 'date': date.strftime("%Y-%m")} move_id = account_move_obj.create(cr, uid, { 'ref': entry['name'], 'period_id': period_id, From 2f198e85766e432dcf8d628a96ca3d1d55a9bfee Mon Sep 17 00:00:00 2001 From: "ARA (OpenERP)" Date: Tue, 26 Jul 2011 12:09:59 +0530 Subject: [PATCH 015/409] [FIX] account_cancel: re-open invoice assigned to useability/no one lp bug: https://launchpad.net/bugs/807543 fixed bzr revid: ara@tinyerp.com-20110726063959-lh8o2qjc697xe1jo --- addons/account_cancel/account_cancel_view.xml | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/addons/account_cancel/account_cancel_view.xml b/addons/account_cancel/account_cancel_view.xml index 9e8b532e931..786afb9e9f0 100644 --- a/addons/account_cancel/account_cancel_view.xml +++ b/addons/account_cancel/account_cancel_view.xml @@ -12,5 +12,30 @@ + + + invoice.form.inherit + account.invoice + form + + + + +
    +
+ + +
+

Minh Tran

+ +
+ + +
+ +
+
+ + + + + + + + + + + + +
ProductPriceDisc (%)QtyTotal
+
+
+
    +
  • Subtotal: 0 €
  • +
  • Tax: 0 €
  • +
  • Total: 0 €
  • +
+
+
+
+ + + +
+ + + +
+ + + +
+ + + +
+
+
+
+ +
+
+ + +
+ +
+ + + + + + + + + + + + + + + + + From 14ba370293cf418a2553fb106c1592d80464c2d4 Mon Sep 17 00:00:00 2001 From: Antony Lesuisse Date: Tue, 27 Sep 2011 18:37:09 +0200 Subject: [PATCH 094/409] account_voucher multicurrency scenario bzr revid: al@openerp.com-20110927163709-08ja2ruir230c6hu --- addons/account_voucher/test/case1_usd_usd.yml | 355 +++++++++++++++++ .../test/case2_suppl_usd_eur.yml | 328 ++++++++++++++++ addons/account_voucher/test/case2_usd_eur.yml | 360 ++++++++++++++++++ addons/account_voucher/test/case3_eur_eur.yml | 331 ++++++++++++++++ addons/account_voucher/test/case4_cad_chf.yml | 194 ++++++++++ 5 files changed, 1568 insertions(+) create mode 100644 addons/account_voucher/test/case1_usd_usd.yml create mode 100644 addons/account_voucher/test/case2_suppl_usd_eur.yml create mode 100644 addons/account_voucher/test/case2_usd_eur.yml create mode 100644 addons/account_voucher/test/case3_eur_eur.yml create mode 100644 addons/account_voucher/test/case4_cad_chf.yml diff --git a/addons/account_voucher/test/case1_usd_usd.yml b/addons/account_voucher/test/case1_usd_usd.yml new file mode 100644 index 00000000000..dc7654ab9c3 --- /dev/null +++ b/addons/account_voucher/test/case1_usd_usd.yml @@ -0,0 +1,355 @@ +- + In order to check the Account_voucher module with multi-currency in OpenERP, + I create 2 Invoices in USD and make 2 Payments in USD based on the currency rating on that particular date +- + I create currency USD in OpenERP for January of 1.333333 Rate +- + !python {model: res.currency.rate}: | + from datetime import datetime + curr_id = self.pool.get('res.currency').search(cr, uid, [('name', '=', 'USD')]) + date = '%s-01-01' %(datetime.now().year) + ids = self.search(cr, uid, [('currency_id','=',curr_id), ('name', '=', date)]) + self.write(cr, uid, ids, {'rate': 1.333333}) +- + I create currency USD in OpenERP for February of 1.250000 Rate +- + !record {model: res.currency.rate, id: feb_usd}: + currency_id: base.USD + name: !eval "'%s-02-01' %(datetime.now().year)" + rate: 1.250000 + +- + I create currency USD in OpenERP for March of 1.111111 Rate +- + !record {model: res.currency.rate, id: mar_usd}: + currency_id: base.USD + name: !eval "'%s-03-01' %(datetime.now().year)" + rate: 1.111111 + +- + I create currency USD in OpenERP for April of 1.052632 Rate +- + !record {model: res.currency.rate, id: apr_usd}: + currency_id: base.USD + name: !eval "'%s-04-01' %(datetime.now().year)" + rate: 1.052632 + +- + I create a bank journal with USD as currency +- + !record {model: account.journal, id: bank_journal_USD}: + name: Bank Journal(USD) + code: BUSD + type: bank + analytic_journal_id: account.sit + sequence_id: account.sequence_bank_journal + default_debit_account_id: account.cash + default_credit_account_id: account.cash + currency: base.USD + company_id: base.main_company + view_id: account.account_journal_bank_view + +- + I set up some accounts for currency rate expense/income in my company +- + !record {model: res.company, id: base.main_company}: + property_expense_currency_exchange: account.o_expense + property_income_currency_exchange: account.o_income +- + I create new partner Mark Strauss. +- + !record {model: res.partner, id: res_partner_strauss0}: + address: + - city: paris + country_id: base.fr + name: Mark Strauss + street: 1 rue Rockfeller + type: invoice + zip: '75016' + name: Mr. Mark Strauss +- + I create the first invoice on 1st January for 200 USD +- + !record {model: account.invoice, id: account_invoice_jan}: + account_id: account.a_recv + address_contact_id: base.res_partner_address_3000 + address_invoice_id: base.res_partner_address_3000 + company_id: base.main_company + currency_id: base.USD + date_invoice: !eval "'%s-01-01' %(datetime.now().year)" + period_id: account.period_1 + invoice_line: + - account_id: account.a_sale + name: '[PC1] Basic PC' + price_unit: 200.0 + quantity: 1.0 + product_id: product.product_product_pc1 + uos_id: product.product_uom_unit + journal_id: account.sales_journal + partner_id: res_partner_strauss0 + reference_type: none +- + I Validate invoice by clicking on Validate button +- + !workflow {model: account.invoice, action: invoice_open, ref: account_invoice_jan} +- + I check that first invoice move is correct for debtor account (debit - credit == 150.0) +- + !python {model: account.invoice}: | + invoice_id = self.browse(cr, uid, ref("account_invoice_jan")) + assert invoice_id.move_id, "Move not created for open invoice" + move_line_obj = self.pool.get('account.move.line') + move_lines = move_line_obj.search(cr, uid, [('move_id', '=', invoice_id.move_id.id), ('account_id', '=', invoice_id.account_id.id)]) + move_line = move_line_obj.browse(cr, uid, move_lines[0]) + assert (move_line.debit - move_line.credit == 150.0), "Invoice move is not correct for debtors account" +- + I create the second invoice on 1st February for 100 USD +- + !record {model: account.invoice, id: account_invoice_feb}: + account_id: account.a_recv + address_contact_id: base.res_partner_address_3000 + address_invoice_id: base.res_partner_address_3000 + company_id: base.main_company + currency_id: base.USD + date_invoice: !eval "'%s-02-01' %(datetime.now().year)" + period_id: account.period_2 + invoice_line: + - account_id: account.a_sale + name: '[PC1] Basic PC' + price_unit: 100.0 + quantity: 1.0 + product_id: product.product_product_pc1 + uos_id: product.product_uom_unit + journal_id: account.sales_journal + partner_id: res_partner_strauss0 + reference_type: none +- + I Validate invoice by clicking on Validate button +- + !workflow {model: account.invoice, action: invoice_open, ref: account_invoice_feb} +- + I check that second invoice move is correct for debtor account (debit - credit == 80) +- + !python {model: account.invoice}: | + invoice_id = self.browse(cr, uid, ref("account_invoice_feb")) + assert invoice_id.move_id, "Move not created for open invoice" + move_line_obj = self.pool.get('account.move.line') + move_lines = move_line_obj.search(cr, uid, [('move_id', '=', invoice_id.move_id.id), ('account_id', '=', invoice_id.account_id.id)]) + move_line = move_line_obj.browse(cr, uid, move_lines[0]) + assert (move_line.debit - move_line.credit == 80), "Invoice move is not correct for debtors account" + +- + I create the first voucher of payment + +- + !python {model: account.voucher}: | + import netsvc, time + vals = {} + res = self.onchange_partner_id(cr, uid, [], ref("res_partner_strauss0"), ref('bank_journal_USD'), 240.00, 2, ttype='receipt', date=False) + vals = { + 'account_id': ref('account.cash'), + 'amount': 240.00, + 'company_id': ref('base.main_company'), + 'currency_id': ref('base.USD'), + 'journal_id': ref('bank_journal_USD'), + 'partner_id': ref('res_partner_strauss0'), + 'period_id': ref('account.period_3'), + 'type': 'receipt', + 'date': time.strftime("%Y-03-01"), + 'payment_option': 'with_writeoff', + 'writeoff_acc_id': ref('account.a_expense'), + 'comment': 'Write Off', + 'name': 'First payment', + } + if not res['value']['line_cr_ids']: + res['value']['line_cr_ids'] = [{'type': 'cr', 'account_id': ref('account.a_recv'),}] + for item in res['value']['line_cr_ids']: + if item['amount_unreconciled'] == 200.00: + item['amount'] = 180.00 + else: + item['amount'] = 70.00 + vals['line_cr_ids'] = [(0,0,i) for i in res['value']['line_cr_ids']] + id = self.create(cr, uid, vals) + voucher_id = self.browse(cr, uid, id) + assert (voucher_id.state=='draft'), "Voucher is not in draft state" +- + I check that writeoff amount computed is -10.0 +- + !python {model: account.voucher}: | + voucher = self.search(cr, uid, [('name', '=', 'First payment'), ('partner_id', '=', ref('res_partner_strauss0'))]) + voucher_id = self.browse(cr, uid, voucher[0]) + assert (voucher_id.writeoff_amount == -10.0), "Writeoff amount is not -10.0" +- + I check that currency rate difference is -34.0 +- + !python {model: account.voucher}: | + voucher = self.search(cr, uid, [('name', '=', 'First payment'), ('partner_id', '=', ref('res_partner_strauss0'))]) + voucher_id = self.browse(cr, uid, voucher[0]) + assert (voucher_id.currency_rate_difference == -34.0), "Currency rate difference is not -34.0" +- + I confirm the voucher +- + !python {model: account.voucher}: | + import netsvc + voucher = self.search(cr, uid, [('name', '=', 'First payment'), ('partner_id', '=', ref('res_partner_strauss0'))]) + wf_service = netsvc.LocalService("workflow") + wf_service.trg_validate(uid, 'account.voucher', voucher[0], 'proforma_voucher', cr) +- + I check that the move of my first voucher is valid +- + !python {model: account.voucher}: | + voucher = self.search(cr, uid, [('name', '=', 'First payment'), ('partner_id', '=', ref('res_partner_strauss0'))]) + voucher_id = self.browse(cr, uid, voucher[0]) + move_line_obj = self.pool.get('account.move.line') + move_lines = move_line_obj.search(cr, uid, [('move_id', '=', voucher_id.move_id.id)]) + for move_line in move_line_obj.browse(cr, uid, move_lines): + assert move_line.state == 'valid', "Voucher move is not valid" +- + I check that my debtor account is correct +- + I check that the debtor account has 2 new lines with -180 and -70 as amount_currency columns and that their credit columns are respectively 135 and 56 +- + I check that my currency rate difference is correct. 34 in credit with no amount_currency +- + I check that my write-off is correct. 9 debit and 10 amount_currency +- + !python {model: account.voucher}: | + voucher = self.search(cr, uid, [('name', '=', 'First payment'), ('partner_id', '=', ref('res_partner_strauss0'))]) + voucher_id = self.browse(cr, uid, voucher[0]) + move_line_obj = self.pool.get('account.move.line') + move_lines = move_line_obj.search(cr, uid, [('move_id', '=', voucher_id.move_id.id)]) + for move_line in move_line_obj.browse(cr, uid, move_lines): + if move_line.amount_currency == -180.00: + assert move_line.credit == 135.00, "Debtor account has wrong entry." + elif move_line.amount_currency == -70.00: + assert move_line.credit == 56.00, "Debtor account has wrong entry." + elif move_line.credit == 34.00: + assert move_line.amount_currency == 0.00, "Incorrect currency rate difference." + elif move_line.amount_currency == 10.00: + assert move_line.debit == 9.00, "Writeoff amount is wrong." +- + I check the residual amount of Invoice1, should be 20 in amount_currency and 15 in company currency +- + !python {model: account.invoice}: | + invoice_id = self.browse(cr, uid, ref("account_invoice_jan")) + move_line_obj = self.pool.get('account.move.line') + move_lines = move_line_obj.search(cr, uid, [('move_id', '=', invoice_id.move_id.id), ('invoice', '=', invoice_id.id), ('account_id', '=', invoice_id.account_id.id)]) + move_line = move_line_obj.browse(cr, uid, move_lines[0]) + assert (move_line.amount_residual_currency == 20.0 and move_line.amount_residual == 15.0) , "Residual amount is not correct for first Invoice" +- + I check the residual amuont of Invoice2, should be 30 in residual currency and 24 in amount_residual +- + !python {model: account.invoice}: | + invoice_id = self.browse(cr, uid, ref("account_invoice_feb")) + move_line_obj = self.pool.get('account.move.line') + move_lines = move_line_obj.search(cr, uid, [('move_id', '=', invoice_id.move_id.id), ('invoice', '=', invoice_id.id), ('account_id', '=', invoice_id.account_id.id)]) + move_line = move_line_obj.browse(cr, uid, move_lines[0]) + assert (move_line.amount_residual_currency == 30.0 and move_line.amount_residual == 24.0) , "Residual amount is not correct for first Invoice" +- + I create the second voucher of payment + + +- + !python {model: account.voucher}: | + import netsvc, time + vals = {} + res = self.onchange_partner_id(cr, uid, [], ref("res_partner_strauss0"), ref('bank_journal_USD'), 45.00, 2, ttype='receipt', date=False) + vals = { + 'account_id': ref('account.cash'), + 'amount': 45.00, + 'company_id': ref('base.main_company'), + 'currency_id': ref('base.USD'), + 'journal_id': ref('bank_journal_USD'), + 'partner_id': ref('res_partner_strauss0'), + 'period_id': ref('account.period_3'), + 'type': 'receipt', + 'date': time.strftime("%Y-04-01"), + 'payment_option': 'with_writeoff', + 'writeoff_acc_id': ref('account.a_expense'), + 'comment': 'Write Off', + 'name': 'Second payment', + } + if not res['value']['line_cr_ids']: + res['value']['line_cr_ids'] = [{'type': 'cr', 'account_id': ref('account.a_recv'),}] + for item in res['value']['line_cr_ids']: + if item['amount_unreconciled'] == 20.00: + item['amount'] = 20.00 + else: + item['amount'] = 30.00 + vals['line_cr_ids'] = [(0,0,i) for i in res['value']['line_cr_ids']] + id = self.create(cr, uid, vals) + voucher_id = self.browse(cr, uid, id) + assert (voucher_id.state=='draft'), "Voucher is not in draft state" +- + I check that writeoff amount computed is -5.0 +- + !python {model: account.voucher}: | + voucher = self.search(cr, uid, [('name', '=', 'Second payment'), ('partner_id', '=', ref('res_partner_strauss0'))]) + voucher_id = self.browse(cr, uid, voucher[0]) + assert (voucher_id.writeoff_amount == -5.0), "Writeoff amount is not -5.0" +- + I check that currency rate difference is -8.50 +- + !python {model: account.voucher}: | + voucher = self.search(cr, uid, [('name', '=', 'Second payment'), ('partner_id', '=', ref('res_partner_strauss0'))]) + voucher_id = self.browse(cr, uid, voucher[0]) + assert (voucher_id.currency_rate_difference == -8.50), "Currency rate difference is not -8.50" +- + I confirm the voucher +- + !python {model: account.voucher}: | + import netsvc + voucher = self.search(cr, uid, [('name', '=', 'Second payment'), ('partner_id', '=', ref('res_partner_strauss0'))]) + wf_service = netsvc.LocalService("workflow") + wf_service.trg_validate(uid, 'account.voucher', voucher[0], 'proforma_voucher', cr) +- + I check that the move of my second voucher is valid +- + !python {model: account.voucher}: | + voucher = self.search(cr, uid, [('name', '=', 'Second payment'), ('partner_id', '=', ref('res_partner_strauss0'))]) + voucher_id = self.browse(cr, uid, voucher[0]) + move_line_obj = self.pool.get('account.move.line') + move_lines = move_line_obj.search(cr, uid, [('move_id', '=', voucher_id.move_id.id)]) + for move_line in move_line_obj.browse(cr, uid, move_lines): + assert move_line.state == 'valid', "Voucher move is not valid" +- + I check that my debtor account is correct +- + I check that the debtor account has 2 new lines with -20 and -30 as amount_currency columns and their credit columns are respectively 15 and 24. +- + I check that my currency rate difference is correct. 8.5 in credit with no amount_currency +- + I check that my writeoff is correct. 4.75 debit and 5 amount_currency +- + !python {model: account.voucher}: | + voucher = self.search(cr, uid, [('name', '=', 'Second payment'), ('partner_id', '=', ref('res_partner_strauss0'))]) + voucher_id = self.browse(cr, uid, voucher[0]) + move_line_obj = self.pool.get('account.move.line') + move_lines = move_line_obj.search(cr, uid, [('move_id', '=', voucher_id.move_id.id)]) + for move_line in move_line_obj.browse(cr, uid, move_lines): + if move_line.amount_currency == -20.00: + assert move_line.credit == 15.00, "Debtor account has wrong entry." + elif move_line.amount_currency == -30.00: + assert move_line.credit == 24.00, "Debtor account has wrong entry." + elif move_line.credit == 8.50: + assert move_line.amount_currency == 0.00, "Incorrect Currency Difference." + elif move_line.amount_currency == 5.00: + assert move_line.debit == 4.75, "Writeoff amount is wrong." +- + I check the residual amount of Invoice1, should be 0 in residual currency and 0 in amount_residual and paid +- + !python {model: account.invoice}: | + invoice_id = self.browse(cr, uid, ref("account_invoice_jan")) + move_line_obj = self.pool.get('account.move.line') + move_lines = move_line_obj.search(cr, uid, [('move_id', '=', invoice_id.move_id.id), ('invoice', '=', invoice_id.id), ('account_id', '=', invoice_id.account_id.id)]) + move_line = move_line_obj.browse(cr, uid, move_lines[0]) + assert (move_line.amount_residual_currency == 0.0 and move_line.amount_residual == 0.0 and invoice_id.state == 'paid') , "Residual amount is not correct for first Invoice" +- + I check the residual amuont of Invoice2, should be 0 in residual currency and 0 in amount_residual and paid +- + !python {model: account.invoice}: | + invoice_id = self.browse(cr, uid, ref("account_invoice_feb")) + move_line_obj = self.pool.get('account.move.line') + move_lines = move_line_obj.search(cr, uid, [('move_id', '=', invoice_id.move_id.id), ('invoice', '=', invoice_id.id), ('account_id', '=', invoice_id.account_id.id)]) + move_line = move_line_obj.browse(cr, uid, move_lines[0]) + assert (move_line.amount_residual_currency == 0.0 and move_line.amount_residual == 0.0 and invoice_id.state == 'paid') , "Residual amount is not correct for second Invoice" \ No newline at end of file diff --git a/addons/account_voucher/test/case2_suppl_usd_eur.yml b/addons/account_voucher/test/case2_suppl_usd_eur.yml new file mode 100644 index 00000000000..31e756d5355 --- /dev/null +++ b/addons/account_voucher/test/case2_suppl_usd_eur.yml @@ -0,0 +1,328 @@ +- + In order to check the Account_voucher module with multi-currency in OpenERP, + I create 2 Invoices in USD and make 2 Payments one in USD and another in EUR, based on the currency rating on that particular date +- + I create a bank journal with EUR as currency +- + !record {model: account.journal, id: bank_journal_EUR}: + name: Bank Journal(EUR) + code: BEUR + type: bank + analytic_journal_id: account.sit + sequence_id: account.sequence_bank_journal + default_debit_account_id: account.cash + default_credit_account_id: account.cash + currency: base.EUR + company_id: base.main_company + view_id: account.account_journal_bank_view +- + I create a bank journal with USD as currency +- + !record {model: account.journal, id: bank_journal_USD}: + name: Bank Journal(USD) + code: BUSD + type: bank + analytic_journal_id: account.sit + sequence_id: account.sequence_bank_journal + default_debit_account_id: account.cash + default_credit_account_id: account.cash + currency: base.USD + company_id: base.main_company + view_id: account.account_journal_bank_view +- + I create a new partner Robert Clements +- + !record {model: res.partner, id: res_partner_clements0}: + address: + - city: marseille + country_id: base.fr + name: Robert + street: 1 rue Rockfeller + type: invoice + zip: '13016' + name: Mr.Robert Clements +- + I create the first invoice on 1st January for 200 USD +- + !record {model: account.invoice, id: account_first_invoice_jan_suppl}: + account_id: account.a_pay + type : in_invoice + address_contact_id: base.res_partner_address_3000 + address_invoice_id: base.res_partner_address_3000 + company_id: base.main_company + currency_id: base.USD + date_invoice: !eval "'%s-01-01' %(datetime.now().year)" + period_id: account.period_1 + invoice_line: + - account_id: account.a_expense + name: '[PC1] Basic PC' + price_unit: 200.0 + quantity: 1.0 + product_id: product.product_product_pc1 + uos_id: product.product_uom_unit + journal_id: account.sales_journal + partner_id: res_partner_clements0 + reference_type: none + check_total : 200 +- + I Validate invoice by clicking on Validate button +- + !workflow {model: account.invoice, action: invoice_open, ref: account_first_invoice_jan_suppl} +- + I check that first invoice move is correct for debtor account(debit - credit == -150) +- + !python {model: account.invoice}: | + invoice_id = self.browse(cr, uid, ref("account_first_invoice_jan_suppl")) + assert invoice_id.move_id, "Move not created for open invoice" + move_line_obj = self.pool.get('account.move.line') + move_lines = move_line_obj.search(cr, uid, [('move_id', '=', invoice_id.move_id.id), ('account_id', '=', invoice_id.account_id.id)]) + move_line = move_line_obj.browse(cr, uid, move_lines[0]) + assert (move_line.debit - move_line.credit == -150.00), "Invoice move is incorrect for debtors account" +- + I create the second invoice on 1st February for 100 USD +- + !record {model: account.invoice, id: account_second_invoice_feb_suppl}: + account_id: account.a_pay + address_contact_id: base.res_partner_address_3000 + address_invoice_id: base.res_partner_address_3000 + company_id: base.main_company + currency_id: base.USD + date_invoice: !eval "'%s-02-01' %(datetime.now().year)" + period_id: account.period_2 + type : in_invoice + invoice_line: + - account_id: account.a_expense + name: '[PC1] Basic PC' + price_unit: 100.0 + quantity: 1.0 + product_id: product.product_product_pc1 + uos_id: product.product_uom_unit + journal_id: account.sales_journal + partner_id: res_partner_clements0 + reference_type: none + check_total : 100.0 +- + I Validate invoice by clicking on Validate button +- + !workflow {model: account.invoice, action: invoice_open, ref: account_second_invoice_feb_suppl} +- + I check that second invoice move is correct for debtor account (debit - credit == -80) +- + !python {model: account.invoice}: | + invoice_id = self.browse(cr, uid, ref("account_second_invoice_feb_suppl")) + assert invoice_id.move_id, "Move not created for open invoice" + move_line_obj = self.pool.get('account.move.line') + move_lines = move_line_obj.search(cr, uid, [('move_id', '=', invoice_id.move_id.id), ('account_id', '=', invoice_id.account_id.id)]) + move_line = move_line_obj.browse(cr, uid, move_lines[0]) + assert (move_line.debit - move_line.credit == -80), "Invoice move is incorrect for debtors account" +- + I create the first voucher of payment + +- + !python {model: account.voucher}: | + import netsvc, time + vals = {} + res = self.onchange_partner_id(cr, uid, [], ref("res_partner_clements0"), ref('bank_journal_EUR'), 240.0, 2, ttype='payment', date=False) + vals = { + 'account_id': ref('account.cash'), + 'amount': 240.0, + 'company_id': ref('base.main_company'), + 'currency_id': ref('base.EUR'), + 'journal_id': ref('bank_journal_EUR'), + 'partner_id': ref('res_partner_clements0'), + 'period_id': ref('account.period_3'), + 'type': 'payment', + 'date': time.strftime("%Y-03-01"), + 'payment_option': 'with_writeoff', + 'writeoff_acc_id': ref('account.a_expense'), + 'comment': 'Write Off', + 'name': 'First payment', + } + if not res['value']['line_dr_ids']: + res['value']['line_dr_ids'] = [{'type': 'dr', 'account_id': ref('account.a_pay'),}] + for item in res['value']['line_dr_ids']: + if item['amount_unreconciled'] == 200.00: + item['amount'] = 180.00 + else: + item['amount'] = 70.00 + vals['line_dr_ids'] = [(0,0,i) for i in res['value']['line_dr_ids']] + id = self.create(cr, uid, vals) + voucher_id = self.browse(cr, uid, id) + assert (voucher_id.state=='draft'), "Voucher is not in draft state" +- + I check that writeoff amount computed is -15.0 +- + !python {model: account.voucher}: | + voucher = self.search(cr, uid, [('name', '=', 'First payment'), ('partner_id', '=', ref('res_partner_clements0'))]) + voucher_id = self.browse(cr, uid, voucher[0]) + assert (voucher_id.writeoff_amount == -15.0), "Writeoff amount is not -15.0" +- + I check that currency rate difference is 34.0 +- + !python {model: account.voucher}: | + voucher = self.search(cr, uid, [('name', '=', 'First payment'), ('partner_id', '=', ref('res_partner_clements0'))]) + voucher_id = self.browse(cr, uid, voucher[0]) + assert (voucher_id.currency_rate_difference == 34.0), "Currency rate difference is not 34.0" +- + I confirm the voucher +- + !python {model: account.voucher}: | + import netsvc + voucher = self.search(cr, uid, [('name', '=', 'First payment'), ('partner_id', '=', ref('res_partner_clements0'))]) + wf_service = netsvc.LocalService("workflow") + wf_service.trg_validate(uid, 'account.voucher', voucher[0], 'proforma_voucher', cr) +- + I check that the move of my voucher is valid +- + !python {model: account.voucher}: | + voucher = self.search(cr, uid, [('name', '=', 'First payment'), ('partner_id', '=', ref('res_partner_clements0'))]) + voucher_id = self.browse(cr, uid, voucher[0]) + move_line_obj = self.pool.get('account.move.line') + move_lines = move_line_obj.search(cr, uid, [('move_id', '=', voucher_id.move_id.id)]) + for move_line in move_line_obj.browse(cr, uid, move_lines): + assert move_line.state == 'valid', "Voucher move is not valid" +- + I check that my creditor account is correct +- + I check that the creditor account has 2 new lines with 180 and 70 in amount_currency columns and their debit columns are respectively 135 and 56 and currency is USD($). +- + I check that my currency rate difference is correct. -34 in credit with no amount_currency +- + I check that my writeoff is correct. -15 in credit with no amount_currency +- + !python {model: account.voucher}: | + voucher = self.search(cr, uid, [('name', '=', 'First payment'), ('partner_id', '=', ref('res_partner_clements0'))]) + voucher_id = self.browse(cr, uid, voucher[0]) + move_line_obj = self.pool.get('account.move.line') + move_lines = move_line_obj.search(cr, uid, [('move_id', '=', voucher_id.move_id.id)]) + for move_line in move_line_obj.browse(cr, uid, move_lines): + if move_line.amount_currency == 180.00: + assert move_line.debit == 135.00, "Creditor account has wrong entry." + elif move_line.amount_currency == 70.00: + assert move_line.debit == 56.00, "Debtor account has wrong entry." + elif move_line.debit == 34.00: + assert move_line.amount_currency == 0.00, "Incorrect Currency Difference." + elif move_line.debit == 15.00: + assert move_line.amount_currency == 0.00, "Writeoff amount is wrong." +- + I check the residual amount of Invoice1, should be 20 in residual currency and 15 in amount_residual +- + !python {model: account.invoice}: | + invoice_id = self.browse(cr, uid, ref("account_first_invoice_jan_suppl")) + move_line_obj = self.pool.get('account.move.line') + move_lines = move_line_obj.search(cr, uid, [('move_id', '=', invoice_id.move_id.id), ('invoice', '=', invoice_id.id), ('account_id', '=', invoice_id.account_id.id)]) + move_line = move_line_obj.browse(cr, uid, move_lines[0]) + assert (move_line.amount_residual_currency == 20.0 and move_line.amount_residual == 15) , "Residual amount is not correct for first Invoice" +- + I check the residual amuont of Invoice2, should be 30 in residual currency and 24 in amount_residual +- + !python {model: account.invoice}: | + invoice_id = self.browse(cr, uid, ref("account_second_invoice_feb_suppl")) + move_line_obj = self.pool.get('account.move.line') + move_lines = move_line_obj.search(cr, uid, [('move_id', '=', invoice_id.move_id.id), ('invoice', '=', invoice_id.id), ('account_id', '=', invoice_id.account_id.id)]) + move_line = move_line_obj.browse(cr, uid, move_lines[0]) + assert (move_line.amount_residual_currency == 30 and move_line.amount_residual == 24) , "Residual amount is not correct for second Invoice" +- + I create the second voucher of payment + +- + !python {model: account.voucher}: | + import netsvc, time + vals = {} + res = self.onchange_partner_id(cr, uid, [], ref("res_partner_clements0"), ref('bank_journal_EUR'), 45.0, 2, ttype='payment', date=False) + vals = { + 'account_id': ref('account.cash'), + 'amount': 45.0, + 'company_id': ref('base.main_company'), + 'currency_id': ref('base.USD'), + 'journal_id': ref('bank_journal_USD'), + 'partner_id': ref('res_partner_clements0'), + 'period_id': ref('account.period_3'), + 'type': 'payment', + 'date': time.strftime("%Y-04-01"), + 'payment_option': 'with_writeoff', + 'writeoff_acc_id': ref('account.a_expense'), + 'comment': 'Write Off', + 'name': 'Second payment', + } + if not res['value']['line_dr_ids']: + res['value']['line_dr_ids'] = [{'type': 'dr', 'account_id': ref('account.a_pay'),}] + for item in res['value']['line_dr_ids']: + if item['amount_unreconciled'] == 20.00: + item['amount'] = 20.00 + else: + item['amount'] = 30.00 + vals['line_dr_ids'] = [(0,0,i) for i in res['value']['line_dr_ids']] + id = self.create(cr, uid, vals) + voucher_id = self.browse(cr, uid, id) + assert (voucher_id.state=='draft'), "Voucher is not in draft state" +- + I check that writeoff amount computed is -5.0 +- + !python {model: account.voucher}: | + voucher = self.search(cr, uid, [('name', '=', 'Second payment'), ('partner_id', '=', ref('res_partner_clements0'))]) + voucher_id = self.browse(cr, uid, voucher[0]) + assert (voucher_id.writeoff_amount == 5.0), "Writeoff amount is not 5.0" +- + I check that currency rate difference is 8.50 +- + !python {model: account.voucher}: | + voucher = self.search(cr, uid, [('name', '=', 'Second payment'), ('partner_id', '=', ref('res_partner_clements0'))]) + voucher_id = self.browse(cr, uid, voucher[0]) + assert (voucher_id.currency_rate_difference == 8.50), "Currency rate difference is not 8.50" +- + I confirm the voucher +- + !python {model: account.voucher}: | + import netsvc + voucher = self.search(cr, uid, [('name', '=', 'Second payment'), ('partner_id', '=', ref('res_partner_clements0'))]) + wf_service = netsvc.LocalService("workflow") + wf_service.trg_validate(uid, 'account.voucher', voucher[0], 'proforma_voucher', cr) +- + I check that my voucher state is posted +- + !python {model: account.voucher}: | + voucher = self.search(cr, uid, [('name', '=', 'Second payment'), ('partner_id', '=', ref('res_partner_clements0'))]) + voucher_id = self.browse(cr, uid, voucher[0]) + assert voucher_id.state == 'posted', "Voucher state is not posted" +- + I check that my creditor account is correct +- + I check that the creditor account has 2 new lines with 20 and 30 in amount_currency columns and their debit columns are respectively 15 and 24 and currency is USD($). +- + I check that my currency rate difference is correct. 8.50 in debit with no amount_currency +- + I check that my writeoff is correct. 4.75 in credit and 5 in amount_currency +- + !python {model: account.voucher}: | + voucher = self.search(cr, uid, [('name', '=', 'Second payment'), ('partner_id', '=', ref('res_partner_clements0'))]) + voucher_id = self.browse(cr, uid, voucher[0]) + move_line_obj = self.pool.get('account.move.line') + move_lines = move_line_obj.search(cr, uid, [('move_id', '=', voucher_id.move_id.id)]) + for move_line in move_line_obj.browse(cr, uid, move_lines): + if move_line.amount_currency == 20.00: + assert move_line.debit == 15.00, "Debtor account has wrong entry." + elif move_line.amount_currency == 30.00: + assert move_line.debit == 24.00, "Debtor account has wrong entry." + elif move_line.debit == 8.50: + assert move_line.amount_currency == 0.00, "Incorrect Currency Difference." + elif move_line.amount_currency == -5.00: + assert move_line.credit == 4.75, "Writeoff amount is wrong." +- + I check the residual amount of invoice 1, should be 0 in residual currency and 0 in amount_residual and paid +- + !python {model: account.invoice}: | + invoice_id = self.browse(cr, uid, ref("account_first_invoice_jan_suppl")) + move_line_obj = self.pool.get('account.move.line') + move_lines = move_line_obj.search(cr, uid, [('move_id', '=', invoice_id.move_id.id), ('invoice', '=', invoice_id.id), ('account_id', '=', invoice_id.account_id.id)]) + move_line = move_line_obj.browse(cr, uid, move_lines[0]) + assert (move_line.amount_residual_currency == 0.0 and move_line.amount_residual == 0.0 and invoice_id.state == 'paid') , "Residual amount is not correct for first Invoice" +- + I check the residual amount of invoice 2, should be 0 in residual currency and 0 in amount_residual and paid +- + !python {model: account.invoice}: | + invoice_id = self.browse(cr, uid, ref("account_second_invoice_feb_suppl")) + move_line_obj = self.pool.get('account.move.line') + move_lines = move_line_obj.search(cr, uid, [('move_id', '=', invoice_id.move_id.id), ('invoice', '=', invoice_id.id), ('account_id', '=', invoice_id.account_id.id)]) + move_line = move_line_obj.browse(cr, uid, move_lines[0]) + assert (move_line.amount_residual_currency == 0.0 and move_line.amount_residual == 0.0 and invoice_id.state == 'paid') , "Residual amount is not correct for second Invoice" diff --git a/addons/account_voucher/test/case2_usd_eur.yml b/addons/account_voucher/test/case2_usd_eur.yml new file mode 100644 index 00000000000..ded95095e5a --- /dev/null +++ b/addons/account_voucher/test/case2_usd_eur.yml @@ -0,0 +1,360 @@ +- + In order to check the Account_voucher module with multi-currency in OpenERP, + I create 2 Invoices in USD and make 2 Payments one in USD and another in EUR, based on the currency rating on that particular date +- + I create currency USD in OpenERP for January of 1.333333 Rate +- + !python {model: res.currency.rate}: | + from datetime import datetime + curr_id = self.pool.get('res.currency').search(cr, uid, [('name', '=', 'USD')]) + date = '%s-01-01' %(datetime.now().year) + ids = self.search(cr, uid, [('currency_id', '=', curr_id), ('name', '=', date)]) + self.write(cr, uid, ids, {'rate': 1.333333}) +- + I create currency USD in OpenERP for February of 1.250000 Rate +- + !record {model: res.currency.rate, id: febr_usd}: + currency_id: base.USD + name: !eval "'%s-02-01' %(datetime.now().year)" + rate: 1.250000 +- + I create currency USD in OpenERP for March of 1.111111 Rate +- + !record {model: res.currency.rate, id: marc_usd}: + currency_id: base.USD + name: !eval "'%s-03-01' %(datetime.now().year)" + rate: 1.111111 +- + I create currency USD in OpenERP for April of 1.052632 Rate +- + !record {model: res.currency.rate, id: apri_usd}: + currency_id: base.USD + name: !eval "'%s-04-01' %(datetime.now().year)" + rate: 1.052632 +- + I create a bank journal with EUR as currency +- + !record {model: account.journal, id: bank_journal_EUR}: + name: Bank Journal(EUR) + code: BEUR + type: bank + analytic_journal_id: account.sit + sequence_id: account.sequence_bank_journal + default_debit_account_id: account.cash + default_credit_account_id: account.cash + currency: base.EUR + company_id: base.main_company + view_id: account.account_journal_bank_view +- + I create a bank journal with USD as currency +- + !record {model: account.journal, id: bank_journal_USD}: + name: Bank Journal(USD) + code: BUSD + type: bank + analytic_journal_id: account.sit + sequence_id: account.sequence_bank_journal + default_debit_account_id: account.cash + default_credit_account_id: account.cash + currency: base.USD + company_id: base.main_company + view_id: account.account_journal_bank_view +- + I set up some accounts for currency rate expense/income in my company +- + !record {model: res.company, id: base.main_company}: + property_expense_currency_exchange: account.o_expense + property_income_currency_exchange: account.o_income +- + I create a new partner Michael Geller +- + !record {model: res.partner, id: res_partner_michael0}: + address: + - city: paris + country_id: base.fr + name: Michael + street: 1 rue Rockfeller + type: invoice + zip: '75016' + name: Mr.Michael Geller +- + I create the first invoice on 1st January for 200 USD +- + !record {model: account.invoice, id: account_first_invoice_jan}: + account_id: account.a_recv + address_contact_id: base.res_partner_address_3000 + address_invoice_id: base.res_partner_address_3000 + company_id: base.main_company + currency_id: base.USD + date_invoice: !eval "'%s-01-01' %(datetime.now().year)" + period_id: account.period_1 + invoice_line: + - account_id: account.a_sale + name: '[PC1] Basic PC' + price_unit: 200.0 + quantity: 1.0 + product_id: product.product_product_pc1 + uos_id: product.product_uom_unit + journal_id: account.sales_journal + partner_id: res_partner_michael0 + reference_type: none +- + I Validate invoice by clicking on Validate button +- + !workflow {model: account.invoice, action: invoice_open, ref: account_first_invoice_jan} +- + I check that first invoice move is correct for debtor account(debit - credit == 150) +- + !python {model: account.invoice}: | + invoice_id = self.browse(cr, uid, ref("account_first_invoice_jan")) + assert invoice_id.move_id, "Move not created for open invoice" + move_line_obj = self.pool.get('account.move.line') + move_lines = move_line_obj.search(cr, uid, [('move_id', '=', invoice_id.move_id.id), ('account_id', '=', invoice_id.account_id.id)]) + move_line = move_line_obj.browse(cr, uid, move_lines[0]) + assert (move_line.debit - move_line.credit == 150.00), "Invoice move is incorrect for debtors account" +- + I create the second invoice on 1st February for 100 USD +- + !record {model: account.invoice, id: account_second_invoice_feb}: + account_id: account.a_recv + address_contact_id: base.res_partner_address_3000 + address_invoice_id: base.res_partner_address_3000 + company_id: base.main_company + currency_id: base.USD + date_invoice: !eval "'%s-02-01' %(datetime.now().year)" + period_id: account.period_2 + invoice_line: + - account_id: account.a_sale + name: '[PC1] Basic PC' + price_unit: 100.0 + quantity: 1.0 + product_id: product.product_product_pc1 + uos_id: product.product_uom_unit + journal_id: account.sales_journal + partner_id: res_partner_michael0 + reference_type: none +- + I Validate invoice by clicking on Validate button +- + !workflow {model: account.invoice, action: invoice_open, ref: account_second_invoice_feb} +- + I check that second invoice move is correct for debtor account (debit - credit == 80) +- + !python {model: account.invoice}: | + invoice_id = self.browse(cr, uid, ref("account_second_invoice_feb")) + assert invoice_id.move_id, "Move not created for open invoice" + move_line_obj = self.pool.get('account.move.line') + move_lines = move_line_obj.search(cr, uid, [('move_id', '=', invoice_id.move_id.id), ('account_id', '=', invoice_id.account_id.id)]) + move_line = move_line_obj.browse(cr, uid, move_lines[0]) + assert (move_line.debit - move_line.credit == 80), "Invoice move is incorrect for debtors account" +- + I create the first voucher of payment + +- + !python {model: account.voucher}: | + import netsvc, time + vals = {} + res = self.onchange_partner_id(cr, uid, [], ref("res_partner_michael0"), ref('bank_journal_EUR'), 240.0, 2, ttype='receipt', date=False) + vals = { + 'account_id': ref('account.cash'), + 'amount': 240.0, + 'company_id': ref('base.main_company'), + 'currency_id': ref('base.EUR'), + 'journal_id': ref('bank_journal_EUR'), + 'partner_id': ref('res_partner_michael0'), + 'period_id': ref('account.period_3'), + 'type': 'receipt', + 'date': time.strftime("%Y-03-01"), + 'payment_option': 'with_writeoff', + 'writeoff_acc_id': ref('account.a_expense'), + 'comment': 'Write Off', + 'name': 'First payment', + } + if not res['value']['line_cr_ids']: + res['value']['line_cr_ids'] = [{'type': 'cr', 'account_id': ref('account.a_recv'),}] + for item in res['value']['line_cr_ids']: + if item['amount_unreconciled'] == 200.00: + item['amount'] = 180.00 + else: + item['amount'] = 70.00 + vals['line_cr_ids'] = [(0,0,i) for i in res['value']['line_cr_ids']] + id = self.create(cr, uid, vals) + voucher_id = self.browse(cr, uid, id) + assert (voucher_id.state=='draft'), "Voucher is not in draft state" +- + I check that writeoff amount computed is 15.0 +- + !python {model: account.voucher}: | + voucher = self.search(cr, uid, [('name', '=', 'First payment'), ('partner_id', '=', ref('res_partner_michael0'))]) + voucher_id = self.browse(cr, uid, voucher[0]) + assert (voucher_id.writeoff_amount == 15.0), "Writeoff amount is not 15.0" +- + I check that currency rate difference is -34.0 +- + !python {model: account.voucher}: | + voucher = self.search(cr, uid, [('name', '=', 'First payment'), ('partner_id', '=', ref('res_partner_michael0'))]) + voucher_id = self.browse(cr, uid, voucher[0]) + assert (voucher_id.currency_rate_difference == -34.0), "Currency rate difference is not -34.0" +- + I confirm the voucher +- + !python {model: account.voucher}: | + import netsvc + voucher = self.search(cr, uid, [('name', '=', 'First payment'), ('partner_id', '=', ref('res_partner_michael0'))]) + wf_service = netsvc.LocalService("workflow") + wf_service.trg_validate(uid, 'account.voucher', voucher[0], 'proforma_voucher', cr) +- + I check that the move of my voucher is valid +- + !python {model: account.voucher}: | + voucher = self.search(cr, uid, [('name', '=', 'First payment'), ('partner_id', '=', ref('res_partner_michael0'))]) + voucher_id = self.browse(cr, uid, voucher[0]) + move_line_obj = self.pool.get('account.move.line') + move_lines = move_line_obj.search(cr, uid, [('move_id', '=', voucher_id.move_id.id)]) + for move_line in move_line_obj.browse(cr, uid, move_lines): + assert move_line.state == 'valid', "Voucher move is not valid" +- + I check that my debtor account is correct +- + I check that the debtor account has 2 new lines with -180 and -70 in amount_currency columns and their credit columns are respectively 135 and 56 and currency is USD($). +- + I check that my currency rate difference is correct. 34 in credit with no amount_currency +- + I check that my writeoff is correct. 15 in credit with no amount_currency +- + !python {model: account.voucher}: | + voucher = self.search(cr, uid, [('name', '=', 'First payment'), ('partner_id', '=', ref('res_partner_michael0'))]) + voucher_id = self.browse(cr, uid, voucher[0]) + move_line_obj = self.pool.get('account.move.line') + move_lines = move_line_obj.search(cr, uid, [('move_id', '=', voucher_id.move_id.id)]) + for move_line in move_line_obj.browse(cr, uid, move_lines): + if move_line.amount_currency == -180.00: + assert move_line.credit == 135.00, "Debtor account has wrong entry." + elif move_line.amount_currency == -70.00: + assert move_line.credit == 56.00, "Debtor account has wrong entry." + elif move_line.credit == 34.00: + assert move_line.amount_currency == 0.00, "Incorrect Currency Difference." + elif move_line.credit == 15.00: + assert move_line.amount_currency == 0.00, "Writeoff amount is wrong." +- + I check the residual amount of Invoice1, should be 20 in residual currency and 15 in amount_residual +- + !python {model: account.invoice}: | + invoice_id = self.browse(cr, uid, ref("account_first_invoice_jan")) + move_line_obj = self.pool.get('account.move.line') + move_lines = move_line_obj.search(cr, uid, [('move_id', '=', invoice_id.move_id.id), ('invoice', '=', invoice_id.id), ('account_id', '=', invoice_id.account_id.id)]) + move_line = move_line_obj.browse(cr, uid, move_lines[0]) + assert (move_line.amount_residual_currency == 20.0 and move_line.amount_residual == 15) , "Residual amount is not correct for first Invoice" +- + I check the residual amuont of Invoice2, should be 30 in residual currency and 24 in amount_residual +- + !python {model: account.invoice}: | + invoice_id = self.browse(cr, uid, ref("account_second_invoice_feb")) + move_line_obj = self.pool.get('account.move.line') + move_lines = move_line_obj.search(cr, uid, [('move_id', '=', invoice_id.move_id.id), ('invoice', '=', invoice_id.id), ('account_id', '=', invoice_id.account_id.id)]) + move_line = move_line_obj.browse(cr, uid, move_lines[0]) + assert (move_line.amount_residual_currency == 30 and move_line.amount_residual == 24) , "Residual amount is not correct for second Invoice" +- + I create the second voucher of payment + +- + !python {model: account.voucher}: | + import netsvc, time + vals = {} + res = self.onchange_partner_id(cr, uid, [], ref("res_partner_michael0"), ref('bank_journal_EUR'), 45.0, 2, ttype='receipt', date=False) + vals = { + 'account_id': ref('account.cash'), + 'amount': 45.0, + 'company_id': ref('base.main_company'), + 'currency_id': ref('base.USD'), + 'journal_id': ref('bank_journal_USD'), + 'partner_id': ref('res_partner_michael0'), + 'period_id': ref('account.period_3'), + 'type': 'receipt', + 'date': time.strftime("%Y-04-01"), + 'payment_option': 'with_writeoff', + 'writeoff_acc_id': ref('account.a_expense'), + 'comment': 'Write Off', + 'name': 'Second payment', + } + if not res['value']['line_cr_ids']: + res['value']['line_cr_ids'] = [{'type': 'cr', 'account_id': ref('account.a_recv'),}] + for item in res['value']['line_cr_ids']: + if item['amount_unreconciled'] == 20.00: + item['amount'] = 20.00 + else: + item['amount'] = 30.00 + vals['line_cr_ids'] = [(0,0,i) for i in res['value']['line_cr_ids']] + id = self.create(cr, uid, vals) + voucher_id = self.browse(cr, uid, id) + assert (voucher_id.state=='draft'), "Voucher is not in draft state" +- + I check that writeoff amount computed is -5.0 +- + !python {model: account.voucher}: | + voucher = self.search(cr, uid, [('name', '=', 'Second payment'), ('partner_id', '=', ref('res_partner_michael0'))]) + voucher_id = self.browse(cr, uid, voucher[0]) + assert (voucher_id.writeoff_amount == -5.0), "Writeoff amount is not -5.0" +- + I check that currency rate difference is -8.50 +- + !python {model: account.voucher}: | + voucher = self.search(cr, uid, [('name', '=', 'Second payment'), ('partner_id', '=', ref('res_partner_michael0'))]) + voucher_id = self.browse(cr, uid, voucher[0]) + assert (voucher_id.currency_rate_difference == -8.50), "Currency rate difference is not -8.50" +- + I confirm the voucher +- + !python {model: account.voucher}: | + import netsvc + voucher = self.search(cr, uid, [('name', '=', 'Second payment'), ('partner_id', '=', ref('res_partner_michael0'))]) + wf_service = netsvc.LocalService("workflow") + wf_service.trg_validate(uid, 'account.voucher', voucher[0], 'proforma_voucher', cr) +- + I check that my voucher state is posted +- + !python {model: account.voucher}: | + voucher = self.search(cr, uid, [('name', '=', 'Second payment'), ('partner_id', '=', ref('res_partner_michael0'))]) + voucher_id = self.browse(cr, uid, voucher[0]) + assert voucher_id.state == 'posted', "Voucher state is not posted" +- + I check that my debtor account is correct +- + I check that the debtor account has 2 new lines with -20 and -30 in amount_currency columns and their credit columns are respectively 15 and 24 and currency is USD($). +- + I check that my currency rate difference is correct. 8.50 in credit with no amount_currency +- + I check that my writeoff is correct. 4.75 in debit and 5 in amount_currency +- + !python {model: account.voucher}: | + voucher = self.search(cr, uid, [('name', '=', 'Second payment'), ('partner_id', '=', ref('res_partner_michael0'))]) + voucher_id = self.browse(cr, uid, voucher[0]) + move_line_obj = self.pool.get('account.move.line') + move_lines = move_line_obj.search(cr, uid, [('move_id', '=', voucher_id.move_id.id)]) + for move_line in move_line_obj.browse(cr, uid, move_lines): + if move_line.amount_currency == -20.00: + assert move_line.credit == 15.00, "Debtor account has wrong entry." + elif move_line.amount_currency == -30.00: + assert move_line.credit == 24.00, "Debtor account has wrong entry." + elif move_line.credit == 8.50: + assert move_line.amount_currency == 0.00, "Incorrect Currency Difference." + elif move_line.amount_currency == 5.00: + assert move_line.debit == 4.75, "Writeoff amount is wrong." +- + I check the residual amount of invoice 1, should be 0 in residual currency and 0 in amount_residual and paid +- + !python {model: account.invoice}: | + invoice_id = self.browse(cr, uid, ref("account_first_invoice_jan")) + move_line_obj = self.pool.get('account.move.line') + move_lines = move_line_obj.search(cr, uid, [('move_id', '=', invoice_id.move_id.id), ('invoice', '=', invoice_id.id), ('account_id', '=', invoice_id.account_id.id)]) + move_line = move_line_obj.browse(cr, uid, move_lines[0]) + assert (move_line.amount_residual_currency == 0.0 and move_line.amount_residual == 0.0 and invoice_id.state == 'paid') , "Residual amount is not correct for first Invoice" +- + I check the residual amuont of invoice 2, should be 0 in residual currency and 0 in amount_residual and paid +- + !python {model: account.invoice}: | + invoice_id = self.browse(cr, uid, ref("account_second_invoice_feb")) + move_line_obj = self.pool.get('account.move.line') + move_lines = move_line_obj.search(cr, uid, [('move_id', '=', invoice_id.move_id.id), ('invoice', '=', invoice_id.id), ('account_id', '=', invoice_id.account_id.id)]) + move_line = move_line_obj.browse(cr, uid, move_lines[0]) + assert (move_line.amount_residual_currency == 0.0 and move_line.amount_residual == 0.0 and invoice_id.state == 'paid') , "Residual amount is not correct for second Invoice" \ No newline at end of file diff --git a/addons/account_voucher/test/case3_eur_eur.yml b/addons/account_voucher/test/case3_eur_eur.yml new file mode 100644 index 00000000000..b35daf49402 --- /dev/null +++ b/addons/account_voucher/test/case3_eur_eur.yml @@ -0,0 +1,331 @@ +- + In order to check the Account_voucher module with multi-currency in OpenERP, + I create 2 Invoices in EUR and make 2 Payments in EUR based on the currency rating on that particular date. +- + I create currency EUR in OpenERP for January of 1.000000 Rate +- + !record {model: res.currency.rate, id: jan_eur}: + currency_id: base.EUR + name: !eval "'%s-01-01' %(datetime.now().year)" + rate: 1.000000 +- + I create currency EUR in OpenERP for February of 1.000000 Rate +- + !record {model: res.currency.rate, id: feb_eur}: + currency_id: base.EUR + name: !eval "'%s-02-01' %(datetime.now().year)" + rate: 1.000000 +- + I create currency EUR in OpenERP for March of 1.000000 Rate +- + !record {model: res.currency.rate, id: mar_eur}: + currency_id: base.EUR + name: !eval "'%s-03-01' %(datetime.now().year)" + rate: 1.000000 +- + I create currency EUR in OpenERP for April of 1.000000 Rate +- + !record {model: res.currency.rate, id: apr_eur}: + currency_id: base.EUR + name: !eval "'%s-04-01' %(datetime.now().year)" + rate: 1.000000 +- + I create a bank journal with EUR as currency +- + !record {model: account.journal, id: bank_journal_EUR}: + name: Bank Journal(EUR) + code: BEUR + type: bank + analytic_journal_id: account.sit + sequence_id: account.sequence_bank_journal + default_debit_account_id: account.cash + default_credit_account_id: account.cash + currency: base.EUR + company_id: base.main_company + view_id: account.account_journal_bank_view +- + I set up some accounts for currency rate expense/income in my company +- + !record {model: res.company, id: base.main_company}: + property_expense_currency_exchange: account.o_expense + property_income_currency_exchange: account.o_income +- + I create a new partner Peter Lawson. +- + !record {model: res.partner, id: res_partner_peter0}: + address: + - city: paris + country_id: base.fr + name: Peter + street: 1 rue Rockfeller + type: invoice + zip: '75016' + name: Mr.Peter Lawson +- + I create the first invoice on 1st January for 150 EUR +- + !record {model: account.invoice, id: account_first_invoice_jan_eur}: + account_id: account.a_recv + address_contact_id: base.res_partner_address_3000 + address_invoice_id: base.res_partner_address_3000 + company_id: base.main_company + currency_id: base.EUR + date_invoice: !eval "'%s-01-01' %(datetime.now().year)" + period_id: account.period_1 + invoice_line: + - account_id: account.a_sale + name: '[PC1] Basic PC' + price_unit: 150.0 + quantity: 1.0 + product_id: product.product_product_pc1 + uos_id: product.product_uom_unit + journal_id: account.sales_journal + partner_id: res_partner_peter0 + reference_type: none +- + I Validate invoice by clicking on Validate button +- + !workflow {model: account.invoice, action: invoice_open, ref: account_first_invoice_jan_eur} +- + I check that first invoice move is correct for debtor account(debit - credit == 150) +- + !python {model: account.invoice}: | + invoice_id = self.browse(cr, uid, ref("account_first_invoice_jan_eur")) + assert invoice_id.move_id, "Move not created for open invoice" + move_line_obj = self.pool.get('account.move.line') + move_lines = move_line_obj.search(cr, uid, [('move_id', '=', invoice_id.move_id.id), ('account_id', '=', invoice_id.account_id.id)]) + move_line = move_line_obj.browse(cr, uid, move_lines[0]) + assert (move_line.debit - move_line.credit == 150.00), "Invoice move is incorrect for debtors account" +- + I create the second invoice on 1st February for 80 EUR +- + !record {model: account.invoice, id: account_second_invoice_feb_eur}: + account_id: account.a_recv + address_contact_id: base.res_partner_address_3000 + address_invoice_id: base.res_partner_address_3000 + company_id: base.main_company + currency_id: base.EUR + date_invoice: !eval "'%s-02-01' %(datetime.now().year)" + period_id: account.period_1 + invoice_line: + - account_id: account.a_sale + name: '[PC1] Basic PC' + price_unit: 80.0 + quantity: 1.0 + product_id: product.product_product_pc1 + uos_id: product.product_uom_unit + journal_id: account.sales_journal + partner_id: res_partner_peter0 + reference_type: none +- + I Validate invoice by clicking on Validate button +- + !workflow {model: account.invoice, action: invoice_open, ref: account_second_invoice_feb_eur} +- + I check that second invoice move is correct for debtor account (debit - credit == 80) +- + !python {model: account.invoice}: | + invoice_id = self.browse(cr, uid, ref("account_second_invoice_feb_eur")) + assert invoice_id.move_id, "Move not created for open invoice" + move_line_obj = self.pool.get('account.move.line') + move_lines = move_line_obj.search(cr, uid, [('move_id', '=', invoice_id.move_id.id), ('account_id', '=', invoice_id.account_id.id)]) + move_line = move_line_obj.browse(cr, uid, move_lines[0]) + assert (move_line.debit - move_line.credit == 80.00), "Invoice move is incorrect for debtors account" +- + I create the first voucher of payment + +- + !python {model: account.voucher}: | + import netsvc, time + vals = {} + res = self.onchange_partner_id(cr, uid, [], ref("res_partner_peter0"), ref('bank_journal_EUR'), 120.00, 2, ttype='receipt', date=False) + vals = { + 'account_id': ref('account.cash'), + 'amount': 120.00, + 'company_id': ref('base.main_company'), + 'currency_id': ref('base.EUR'), + 'journal_id': ref('bank_journal_EUR'), + 'partner_id': ref('res_partner_peter0'), + 'period_id': ref('account.period_3'), + 'type': 'receipt', + 'date': time.strftime("%Y-03-01"), + 'payment_option': 'with_writeoff', + 'writeoff_acc_id': ref('account.a_expense'), + 'comment': 'Write Off', + 'name': 'First payment', + } + if not res['value']['line_cr_ids']: + res['value']['line_cr_ids'] = [{'type': 'cr', 'account_id': ref('account.a_recv'),}] + for item in res['value']['line_cr_ids']: + if item['amount_unreconciled'] == 150.00: + item['amount'] = 100.00 + else: + item['amount'] = 20.00 + vals['line_cr_ids'] = [(0,0,i) for i in res['value']['line_cr_ids']] + id = self.create(cr, uid, vals) + voucher_id = self.browse(cr, uid, id) + assert (voucher_id.state=='draft'), "Voucher is not in draft state" +- + I check that writeoff amount computed is 0.00 +- + !python {model: account.voucher}: | + voucher = self.search(cr, uid, [('name', '=', 'First payment'),('partner_id', '=', ref('res_partner_peter0'))]) + voucher_id = self.browse(cr, uid, voucher[0]) + assert (voucher_id.writeoff_amount == 0.00), "Writeoff amount is not 0.00" +- + I check that currency rate difference is 0.00 +- + !python {model: account.voucher}: | + voucher = self.search(cr, uid, [('name', '=', 'First payment'),('partner_id', '=', ref('res_partner_peter0'))]) + voucher_id = self.browse(cr, uid, voucher[0]) + assert (voucher_id.currency_rate_difference == 0.00), "Currency rate difference is not 0.00" +- + I confirm the voucher +- + !python {model: account.voucher}: | + import netsvc + voucher = self.search(cr, uid, [('name', '=', 'First payment'),('partner_id', '=', ref('res_partner_peter0'))]) + wf_service = netsvc.LocalService("workflow") + wf_service.trg_validate(uid, 'account.voucher', voucher[0], 'proforma_voucher', cr) +- + I check that the move of my first voucher is valid +- + !python {model: account.voucher}: | + voucher = self.search(cr, uid, [('name', '=', 'First payment'),('partner_id', '=', ref('res_partner_peter0'))]) + voucher_id = self.browse(cr, uid, voucher[0]) + move_line_obj = self.pool.get('account.move.line') + move_lines = move_line_obj.search(cr, uid, [('move_id', '=', voucher_id.move_id.id)]) + for move_line in move_line_obj.browse(cr, uid, move_lines): + assert move_line.state == 'valid', "Voucher move is not valid" +- + I check that my debtor account is correct +- + I check that the debtor account has 2 new lines with 0.00 and 0.00 in amount_currency columns and their credit are 20 and 100 respectively +- + !python {model: account.voucher}: | + voucher = self.search(cr, uid, [('name', '=', 'First payment'),('partner_id', '=', ref('res_partner_peter0'))]) + voucher_id = self.browse(cr, uid, voucher[0]) + move_line_obj = self.pool.get('account.move.line') + move_lines = move_line_obj.search(cr, uid, [('move_id', '=', voucher_id.move_id.id)]) + for move_line in move_line_obj.browse(cr, uid, move_lines): + if move_line.credit == 20.00: + assert move_line.amount_currency == 0.00, "Debtor account has wrong entry." + elif move_line.credit == 100.00: + assert move_line.amount_currency == 0.00, "Debtor account has wrong entry." +- + I check the residual amount of Invoice1 is 50 +- + !python {model: account.invoice}: | + invoice_id = self.browse(cr, uid, ref("account_first_invoice_jan_eur")) + move_line_obj = self.pool.get('account.move.line') + move_lines = move_line_obj.search(cr, uid, [('move_id', '=', invoice_id.move_id.id), ('invoice', '=', invoice_id.id), ('account_id', '=', invoice_id.account_id.id)]) + move_line = move_line_obj.browse(cr, uid, move_lines[0]) + assert (move_line.amount_residual_currency == 50.0 and move_line.amount_residual == 50.0) , "Residual amount is not correct for first Invoice" +- + I check the residual amuont of Invoice2 is 60 +- + !python {model: account.invoice}: | + invoice_id = self.browse(cr, uid, ref("account_second_invoice_feb_eur")) + move_line_obj = self.pool.get('account.move.line') + move_lines = move_line_obj.search(cr, uid, [('move_id', '=', invoice_id.move_id.id), ('invoice', '=', invoice_id.id), ('account_id', '=', invoice_id.account_id.id)]) + move_line = move_line_obj.browse(cr, uid, move_lines[0]) + assert (move_line.amount_residual_currency == 60.0 and move_line.amount_residual == 60.0) , "Residual amount is not correct for second Invoice" +- + I create the second voucher of payment and check to let open the debtor overpaid amount. + +- + !python {model: account.voucher}: | + import netsvc, time + vals = {} + res = self.onchange_partner_id(cr, uid, [], ref("res_partner_peter0"), ref('bank_journal_EUR'), 120.00, 2, ttype='receipt', date=False) + vals = { + 'account_id': ref('account.cash'), + 'amount': 120.00, + 'company_id': ref('base.main_company'), + 'currency_id': ref('base.EUR'), + 'journal_id': ref('bank_journal_EUR'), + 'partner_id': ref('res_partner_peter0'), + 'period_id': ref('account.period_3'), + 'type': 'receipt', + 'date': time.strftime("%Y-04-01"), + 'payment_option': 'with_writeoff', + 'writeoff_acc_id': ref('account.a_expense'), + 'comment': 'Write Off', + 'name': 'Second payment', + } + if not res['value']['line_cr_ids']: + res['value']['line_cr_ids'] = [{'type': 'cr', 'account_id': ref('account.a_recv'),}] + for item in res['value']['line_cr_ids']: + if item['amount_unreconciled'] == 50.00: + item['amount'] = 50.00 + elif item['amount_unreconciled'] == 60.00: + item['amount'] = 70.00 + vals['line_cr_ids'] = [(0,0,i) for i in res['value']['line_cr_ids']] + id = self.create(cr, uid, vals) + voucher_id = self.browse(cr, uid, id) + assert (voucher_id.state=='draft'), "Voucher is not in draft state" +- + I check that writeoff amount computed is 0.00 +- + !python {model: account.voucher}: | + voucher = self.search(cr, uid, [('name', '=', 'Second payment'),('partner_id', '=', ref('res_partner_peter0'))]) + voucher_id = self.browse(cr, uid, voucher[0]) + assert (voucher_id.writeoff_amount == 0.00), "Writeoff amount is not 0" +- + I check that currency rate difference is 0.00 +- + !python {model: account.voucher}: | + voucher = self.search(cr, uid, [('name', '=', 'Second payment'), ('partner_id', '=', ref('res_partner_peter0'))]) + voucher_id = self.browse(cr, uid, voucher[0]) + assert (voucher_id.currency_rate_difference == 0.00), "Currency rate difference is not 0" +- + I confirm the voucher +- + !python {model: account.voucher}: | + import netsvc + voucher = self.search(cr, uid, [('name', '=', 'Second payment'), ('partner_id', '=', ref('res_partner_peter0'))]) + wf_service = netsvc.LocalService("workflow") + wf_service.trg_validate(uid, 'account.voucher', voucher[0], 'proforma_voucher', cr) +- + I check that the move of my second voucher is valid +- + !python {model: account.voucher}: | + voucher = self.search(cr, uid, [('name', '=', 'Second payment'), ('partner_id', '=', ref('res_partner_peter0'))]) + voucher_id = self.browse(cr, uid, voucher[0]) + move_line_obj = self.pool.get('account.move.line') + move_lines = move_line_obj.search(cr, uid, [('move_id', '=', voucher_id.move_id.id)]) + for move_line in move_line_obj.browse(cr, uid, move_lines): + assert move_line.state == 'valid', "Voucher move is not valid" +- + I check that my debtor account is correct +- + I check that the debtor account has 2 new lines with 0.00 and 0.00 in amount_currency columns and their credit are 70 and 50 +- + !python {model: account.voucher}: | + voucher = self.search(cr, uid, [('name', '=', 'Second payment'), ('partner_id', '=', ref('res_partner_peter0'))]) + voucher_id = self.browse(cr, uid, voucher[0]) + move_line_obj = self.pool.get('account.move.line') + move_lines = move_line_obj.search(cr, uid, [('move_id', '=', voucher_id.move_id.id)]) + for move_line in move_line_obj.browse(cr, uid, move_lines): + if move_line.credit == 70.00: + assert move_line.amount_currency == 0.00, "Debtor account has wrong entry." + elif move_line.credit == 50.00: + assert move_line.amount_currency == 0.00, "Debtor account has wrong entry." +- + I check the residual amount of Invoice1 is 0 +- + !python {model: account.invoice}: | + invoice_id = self.browse(cr, uid, ref("account_first_invoice_jan_eur")) + move_line_obj = self.pool.get('account.move.line') + move_lines = move_line_obj.search(cr, uid, [('move_id', '=', invoice_id.move_id.id), ('invoice', '=', invoice_id.id), ('account_id', '=', invoice_id.account_id.id)]) + move_line = move_line_obj.browse(cr, uid, move_lines[0]) + assert (move_line.amount_residual_currency == 0 and move_line.amount_residual == 0) , "Residual amount is not correct for first Invoice" +- + I check the residual amuont of Invoice2 is -10 +- + !python {model: account.invoice}: | + invoice_id = self.browse(cr, uid, ref("account_second_invoice_feb_eur")) + move_line_obj = self.pool.get('account.move.line') + move_lines = move_line_obj.search(cr, uid, [('move_id', '=', invoice_id.move_id.id), ('invoice', '=', invoice_id.id), ('account_id', '=', invoice_id.account_id.id)]) + move_line = move_line_obj.browse(cr, uid, move_lines[0]) + assert (move_line.amount_residual_currency == -10.0 and move_line.amount_residual == -10.0) , "Residual amount is not correct for second Invoice" \ No newline at end of file diff --git a/addons/account_voucher/test/case4_cad_chf.yml b/addons/account_voucher/test/case4_cad_chf.yml new file mode 100644 index 00000000000..b6ed790dffb --- /dev/null +++ b/addons/account_voucher/test/case4_cad_chf.yml @@ -0,0 +1,194 @@ +- + In order to check the Account_voucher module with multi-currency in OpenERP, + I create an invoice in CAD and make its Payment in CHF based on the currency rating on that particular date. +- + I create currency CAD in OpenERP for January of 1.338800 Rate +- + !record {model: res.currency.rate, id: jan_cad}: + currency_id: base.CAD + name: !eval "'%s-01-01' %(datetime.now().year)" + rate: 1.338800 +- + I create currency CAD in OpenERP for March of 2.000000 Rate +- + !record {model: res.currency.rate, id: mar_cad}: + currency_id: base.CAD + name: !eval "'%s-03-01' %(datetime.now().year)" + rate: 2.000000 +- + I create currency CHF in OpenERP for January of 1.308600 Rate +- + !record {model: res.currency.rate, id: jan_chf}: + currency_id: base.CHF + name: !eval "'%s-01-01' %(datetime.now().year)" + rate: 1.308600 +- + I create currency CHF in OpenERP for March of 1.250000 Rate +- + !record {model: res.currency.rate, id: mar_chf}: + currency_id: base.CHF + name: !eval "'%s-03-01' %(datetime.now().year)" + rate: 1.250000 +- + I create a bank journal with CHF as currency +- + !record {model: account.journal, id: bank_journal_CHF}: + name: Bank Journal(CHF) + code: BCHF + type: bank + analytic_journal_id: account.sit + sequence_id: account.sequence_bank_journal + default_debit_account_id: account.cash + default_credit_account_id: account.cash + currency: base.CHF + company_id: base.main_company + view_id: account.account_journal_bank_view +- + I set up some accounts for currency rate expense/income in my company +- + !record {model: res.company, id: base.main_company}: + property_expense_currency_exchange: account.o_expense + property_income_currency_exchange: account.o_income +- + I create a new partner John Armani. +- + !record {model: res.partner, id: res_partner_john0}: + address: + - city: paris + country_id: base.fr + name: John + street: 1 rue Rockfeller + type: invoice + zip: '75016' + name: Mr.John Armani +- + I create the first invoice on 1st January for 200 CAD +- + !record {model: account.invoice, id: account_first_invoice_jan_cad}: + account_id: account.a_recv + address_contact_id: base.res_partner_address_3000 + address_invoice_id: base.res_partner_address_3000 + company_id: base.main_company + currency_id: base.CAD + date_invoice: !eval "'%s-01-01' %(datetime.now().year)" + period_id: account.period_1 + invoice_line: + - account_id: account.a_sale + name: '[PC1] Basic PC' + price_unit: 200.0 + quantity: 1.0 + product_id: product.product_product_pc1 + uos_id: product.product_uom_unit + journal_id: account.sales_journal + partner_id: res_partner_john0 + reference_type: none +- + I Validate invoice by clicking on Validate button +- + !workflow {model: account.invoice, action: invoice_open, ref: account_first_invoice_jan_cad} +- + I check that first invoice move is correct for debtor account (debit - credit == 149.39) +- + !python {model: account.invoice}: | + invoice_id = self.browse(cr, uid, ref("account_first_invoice_jan_cad")) + assert invoice_id.move_id, "Move not created for open invoice" + move_line_obj = self.pool.get('account.move.line') + move_lines = move_line_obj.search(cr, uid, [('move_id', '=', invoice_id.move_id.id), ('account_id', '=', invoice_id.account_id.id)]) + move_line = move_line_obj.browse(cr, uid, move_lines[0]) + assert (move_line.debit - move_line.credit == 149.39), "Invoice move is incorrect for debtors account" +- + I create the first voucher of payment + +- + !python {model: account.voucher}: | + import netsvc, time + vals = {} + res = self.onchange_partner_id(cr, uid, [], ref("res_partner_john0"), ref('bank_journal_CHF'), 300.00, 2, ttype='receipt', date=False) + vals = { + 'account_id': ref('account.cash'), + 'amount': 300.00, + 'company_id': ref('base.main_company'), + 'currency_id': ref('base.CHF'), + 'journal_id': ref('bank_journal_CHF'), + 'partner_id': ref('res_partner_john0'), + 'period_id': ref('account.period_3'), + 'type': 'receipt', + 'date': time.strftime("%Y-03-01"), + 'payment_option': 'with_writeoff', + 'writeoff_acc_id': ref('account.a_expense'), + 'comment': 'Write Off', + 'name': 'First payment', + } + if not res['value']['line_cr_ids']: + res['value']['line_cr_ids'] = [{'type': 'cr', 'account_id': ref('account.a_recv'),}] + for item in res['value']['line_cr_ids']: + if item['amount_unreconciled'] == 200.00: + item['amount'] = 200.00 + vals['line_cr_ids'] = [(0,0,i) for i in res['value']['line_cr_ids']] + id = self.create(cr, uid, vals) + voucher_id = self.browse(cr, uid, id) + assert (voucher_id.state=='draft'), "Voucher is not in draft state" +- + I check that writeoff amount computed is 175.0 +- + !python {model: account.voucher}: | + voucher = self.search(cr, uid, [('name', '=', 'First payment'), ('partner_id', '=', ref('res_partner_john0'))]) + voucher_id = self.browse(cr, uid, voucher[0]) + assert (voucher_id.writeoff_amount == 175.0), "Writeoff amount is not 175.0" +- + I check that currency rate difference is 49.39 +- + !python {model: account.voucher}: | + voucher = self.search(cr, uid, [('name', '=', 'First payment'), ('partner_id', '=', ref('res_partner_john0'))]) + voucher_id = self.browse(cr, uid, voucher[0]) + curr_diff = float(str(voucher_id.currency_rate_difference)) + assert (curr_diff == 49.39), "Currency rate difference is not 49.39" +- + I confirm the voucher +- + !python {model: account.voucher}: | + import netsvc + voucher = self.search(cr, uid, [('name', '=', 'First payment'), ('partner_id', '=', ref('res_partner_john0'))]) + wf_service = netsvc.LocalService("workflow") + wf_service.trg_validate(uid, 'account.voucher', voucher[0], 'proforma_voucher', cr) +- + I check that the move of my voucher is valid +- + !python {model: account.voucher}: | + voucher = self.search(cr, uid, [('name', '=', 'First payment'), ('partner_id', '=', ref('res_partner_john0'))]) + voucher_id = self.browse(cr, uid, voucher[0]) + move_line_obj = self.pool.get('account.move.line') + move_lines = move_line_obj.search(cr, uid, [('move_id', '=', voucher_id.move_id.id)]) + for move_line in move_line_obj.browse(cr, uid, move_lines): + assert move_line.state == 'valid', "Voucher move is not valid" + assert voucher_id.state == 'posted', "Voucher state is not posted" +- + I check that my debtor account is correct +- + I check that the debtor account has 1 new line with -200 as amount_currency columns and 149.39 of credit and currency is CAD($). +- + I check that my currency rate difference is correct. 49.39 in debit with no amount_currency +- + I check that my writeoff is correct. 140 credit and -175 amount_currency +- + !python {model: account.voucher}: | + voucher = self.search(cr, uid, [('name', '=', 'First payment'), ('partner_id', '=', ref('res_partner_john0'))]) + voucher_id = self.browse(cr, uid, voucher[0]) + move_line_obj = self.pool.get('account.move.line') + move_lines = move_line_obj.search(cr, uid, [('move_id', '=', voucher_id.move_id.id)]) + for move_line in move_line_obj.browse(cr, uid, move_lines): + if move_line.amount_currency == -200: + assert move_line.credit == 149.39, "Debtor account has wrong entry." + elif move_line.debit == 49.39: + assert move_line.amount_currency == 0.00, "Incorrect Currency Difference." + elif move_line.credit == 140.00: + assert move_line.amount_currency == -175.00, "Writeoff amount is wrong." +- + I check the residual amount of Invoice1, should be 0 in residual currency and 0 in amount_residual and paid +- + !python {model: account.invoice}: | + invoice_id = self.browse(cr, uid, ref("account_first_invoice_jan_cad")) + move_line_obj = self.pool.get('account.move.line') + move_lines = move_line_obj.search(cr, uid, [('move_id', '=', invoice_id.move_id.id), ('invoice', '=', invoice_id.id), ('account_id', '=', invoice_id.account_id.id)]) + move_line = move_line_obj.browse(cr, uid, move_lines[0]) + assert (move_line.amount_residual_currency == 0.0 and move_line.amount_residual == 0.0 and invoice_id.state == 'paid') , "Residual amount is not correct for first Invoice" From 7c9a8945f42d5c00fb02987043697cc2ccbfe75c Mon Sep 17 00:00:00 2001 From: "Bhumi Thakkar (Open ERP)" Date: Wed, 28 Sep 2011 10:47:00 +0530 Subject: [PATCH 095/409] [IMP] On refresh login page load. bzr revid: bth@tinyerp.com-20110928051700-rb2x9ufn2xcwn8pk --- addons/web_mobile/static/src/js/chrome_mobile.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/addons/web_mobile/static/src/js/chrome_mobile.js b/addons/web_mobile/static/src/js/chrome_mobile.js index e8260739161..3427c0dcdaa 100644 --- a/addons/web_mobile/static/src/js/chrome_mobile.js +++ b/addons/web_mobile/static/src/js/chrome_mobile.js @@ -14,6 +14,9 @@ openerp.web_mobile.mobilewebclient = function(element_id) { openerp.web_mobile.MobileWebClient = openerp.web.Widget.extend({ init: function(element_id) { this._super(null, element_id); + if(document.URL.length>59){ + window.location.replace('/mobile'); + } QWeb.add_template("xml/web_mobile.xml"); var params = {}; this.$element.html(QWeb.render("WebClient", {})); From 5f59f67e835f3a3fcf046c566ee5b1cd936a0ce8 Mon Sep 17 00:00:00 2001 From: "Jiten (OpenERP)" Date: Wed, 28 Sep 2011 11:55:19 +0530 Subject: [PATCH 096/409] [IMP] Improved indentation. bzr revid: jra@tinyerp.com-20110928062519-9pqd1to80a0bwoye --- .../web_mobile/static/src/js/chrome_mobile.js | 45 +- .../web_mobile/static/src/js/form_mobile.js | 388 +++++++++--------- .../web_mobile/static/src/js/list_mobile.js | 12 +- .../web_mobile/static/src/xml/web_mobile.xml | 38 +- 4 files changed, 225 insertions(+), 258 deletions(-) diff --git a/addons/web_mobile/static/src/js/chrome_mobile.js b/addons/web_mobile/static/src/js/chrome_mobile.js index 3427c0dcdaa..d3e69027b9e 100644 --- a/addons/web_mobile/static/src/js/chrome_mobile.js +++ b/addons/web_mobile/static/src/js/chrome_mobile.js @@ -33,20 +33,16 @@ openerp.web_mobile.MobileWebClient = openerp.web.Widget.extend({ openerp.web_mobile.Login = openerp.web.Widget.extend({ start: function() { - this.has_local_storage = typeof(localStorage) != 'undefined'; this.remember_creditentials = true; this.selected_login = null; this.selected_password = null; - if (this.has_local_storage && this.remember_creditentials) { this.selected_login = localStorage.getItem('last_login_login_success'); this.selected_password = localStorage.getItem('last_password_login_success'); } - var self = this; jQuery("#oe_header").children().remove(); - this.rpc("/web/database/get_list", {}, function(result) { self.db_list = result.db_list; self.$element.html(QWeb.render("Login", self)); @@ -66,7 +62,6 @@ openerp.web_mobile.Login = openerp.web.Widget.extend({ var db = $e.find("div select[name=database]").val(); var login = $e.find("div input[name=login]").val(); var password = $e.find("div input[name=password]").val(); - //$e.hide(); // Should hide then call callback this.session.session_login(db, login, password, function() { @@ -116,6 +111,7 @@ openerp.web_mobile.Login = openerp.web.Widget.extend({ }); } }); + openerp.web_mobile.Header = openerp.web.Widget.extend({ init: function(session, element_id) { this._super(session, element_id); @@ -142,7 +138,6 @@ openerp.web_mobile.Shortcuts = openerp.web.Widget.extend({ var self = this; this.rpc('/web/session/sc_list',{} ,function(res){ self.$element.html(QWeb.render("Shortcuts", {'sc' : res})) - self.$element.find("[data-role=header]").find('h1').html('Favourite'); self.$element.find("[data-role=header]").find('#home').click(function(){ $.mobile.changePage("#oe_menu", "slide", false, true); @@ -167,7 +162,6 @@ openerp.web_mobile.Shortcuts = openerp.web.Widget.extend({ $shortcut = $(ev.currentTarget); id = $shortcut.data('menu'); res_id = $shortcut.data('res'); - if(!$('[id^="oe_list_'+res_id+'"]').html()){ $('
').appendTo('#moe'); this.listview = new openerp.web_mobile.ListView(self, "oe_list_"+res_id, res_id); @@ -198,7 +192,6 @@ openerp.web_mobile.Menu = openerp.web.Widget.extend({ this.header.start(); this.footer = new openerp.web_mobile.Footer(this, "oe_footer"); this.footer.start(); - this.$element.html(QWeb.render("Menu", this.data)); this.$element.find("[data-role=header]").find('h1').html('Application'); this.$element.find("[data-role=footer]").find('#shrotcuts').click(function(){ @@ -235,7 +228,6 @@ openerp.web_mobile.Menu = openerp.web.Widget.extend({ this.$element .removeClass("login_valid") .addClass("secondary_menu"); - if(!$('[id^="oe_sec_menu_'+id+'"]').html()){ $('
').appendTo('#moe'); this.secondary = new openerp.web_mobile.Secondary(this, "oe_sec_menu_"+id, this.children); @@ -245,6 +237,7 @@ openerp.web_mobile.Menu = openerp.web.Widget.extend({ } } }); + openerp.web_mobile.Secondary = openerp.web.Widget.extend({ init: function(session, element_id, secondary_menu_id) { this._super(session, element_id); @@ -287,23 +280,23 @@ openerp.web_mobile.Secondary = openerp.web.Widget.extend({ ev.stopPropagation(); var child_len = 0; for (var i = 0; i < this.data.children.length; i++) { - for (var j=0; j < this.data.children[i].children.length; j++) { - if (this.data.children[i].children[j].id == id) { - this.children = this.data.children[i].children[j]; - child_len = this.children.children.length; - } - } + for (var j=0; j < this.data.children[i].children.length; j++) { + if (this.data.children[i].children[j].id == id) { + this.children = this.data.children[i].children[j]; + child_len = this.children.children.length; + } + } } if (child_len > 0) { this.$element .addClass("secondary_menu"); - if(!$('[id^="oe_sec_menu_'+id+'"]').html()){ - $('
').appendTo('#moe'); - this.secondary = new openerp.web_mobile.Secondary(this, "oe_sec_menu_"+id, this.children); - this.secondary.start(); - }else{ - $.mobile.changePage('#oe_sec_menu_'+id, "slide", false, true); - } + if(!$('[id^="oe_sec_menu_'+id+'"]').html()){ + $('
').appendTo('#moe'); + this.secondary = new openerp.web_mobile.Secondary(this, "oe_sec_menu_"+id, this.children); + this.secondary.start(); + }else{ + $.mobile.changePage('#oe_sec_menu_'+id, "slide", false, true); + } } else { if(!$('[id^="oe_list_'+id+'"]').html()){ @@ -314,13 +307,6 @@ openerp.web_mobile.Secondary = openerp.web.Widget.extend({ $.mobile.changePage('#oe_list_'+id, "slide", false, true); } } -/* if(!$('[id^="oe_list_'+id+'"]').html()){ - $('
').appendTo('#moe'); - this.listview = new openerp.web_mobile.ListView(this, "oe_list_"+id, id); - this.listview.start(); - }else{ - $.mobile.changePage('#oe_list_'+id, "slide", false, true); - }*/ jQuery("#oe_header").find("h1").html($menu.data('name')); } }); @@ -328,7 +314,6 @@ openerp.web_mobile.Secondary = openerp.web.Widget.extend({ openerp.web_mobile.Options = openerp.web.Widget.extend({ start: function() { var self = this; - this.$element.html(QWeb.render("Options", this)); this.$element.find("[data-role=header]").find('h1').html('Preference'); this.$element.find("[data-role=footer]").find('#shrotcuts').click(function(){ diff --git a/addons/web_mobile/static/src/js/form_mobile.js b/addons/web_mobile/static/src/js/form_mobile.js index bc52a3a21ae..cd52ac8b7c4 100644 --- a/addons/web_mobile/static/src/js/form_mobile.js +++ b/addons/web_mobile/static/src/js/form_mobile.js @@ -12,23 +12,19 @@ openerp.web_mobile.FormView = openerp.web.Widget.extend({ this.head_title = head_title; this.resmodel = resmodel; this.viewid = viewid; - }, start: function() { var self = this; var id = this.list_id; - var model = this.action.res_model || this.resmodel; if(this.action){ var view_id = this.action.views[1][0]; }else{ var view_id = this.viewid; } - this.dataset = new openerp.web.DataSetSearch(this, model, null, null); var context = new openerp.web.CompoundContext(this.dataset.get_context()); this.dataset.read_slice([],{}, function (result) { - for (var i = 0; i < result.length; i++) { if (result[i].id == id) { var data = result[i]; @@ -44,216 +40,211 @@ openerp.web_mobile.FormView = openerp.web.Widget.extend({ } } self.$element.html(QWeb.render("FormView", {'get_fields': get_fields, 'notebooks': notebooks || false, 'fields' : fields, 'values' : data ,'temp_flag':'1'})); - for(var i=0;i ').appendTo('#moe'); + this.formview = new openerp.web_mobile.FormView(self, "oe_form_"+selected_id+select_model, selected_id, '', head, select_model, false); + this.formview.start(); + }else{ + $.mobile.changePage('#oe_form_'+selected_id+select_model, "slide", false, true); } - else{ - $.mobile.changePage("#oe_shortcuts", "slide", false, true); + } + }); + self.$element.find('[data-role=collapsible-set]').find('[data-role=collapsible]').each(function(i){ + for (var k = 0; k < notebooks.children.length; k++) { + if (notebooks.children[k].attrs.string == $(this).attr('id')) { + get_fields = self.get_fields(notebooks.children[k].children); + for (var i = 0; i < get_fields.length; i++) { + if (fields[get_fields[i].attrs.name].type == 'one2many'){ + self.relational_fields = get_fields[i].attrs.name; + if(fields[get_fields[i].attrs.name].views.form){ + var get_fields_test = self.get_fields(fields[get_fields[i].attrs.name].views.form.arch.children); + var fields_test = fields[get_fields[i].attrs.name]['views'].form.fields; + var notebook=fields[get_fields[i].attrs.name].views.form.arch; + } + } + } } - }); - self.$element.find("[data-role=footer]").find('#preference').click(function(){ - if(!$('#oe_options').html().length){ - this.options = new openerp.web_mobile.Options(self, "oe_options"); - this.options.start(); + if(notebook){ + $(this).find('p').html(QWeb.render("FormView", {'get_fields': get_fields,'fields' : result.fields, 'values' : data,'til': notebook.attrs.string })); + }else{ + $(this).find('p').html(QWeb.render("FormView", {'get_fields': get_fields,'fields' : result.fields, 'values' : data })); } - else{ - $.mobile.changePage("#oe_options", "slide", false, true); - } - }); - self.$element.find('#formbutton').click(function(){ - var head = $(this).prev().find('select').find("option:selected").text(); - var selected_id = $(this).prev().find('select').val(); - var select_model = $(this).prev().find('select').attr('for'); - if(selected_id!="false"){ - if(!$('[id^="oe_form_'+selected_id+select_model+'"]').html()){ - $('
').appendTo('#moe'); + $(this).find('p').find('#formbutton').click(function(){ + var head = $(this).prev().find('select').find("option:selected").text(); + var selected_id = $(this).prev().find('select').val(); + var select_model = $(this).prev().find('select').attr('for'); + if(selected_id!="false"){ + if(!$('[id^="oe_form_'+selected_id+select_model+'"]').html()){ + $('
').appendTo('#moe'); this.formview = new openerp.web_mobile.FormView(self, "oe_form_"+selected_id+select_model, selected_id, '', head, select_model, false); this.formview.start(); - }else{ - $.mobile.changePage('#oe_form_'+selected_id+select_model, "slide", false, true); - } - } - }); - self.$element.find('[data-role=collapsible-set]').find('[data-role=collapsible]').each(function(i){ - - for (var k = 0; k < notebooks.children.length; k++) { - if (notebooks.children[k].attrs.string == $(this).attr('id')) { - get_fields = self.get_fields(notebooks.children[k].children); - for (var i = 0; i < get_fields.length; i++) { - if (fields[get_fields[i].attrs.name].type == 'one2many'){ - self.relational_fields = get_fields[i].attrs.name; - if(fields[get_fields[i].attrs.name].views.form){ - var get_fields_test = self.get_fields(fields[get_fields[i].attrs.name].views.form.arch.children); - var fields_test = fields[get_fields[i].attrs.name]['views'].form.fields; - var notebook=fields[get_fields[i].attrs.name].views.form.arch; - } - } + }else{ + $.mobile.changePage('#oe_form_'+selected_id+select_model, "slide", false, true); } } - if(notebook){ - $(this).find('p').html(QWeb.render("FormView", {'get_fields': get_fields,'fields' : result.fields, 'values' : data,'til': notebook.attrs.string })); - }else{ - $(this).find('p').html(QWeb.render("FormView", {'get_fields': get_fields,'fields' : result.fields, 'values' : data })); - } - $(this).find('p').find('#formbutton').click(function(){ - var head = $(this).prev().find('select').find("option:selected").text(); - var selected_id = $(this).prev().find('select').val(); - var select_model = $(this).prev().find('select').attr('for'); - if(selected_id!="false"){ - if(!$('[id^="oe_form_'+selected_id+select_model+'"]').html()){ - $('
').appendTo('#moe'); - this.formview = new openerp.web_mobile.FormView(self, "oe_form_"+selected_id+select_model, selected_id, '', head, select_model, false); - this.formview.start(); - }else{ - $.mobile.changePage('#oe_form_'+selected_id+select_model, "slide", false, true); - } - } - }); - - } - }); - self.$element.find('[data-role=collapsible-set]').find('[data-role=collapsible]').find('p').find('[data-role=content]').find('ul').find('li').click(function(ev){ - ev.preventDefault(); - ev.stopPropagation(); - var latid,lastid; - $(this).parents().each(function(){ - latid = $(this).attr('id'); - self.$element.find('[data-role=collapsible-set]').find('[data-role=collapsible]').each(function(){ - if(latid==$(this).attr('id')){ - lastid = $(this).attr('id'); - } - }); }); - var relational = $(this).attr('for'); - if(result.fields[relational]){ - var head = $.trim($(this).text()); - var dataset = new openerp.web.DataSetStatic(self, result.fields[relational].relation, result.fields[relational].context); - dataset.domain=[['id', 'in', data[relational]]]; - dataset.name_search('', dataset.domain, 'in',false ,function(res){ - for(var i=0;i ').appendTo('#moe'); - $('[id^="oe_list_'+relational+'_'+self.element_id+'"]').html(QWeb.render("ListView", {'records' : res})); - $('[id^="oe_list_'+relational+'_'+self.element_id+'"]').find("[data-role=header]").find('h1').html(head); - $('[id^="oe_list_'+relational+'_'+self.element_id+'"]').find("[data-role=header]").find('#home').click(function(){ - $.mobile.changePage("#oe_menu", "slide", false, true); - }); - $('[id^="oe_list_'+relational+'_'+self.element_id+'"]').find("[data-role=footer]").find('#shrotcuts').click(function(){ - if(!$('#oe_shortcuts').html().length){ - this.shortcuts = new openerp.web_mobile.Shortcuts(self, "oe_shortcuts"); - this.shortcuts.start(); - } - else{ - $.mobile.changePage("#oe_shortcuts", "slide", false, true); - } - }); - $('[id^="oe_list_'+relational+'_'+self.element_id+'"]').find("[data-role=footer]").find('#preference').click(function(){ - if(!$('#oe_options').html().length){ - this.options = new openerp.web_mobile.Options(self, "oe_options"); - this.options.start(); - } - else{ - $.mobile.changePage("#oe_options", "slide", false, true); - } - }); - $('[id^="oe_list_'+relational+'_'+self.element_id+'"]').find("a#list-id").click(function(ev){ - ev.preventDefault(); - ev.stopPropagation(); - var head_title = $(this).text(); - var listid = $(ev.currentTarget).data('id'); - dataset = new openerp.web.DataSetSearch(self, dataset.model, null, null); - dataset.read_slice([],{}, function (result_relational) { - for (var i = 0; i < result_relational.length; i++) { - if (result_relational[i].id == listid) { - var data_relational = result_relational[i]; - } + } + }); + self.$element.find('[data-role=collapsible-set]').find('[data-role=collapsible]').find('p').find('[data-role=content]').find('ul').find('li').click(function(ev){ + ev.preventDefault(); + ev.stopPropagation(); + var latid,lastid; + $(this).parents().each(function(){ + latid = $(this).attr('id'); + self.$element.find('[data-role=collapsible-set]').find('[data-role=collapsible]').each(function(){ + if(latid==$(this).attr('id')){ + lastid = $(this).attr('id'); + } + }); + }); + var relational = $(this).attr('for'); + if(result.fields[relational]){ + var head = $.trim($(this).text()); + var dataset = new openerp.web.DataSetStatic(self, result.fields[relational].relation, result.fields[relational].context); + dataset.domain=[['id', 'in', data[relational]]]; + dataset.name_search('', dataset.domain, 'in',false ,function(res){ + for(var i=0;i ').appendTo('#moe'); + $('[id^="oe_list_'+relational+'_'+self.element_id+'"]').html(QWeb.render("ListView", {'records' : res})); + $('[id^="oe_list_'+relational+'_'+self.element_id+'"]').find("[data-role=header]").find('h1').html(head); + $('[id^="oe_list_'+relational+'_'+self.element_id+'"]').find("[data-role=header]").find('#home').click(function(){ + $.mobile.changePage("#oe_menu", "slide", false, true); + }); + $('[id^="oe_list_'+relational+'_'+self.element_id+'"]').find("[data-role=footer]").find('#shrotcuts').click(function(){ + if(!$('#oe_shortcuts').html().length){ + this.shortcuts = new openerp.web_mobile.Shortcuts(self, "oe_shortcuts"); + this.shortcuts.start(); + } + else{ + $.mobile.changePage("#oe_shortcuts", "slide", false, true); + } + }); + $('[id^="oe_list_'+relational+'_'+self.element_id+'"]').find("[data-role=footer]").find('#preference').click(function(){ + if(!$('#oe_options').html().length){ + this.options = new openerp.web_mobile.Options(self, "oe_options"); + this.options.start(); + } + else{ + $.mobile.changePage("#oe_options", "slide", false, true); + } + }); + $('[id^="oe_list_'+relational+'_'+self.element_id+'"]').find("a#list-id").click(function(ev){ + ev.preventDefault(); + ev.stopPropagation(); + var head_title = $(this).text(); + var listid = $(ev.currentTarget).data('id'); + dataset = new openerp.web.DataSetSearch(self, dataset.model, null, null); + dataset.read_slice([],{}, function (result_relational) { + for (var i = 0; i < result_relational.length; i++) { + if (result_relational[i].id == listid) { + var data_relational = result_relational[i]; } - if(!$('[id^="oe_form_'+listid+result.fields[relational].relation+'"]').html()){ - $('
').appendTo('#moe'); - for (var k = 0; k < notebooks.children.length; k++) { - if (notebooks.children[k].attrs.string == lastid) { - get_fields = self.get_fields(notebooks.children[k].children); - for (var i = 0; i < get_fields.length; i++) { - if (fields[get_fields[i].attrs.name].type == 'one2many'){ - self.relational_fields = get_fields[i].attrs.name; - if(fields[get_fields[i].attrs.name].views.form){ - var get_fields_test = self.get_fields(fields[get_fields[i].attrs.name].views.form.arch.children); - var fields_test = fields[get_fields[i].attrs.name]['views'].form.fields; - var notebook=fields[get_fields[i].attrs.name].views.form.arch; - } + } + if(!$('[id^="oe_form_'+listid+result.fields[relational].relation+'"]').html()){ + $('
').appendTo('#moe'); + for (var k = 0; k < notebooks.children.length; k++) { + if (notebooks.children[k].attrs.string == lastid) { + get_fields = self.get_fields(notebooks.children[k].children); + for (var i = 0; i < get_fields.length; i++) { + if (fields[get_fields[i].attrs.name].type == 'one2many'){ + self.relational_fields = get_fields[i].attrs.name; + if(fields[get_fields[i].attrs.name].views.form){ + var get_fields_test = self.get_fields(fields[get_fields[i].attrs.name].views.form.arch.children); + var fields_test = fields[get_fields[i].attrs.name]['views'].form.fields; + var notebook=fields[get_fields[i].attrs.name].views.form.arch; } } } } - $('[id^="oe_form_'+listid+result.fields[relational].relation+'"]').html(QWeb.render("FormView", {'get_fields': get_fields_test, 'notebooks': false, 'fields' : fields_test, 'values' : data_relational, 'temp_flag':'1' })); - - $('[id^="oe_form_'+listid+result.fields[relational].relation+'"]').find("[data-role=header]").find('h1').html(head_title); - $('[id^="oe_form_'+listid+result.fields[relational].relation+'"]').find("[data-role=header]").find('#home').click(function(){ - $.mobile.changePage("#oe_menu", "slide", false, true); - }); - $('[id^="oe_form_'+listid+result.fields[relational].relation+'"]').find("[data-role=footer]").find('#shrotcuts').click(function(){ - if(!$('#oe_shortcuts').html().length){ - this.shortcuts = new openerp.web_mobile.Shortcuts(self, "oe_shortcuts"); - this.shortcuts.start(); - } - else{ - $.mobile.changePage("#oe_shortcuts", "slide", false, true); - } - }); - $('[id^="oe_form_'+listid+result.fields[relational].relation+'"]').find("[data-role=footer]").find('#preference').click(function(){ - if(!$('#oe_options').html().length){ - this.options = new openerp.web_mobile.Options(self, "oe_options"); - this.options.start(); - } - else{ - $.mobile.changePage("#oe_options", "slide", false, true); - } - }); - $('[id^="oe_form_'+listid+result.fields[relational].relation+'"]').find('#formbutton').click(function(){ - var head = $(this).prev().find('select').find("option:selected").text(); - var selected_id = $(this).prev().find('select').val(); - var select_model = $(this).prev().find('select').attr('for'); - if(selected_id!="false"){ - if(!$('[id^="oe_form_'+selected_id+select_model+'"]').html()){ - $('
').appendTo('#moe'); - this.formview = new openerp.web_mobile.FormView(self, "oe_form_"+selected_id+select_model, selected_id, '', head, select_model, false); - this.formview.start(); - }else{ - $.mobile.changePage('#oe_form_'+selected_id+select_model, "slide", false, true); - } - } - }); - $.mobile.changePage('#oe_form_'+listid+result.fields[relational].relation, "slide", false, true); - }else{ - $.mobile.changePage('#oe_form_'+listid+result.fields[relational].relation, "slide", false, true); } - }); + $('[id^="oe_form_'+listid+result.fields[relational].relation+'"]').html(QWeb.render("FormView", {'get_fields': get_fields_test, 'notebooks': false, 'fields' : fields_test, 'values' : data_relational, 'temp_flag':'1' })); + $('[id^="oe_form_'+listid+result.fields[relational].relation+'"]').find("[data-role=header]").find('h1').html(head_title); + $('[id^="oe_form_'+listid+result.fields[relational].relation+'"]').find("[data-role=header]").find('#home').click(function(){ + $.mobile.changePage("#oe_menu", "slide", false, true); + }); + $('[id^="oe_form_'+listid+result.fields[relational].relation+'"]').find("[data-role=footer]").find('#shrotcuts').click(function(){ + if(!$('#oe_shortcuts').html().length){ + this.shortcuts = new openerp.web_mobile.Shortcuts(self, "oe_shortcuts"); + this.shortcuts.start(); + } + else{ + $.mobile.changePage("#oe_shortcuts", "slide", false, true); + } + }); + $('[id^="oe_form_'+listid+result.fields[relational].relation+'"]').find("[data-role=footer]").find('#preference').click(function(){ + if(!$('#oe_options').html().length){ + this.options = new openerp.web_mobile.Options(self, "oe_options"); + this.options.start(); + } + else{ + $.mobile.changePage("#oe_options", "slide", false, true); + } + }); + $('[id^="oe_form_'+listid+result.fields[relational].relation+'"]').find('#formbutton').click(function(){ + var head = $(this).prev().find('select').find("option:selected").text(); + var selected_id = $(this).prev().find('select').val(); + var select_model = $(this).prev().find('select').attr('for'); + if(selected_id!="false"){ + if(!$('[id^="oe_form_'+selected_id+select_model+'"]').html()){ + $('
').appendTo('#moe'); + this.formview = new openerp.web_mobile.FormView(self, "oe_form_"+selected_id+select_model, selected_id, '', head, select_model, false); + this.formview.start(); + }else{ + $.mobile.changePage('#oe_form_'+selected_id+select_model, "slide", false, true); + } + } + }); + $.mobile.changePage('#oe_form_'+listid+result.fields[relational].relation, "slide", false, true); + }else{ + $.mobile.changePage('#oe_form_'+listid+result.fields[relational].relation, "slide", false, true); + } }); - $.mobile.changePage("#oe_list_"+relational+"_"+self.element_id, "slide", false, true); - }else{ - $.mobile.changePage("#oe_list_"+relational+"_"+self.element_id, "slide", false, true); - } - }); - } - }); - $.mobile.changePage("#"+self.element_id, "slide", false, true); + }); + $.mobile.changePage("#oe_list_"+relational+"_"+self.element_id, "slide", false, true); + }else{ + $.mobile.changePage("#oe_list_"+relational+"_"+self.element_id, "slide", false, true); + } + }); + } }); + $.mobile.changePage("#"+self.element_id, "slide", false, true); + }); }); }, get_fields: function(view_fields, fields) { @@ -269,4 +260,5 @@ openerp.web_mobile.FormView = openerp.web.Widget.extend({ return this.fields; } }); + }; diff --git a/addons/web_mobile/static/src/js/list_mobile.js b/addons/web_mobile/static/src/js/list_mobile.js index e4498ccbdd5..778d0a568d3 100644 --- a/addons/web_mobile/static/src/js/list_mobile.js +++ b/addons/web_mobile/static/src/js/list_mobile.js @@ -10,8 +10,7 @@ openerp.web_mobile.ListView = openerp.web.Widget.extend({ this.list_id = list_id; }, start: function() { - this.rpc('/web/menu/action', {'menu_id': this.list_id}, - this.on_menu_action_loaded); + this.rpc('/web/menu/action', {'menu_id': this.list_id}, this.on_menu_action_loaded); }, on_menu_action_loaded: function(data) { var self = this; @@ -30,11 +29,10 @@ openerp.web_mobile.ListView = openerp.web.Widget.extend({ $.mobile.changePage("#oe_menu", "slide", false, true); }); self.$element.find("[data-role=footer]").find('#shrotcuts').click(function(){ - if(!$('#oe_shortcuts').html().length){ + if (!$('#oe_shortcuts').html().length) { this.shortcuts = new openerp.web_mobile.Shortcuts(self, "oe_shortcuts"); this.shortcuts.start(); - } - else{ + }else{ $.mobile.changePage("#oe_shortcuts", "slide", false, true); } }); @@ -42,8 +40,7 @@ openerp.web_mobile.ListView = openerp.web.Widget.extend({ if(!$('#oe_options').html().length){ this.options = new openerp.web_mobile.Options(self, "oe_options"); this.options.start(); - } - else{ + }else{ $.mobile.changePage("#oe_options", "slide", false, true); } }); @@ -58,7 +55,6 @@ openerp.web_mobile.ListView = openerp.web.Widget.extend({ ev.stopPropagation(); id = $record.data('id'); head_title = $.trim($record.text()); - if(!$('[id^="oe_form_'+id+this.action.res_model+'"]').html()){ $('
').appendTo('#moe'); this.formview = new openerp.web_mobile.FormView(this, "oe_form_"+id+this.action.res_model, id, this.action, head_title, '' ,''); diff --git a/addons/web_mobile/static/src/xml/web_mobile.xml b/addons/web_mobile/static/src/xml/web_mobile.xml index d2df9a1fed9..977057c11f1 100644 --- a/addons/web_mobile/static/src/xml/web_mobile.xml +++ b/addons/web_mobile/static/src/xml/web_mobile.xml @@ -41,7 +41,6 @@

Powered by openerp.com

- @@ -75,6 +74,7 @@ +
@@ -95,19 +95,19 @@
+
@@ -141,11 +142,11 @@
+ -
@@ -181,19 +182,18 @@ : -
+
-
+
- +
-
- +
-

- -

-

+

-

- -

-

+

From 9a980009b06b8ed30d479b5701e8fa5f5ba3dc92 Mon Sep 17 00:00:00 2001 From: "Jiten (OpenERP)" Date: Wed, 28 Sep 2011 12:28:42 +0530 Subject: [PATCH 097/409] [IMP] Minor change for notebook content to replace

to

. bzr revid: jra@tinyerp.com-20110928065842-pl1skf0oyoo6jds4 --- .../web_mobile/static/src/js/form_mobile.js | 26 +++++++------------ .../web_mobile/static/src/xml/web_mobile.xml | 2 ++ 2 files changed, 12 insertions(+), 16 deletions(-) diff --git a/addons/web_mobile/static/src/js/form_mobile.js b/addons/web_mobile/static/src/js/form_mobile.js index cd52ac8b7c4..9dbd0442e8f 100644 --- a/addons/web_mobile/static/src/js/form_mobile.js +++ b/addons/web_mobile/static/src/js/form_mobile.js @@ -53,8 +53,7 @@ openerp.web_mobile.FormView = openerp.web.Widget.extend({ if(!$('#oe_shortcuts').html().length){ this.shortcuts = new openerp.web_mobile.Shortcuts(self, "oe_shortcuts"); this.shortcuts.start(); - } - else{ + }else{ $.mobile.changePage("#oe_shortcuts", "slide", false, true); } }); @@ -62,8 +61,7 @@ openerp.web_mobile.FormView = openerp.web.Widget.extend({ if(!$('#oe_options').html().length){ this.options = new openerp.web_mobile.Options(self, "oe_options"); this.options.start(); - } - else{ + }else{ $.mobile.changePage("#oe_options", "slide", false, true); } }); @@ -97,11 +95,11 @@ openerp.web_mobile.FormView = openerp.web.Widget.extend({ } } if(notebook){ - $(this).find('p').html(QWeb.render("FormView", {'get_fields': get_fields,'fields' : result.fields, 'values' : data,'til': notebook.attrs.string })); + $(this).find('div#page_content').html(QWeb.render("FormView", {'get_fields': get_fields,'fields' : result.fields, 'values' : data,'til': notebook.attrs.string })); }else{ - $(this).find('p').html(QWeb.render("FormView", {'get_fields': get_fields,'fields' : result.fields, 'values' : data })); + $(this).find('div#page_content').html(QWeb.render("FormView", {'get_fields': get_fields,'fields' : result.fields, 'values' : data })); } - $(this).find('p').find('#formbutton').click(function(){ + $(this).find('div#page_content').find('#formbutton').click(function(){ var head = $(this).prev().find('select').find("option:selected").text(); var selected_id = $(this).prev().find('select').val(); var select_model = $(this).prev().find('select').attr('for'); @@ -117,7 +115,7 @@ openerp.web_mobile.FormView = openerp.web.Widget.extend({ }); } }); - self.$element.find('[data-role=collapsible-set]').find('[data-role=collapsible]').find('p').find('[data-role=content]').find('ul').find('li').click(function(ev){ + self.$element.find('[data-role=collapsible-set]').find('[data-role=collapsible]').find('div#page_content').find('[data-role=content]').find('ul').find('li').click(function(ev){ ev.preventDefault(); ev.stopPropagation(); var latid,lastid; @@ -150,8 +148,7 @@ openerp.web_mobile.FormView = openerp.web.Widget.extend({ if(!$('#oe_shortcuts').html().length){ this.shortcuts = new openerp.web_mobile.Shortcuts(self, "oe_shortcuts"); this.shortcuts.start(); - } - else{ + }else{ $.mobile.changePage("#oe_shortcuts", "slide", false, true); } }); @@ -159,8 +156,7 @@ openerp.web_mobile.FormView = openerp.web.Widget.extend({ if(!$('#oe_options').html().length){ this.options = new openerp.web_mobile.Options(self, "oe_options"); this.options.start(); - } - else{ + }else{ $.mobile.changePage("#oe_options", "slide", false, true); } }); @@ -202,8 +198,7 @@ openerp.web_mobile.FormView = openerp.web.Widget.extend({ if(!$('#oe_shortcuts').html().length){ this.shortcuts = new openerp.web_mobile.Shortcuts(self, "oe_shortcuts"); this.shortcuts.start(); - } - else{ + }else{ $.mobile.changePage("#oe_shortcuts", "slide", false, true); } }); @@ -211,8 +206,7 @@ openerp.web_mobile.FormView = openerp.web.Widget.extend({ if(!$('#oe_options').html().length){ this.options = new openerp.web_mobile.Options(self, "oe_options"); this.options.start(); - } - else{ + }else{ $.mobile.changePage("#oe_options", "slide", false, true); } }); diff --git a/addons/web_mobile/static/src/xml/web_mobile.xml b/addons/web_mobile/static/src/xml/web_mobile.xml index 977057c11f1..e4468fdeb15 100644 --- a/addons/web_mobile/static/src/xml/web_mobile.xml +++ b/addons/web_mobile/static/src/xml/web_mobile.xml @@ -282,11 +282,13 @@

+

+
From aa98635b5f15de151c89b1802da4f820f18280ce Mon Sep 17 00:00:00 2001 From: Valentin Lab Date: Wed, 28 Sep 2011 14:02:59 +0200 Subject: [PATCH 098/409] [imp] extracted on_loaded function for customizability and readability's sake bzr revid: valentin.lab@kalysto.org-20110928120259-9wqx2iijr498t2sq --- .../web_mobile/static/src/js/form_mobile.js | 23 +++++++++++-------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/addons/web_mobile/static/src/js/form_mobile.js b/addons/web_mobile/static/src/js/form_mobile.js index 9dbd0442e8f..ce3a7374bc4 100644 --- a/addons/web_mobile/static/src/js/form_mobile.js +++ b/addons/web_mobile/static/src/js/form_mobile.js @@ -27,22 +27,27 @@ openerp.web_mobile.FormView = openerp.web.Widget.extend({ this.dataset.read_slice([],{}, function (result) { for (var i = 0; i < result.length; i++) { if (result[i].id == id) { - var data = result[i]; + self.datarecord = result[i]; } } - self.rpc("/web/view/load", {"model": model, "view_id": view_id, "view_type": "form", context: context}, function (result) { + self.rpc("/web/view/load", {"model": model, "view_id": view_id, "view_type": "form", context: context}, self.on_loaded) + }); + }, + on_loaded: function(result) { + var self = this; var fields = result.fields; var view_fields = result.arch.children; - var get_fields = self.get_fields(view_fields); + var get_fields = this.get_fields(view_fields); + for (var j = 0; j < view_fields.length; j++) { if (view_fields[j].tag == 'notebook') { var notebooks = view_fields[j]; } } - self.$element.html(QWeb.render("FormView", {'get_fields': get_fields, 'notebooks': notebooks || false, 'fields' : fields, 'values' : data ,'temp_flag':'1'})); + self.$element.html(QWeb.render("FormView", {'get_fields': get_fields, 'notebooks': notebooks || false, 'fields' : fields, 'values' : self.datarecord ,'temp_flag':'1'})); for(var i=0;i Date: Thu, 29 Sep 2011 15:18:06 +0530 Subject: [PATCH 099/409] [FIX] Minor change for render shortcut and improved indentation. bzr revid: jra@tinyerp.com-20110929094806-jvf391ahuc8918ke --- .../web_mobile/static/src/js/chrome_mobile.js | 27 +- .../web_mobile/static/src/js/form_mobile.js | 361 +++++++++--------- 2 files changed, 190 insertions(+), 198 deletions(-) diff --git a/addons/web_mobile/static/src/js/chrome_mobile.js b/addons/web_mobile/static/src/js/chrome_mobile.js index d1ce7d841e6..bdde5efbdc4 100644 --- a/addons/web_mobile/static/src/js/chrome_mobile.js +++ b/addons/web_mobile/static/src/js/chrome_mobile.js @@ -103,8 +103,7 @@ openerp.web_mobile.Login = openerp.web.Widget.extend({ if(!$('#oe_menu').html().length){ this.menu = new openerp.web_mobile.Menu(this, "oe_menu", "oe_secondary_menu"); this.menu.start(); - } - else{ + }else{ $.mobile.changePage("#oe_menu", "slide", false, true); } }, @@ -145,13 +144,14 @@ openerp.web_mobile.Footer = openerp.web.Widget.extend({ openerp.web_mobile.Shortcuts = openerp.web.Widget.extend({ template: "Shortcuts", + init: function(session, element_id) { this._super(session, element_id); }, start: function() { var self = this; this.rpc('/web/session/sc_list',{} ,function(res){ - self.$element.html(this.render({'sc' : res})) + self.$element.html(self.render({'sc' : res})) self.$element.find("[data-role=header]").find('h1').html('Favourite'); self.$element.find("[data-role=header]").find('#home').click(function(){ $.mobile.changePage("#oe_menu", "slide", false, true); @@ -161,8 +161,7 @@ openerp.web_mobile.Shortcuts = openerp.web.Widget.extend({ if(!$('#oe_options').html().length){ this.options = new openerp.web_mobile.Options(self, "oe_options"); this.options.start(); - } - else{ + }else{ $.mobile.changePage("#oe_options", "slide", false, true); } }); @@ -215,8 +214,7 @@ openerp.web_mobile.Menu = openerp.web.Widget.extend({ if(!$('#oe_shortcuts').html().length){ this.shortcuts = new openerp.web_mobile.Shortcuts(self, "oe_shortcuts"); this.shortcuts.start(); - } - else{ + }else{ $.mobile.changePage($("#oe_shortcuts"), "slide", false, true); } }); @@ -224,8 +222,7 @@ openerp.web_mobile.Menu = openerp.web.Widget.extend({ if(!$('#oe_options').html().length){ this.options = new openerp.web_mobile.Options(self, "oe_options"); this.options.start(); - } - else{ + }else{ $.mobile.changePage("#oe_options", "slide", false, true); } }); @@ -273,8 +270,7 @@ openerp.web_mobile.Secondary = openerp.web.Widget.extend({ if(!$('#oe_shortcuts').html().length){ this.shortcuts = new openerp.web_mobile.Shortcuts(self, "oe_shortcuts"); this.shortcuts.start(); - } - else{ + }else{ $.mobile.changePage("#oe_shortcuts", "slide", false, true); } }); @@ -282,8 +278,7 @@ openerp.web_mobile.Secondary = openerp.web.Widget.extend({ if(!$('#oe_options').html().length){ this.options = new openerp.web_mobile.Options(self, "oe_options"); this.options.start(); - } - else{ + }else{ $.mobile.changePage("#oe_options", "slide", false, true); } }); @@ -317,8 +312,7 @@ openerp.web_mobile.Secondary = openerp.web.Widget.extend({ }else{ $.mobile.changePage('#oe_sec_menu_'+id, "slide", false, true); } - } - else { + }else { if(!$('[id^="oe_list_'+id+'"]').html()){ $('
').appendTo('#moe'); this.listview = new openerp.web_mobile.ListView(this, "oe_list_"+id, id); @@ -343,8 +337,7 @@ openerp.web_mobile.Options = openerp.web.Widget.extend({ if(!$('#oe_shortcuts').html().length){ this.shortcuts = new openerp.web_mobile.Shortcuts(self, "oe_shortcuts"); this.shortcuts.start(); - } - else{ + }else{ $.mobile.changePage("#oe_shortcuts", "slide", false, true); } }); diff --git a/addons/web_mobile/static/src/js/form_mobile.js b/addons/web_mobile/static/src/js/form_mobile.js index cf4aa52ae22..69182a92f65 100644 --- a/addons/web_mobile/static/src/js/form_mobile.js +++ b/addons/web_mobile/static/src/js/form_mobile.js @@ -37,215 +37,214 @@ openerp.web_mobile.FormView = openerp.web.Widget.extend({ }); }, on_loaded: function(result) { - var self = this; - var fields = result.fields; - var view_fields = result.arch.children; - var get_fields = this.get_fields(view_fields); - - for (var j = 0; j < view_fields.length; j++) { - if (view_fields[j].tag == 'notebook') { - var notebooks = view_fields[j]; + var self = this; + var fields = result.fields; + var view_fields = result.arch.children; + var get_fields = this.get_fields(view_fields); + for (var j = 0; j < view_fields.length; j++) { + if (view_fields[j].tag == 'notebook') { + var notebooks = view_fields[j]; + } + } + self.$element.html(self.render({'get_fields': get_fields, 'notebooks': notebooks || false, 'fields' : fields, 'values' : self.datarecord ,'temp_flag':'1'})); + for(var i=0;i
').appendTo('#moe'); + this.formview = new openerp.web_mobile.FormView(self, "oe_form_"+selected_id+select_model, selected_id, '', head, select_model, false); + this.formview.start(); + }else{ + $.mobile.changePage('#oe_form_'+selected_id+select_model, "slide", false, true); + } + } + }); + self.$element.find('[data-role=collapsible-set]').find('[data-role=collapsible]').each(function(i){ + for (var k = 0; k < notebooks.children.length; k++) { + if (notebooks.children[k].attrs.string == $(this).attr('id')) { + get_fields = self.get_fields(notebooks.children[k].children); + for (var i = 0; i < get_fields.length; i++) { + if (fields[get_fields[i].attrs.name].type == 'one2many'){ + self.relational_fields = get_fields[i].attrs.name; + if(fields[get_fields[i].attrs.name].views.form){ + var get_fields_test = self.get_fields(fields[get_fields[i].attrs.name].views.form.arch.children); + var fields_test = fields[get_fields[i].attrs.name]['views'].form.fields; + var notebook=fields[get_fields[i].attrs.name].views.form.arch; + } + } } } - self.$element.html(self.render({'get_fields': get_fields, 'notebooks': notebooks || false, 'fields' : fields, 'values' : self.datarecord ,'temp_flag':'1'})); - for(var i=0;i
').appendTo('#moe'); - this.formview = new openerp.web_mobile.FormView(self, "oe_form_"+selected_id+select_model, selected_id, '', head, select_model, false); - this.formview.start(); + this.formview = new openerp.web_mobile.FormView(self, "oe_form_"+selected_id+select_model, selected_id, '', head, select_model, false); + this.formview.start(); }else{ $.mobile.changePage('#oe_form_'+selected_id+select_model, "slide", false, true); } } }); - self.$element.find('[data-role=collapsible-set]').find('[data-role=collapsible]').each(function(i){ - for (var k = 0; k < notebooks.children.length; k++) { - if (notebooks.children[k].attrs.string == $(this).attr('id')) { - get_fields = self.get_fields(notebooks.children[k].children); - for (var i = 0; i < get_fields.length; i++) { - if (fields[get_fields[i].attrs.name].type == 'one2many'){ - self.relational_fields = get_fields[i].attrs.name; - if(fields[get_fields[i].attrs.name].views.form){ - var get_fields_test = self.get_fields(fields[get_fields[i].attrs.name].views.form.arch.children); - var fields_test = fields[get_fields[i].attrs.name]['views'].form.fields; - var notebook=fields[get_fields[i].attrs.name].views.form.arch; - } - } - } - } - if(notebook){ - $(this).find('div#page_content').html(self.render({'get_fields': get_fields,'fields' : result.fields, 'values' : self.datarecord,'til': notebook.attrs.string })); - }else{ - $(this).find('div#page_content').html(self.render({'get_fields': get_fields,'fields' : result.fields, 'values' : self.datarecord})); - } - $(this).find('div#page_content').find('#formbutton').click(function(){ - var head = $(this).prev().find('select').find("option:selected").text(); - var selected_id = $(this).prev().find('select').val(); - var select_model = $(this).prev().find('select').attr('for'); - if(selected_id!="false"){ - if(!$('[id^="oe_form_'+selected_id+select_model+'"]').html()){ - $('
').appendTo('#moe'); - this.formview = new openerp.web_mobile.FormView(self, "oe_form_"+selected_id+select_model, selected_id, '', head, select_model, false); - this.formview.start(); - }else{ - $.mobile.changePage('#oe_form_'+selected_id+select_model, "slide", false, true); - } - } - }); + } + }); + self.$element.find('[data-role=collapsible-set]').find('[data-role=collapsible]').find('div#page_content').find('[data-role=content]').find('ul').find('li').click(function(ev){ + ev.preventDefault(); + ev.stopPropagation(); + var latid,lastid; + $(this).parents().each(function(){ + latid = $(this).attr('id'); + self.$element.find('[data-role=collapsible-set]').find('[data-role=collapsible]').each(function(){ + if(latid==$(this).attr('id')){ + lastid = $(this).attr('id'); } }); - self.$element.find('[data-role=collapsible-set]').find('[data-role=collapsible]').find('div#page_content').find('[data-role=content]').find('ul').find('li').click(function(ev){ - ev.preventDefault(); - ev.stopPropagation(); - var latid,lastid; - $(this).parents().each(function(){ - latid = $(this).attr('id'); - self.$element.find('[data-role=collapsible-set]').find('[data-role=collapsible]').each(function(){ - if(latid==$(this).attr('id')){ - lastid = $(this).attr('id'); + }); + var relational = $(this).attr('for'); + if(result.fields[relational]){ + var head = $.trim($(this).text()); + var dataset = new openerp.web.DataSetStatic(self, result.fields[relational].relation, result.fields[relational].context); + dataset.domain=[['id', 'in', self.datarecord[relational]]]; + dataset.name_search('', dataset.domain, 'in',false ,function(res){ + for(var i=0;i
').appendTo('#moe'); + $('[id^="oe_list_'+relational+'_'+self.element_id+'"]').html(openerp.web.qweb.render("ListView", {'records' : res})); + $('[id^="oe_list_'+relational+'_'+self.element_id+'"]').find("[data-role=header]").find('h1').html(head); + $('[id^="oe_list_'+relational+'_'+self.element_id+'"]').find("[data-role=header]").find('#home').click(function(){ + $.mobile.changePage("#oe_menu", "slide", false, true); + }); + $('[id^="oe_list_'+relational+'_'+self.element_id+'"]').find("[data-role=footer]").find('#shrotcuts').click(function(){ + if(!$('#oe_shortcuts').html().length){ + this.shortcuts = new openerp.web_mobile.Shortcuts(self, "oe_shortcuts"); + this.shortcuts.start(); + }else{ + $.mobile.changePage("#oe_shortcuts", "slide", false, true); } }); - }); - var relational = $(this).attr('for'); - if(result.fields[relational]){ - var head = $.trim($(this).text()); - var dataset = new openerp.web.DataSetStatic(self, result.fields[relational].relation, result.fields[relational].context); - dataset.domain=[['id', 'in', self.datarecord[relational]]]; - dataset.name_search('', dataset.domain, 'in',false ,function(res){ - for(var i=0;i
').appendTo('#moe'); - $('[id^="oe_list_'+relational+'_'+self.element_id+'"]').html(openerp.web.qweb.render("ListView", {'records' : res})); - $('[id^="oe_list_'+relational+'_'+self.element_id+'"]').find("[data-role=header]").find('h1').html(head); - $('[id^="oe_list_'+relational+'_'+self.element_id+'"]').find("[data-role=header]").find('#home').click(function(){ - $.mobile.changePage("#oe_menu", "slide", false, true); - }); - $('[id^="oe_list_'+relational+'_'+self.element_id+'"]').find("[data-role=footer]").find('#shrotcuts').click(function(){ - if(!$('#oe_shortcuts').html().length){ - this.shortcuts = new openerp.web_mobile.Shortcuts(self, "oe_shortcuts"); - this.shortcuts.start(); - }else{ - $.mobile.changePage("#oe_shortcuts", "slide", false, true); + }); + $('[id^="oe_list_'+relational+'_'+self.element_id+'"]').find("a#list-id").click(function(ev){ + ev.preventDefault(); + ev.stopPropagation(); + var head_title = $(this).text(); + var listid = $(ev.currentTarget).data('id'); + dataset = new openerp.web.DataSetSearch(self, dataset.model, null, null); + dataset.read_slice([],{}, function (result_relational) { + for (var i = 0; i < result_relational.length; i++) { + if (result_relational[i].id == listid) { + var data_relational = result_relational[i]; } - }); - $('[id^="oe_list_'+relational+'_'+self.element_id+'"]').find("[data-role=footer]").find('#preference').click(function(){ - if(!$('#oe_options').html().length){ - this.options = new openerp.web_mobile.Options(self, "oe_options"); - this.options.start(); - }else{ - $.mobile.changePage("#oe_options", "slide", false, true); - } - }); - $('[id^="oe_list_'+relational+'_'+self.element_id+'"]').find("a#list-id").click(function(ev){ - ev.preventDefault(); - ev.stopPropagation(); - var head_title = $(this).text(); - var listid = $(ev.currentTarget).data('id'); - dataset = new openerp.web.DataSetSearch(self, dataset.model, null, null); - dataset.read_slice([],{}, function (result_relational) { - for (var i = 0; i < result_relational.length; i++) { - if (result_relational[i].id == listid) { - var data_relational = result_relational[i]; + } + if(!$('[id^="oe_form_'+listid+result.fields[relational].relation+'"]').html()){ + $('
').appendTo('#moe'); + for (var k = 0; k < notebooks.children.length; k++) { + if (notebooks.children[k].attrs.string == lastid) { + get_fields = self.get_fields(notebooks.children[k].children); + for (var i = 0; i < get_fields.length; i++) { + if (fields[get_fields[i].attrs.name].type == 'one2many'){ + self.relational_fields = get_fields[i].attrs.name; + if(fields[get_fields[i].attrs.name].views.form){ + var get_fields_test = self.get_fields(fields[get_fields[i].attrs.name].views.form.arch.children); + var fields_test = fields[get_fields[i].attrs.name]['views'].form.fields; + var notebook=fields[get_fields[i].attrs.name].views.form.arch; + } + } } } - if(!$('[id^="oe_form_'+listid+result.fields[relational].relation+'"]').html()){ - $('
').appendTo('#moe'); - for (var k = 0; k < notebooks.children.length; k++) { - if (notebooks.children[k].attrs.string == lastid) { - get_fields = self.get_fields(notebooks.children[k].children); - for (var i = 0; i < get_fields.length; i++) { - if (fields[get_fields[i].attrs.name].type == 'one2many'){ - self.relational_fields = get_fields[i].attrs.name; - if(fields[get_fields[i].attrs.name].views.form){ - var get_fields_test = self.get_fields(fields[get_fields[i].attrs.name].views.form.arch.children); - var fields_test = fields[get_fields[i].attrs.name]['views'].form.fields; - var notebook=fields[get_fields[i].attrs.name].views.form.arch; - } - } - } - } - } - $('[id^="oe_form_'+listid+result.fields[relational].relation+'"]').html(self.render({'get_fields': get_fields_test, 'notebooks': false, 'fields' : fields_test, 'values' : data_relational, 'temp_flag':'1' })); - $('[id^="oe_form_'+listid+result.fields[relational].relation+'"]').find("[data-role=header]").find('h1').html(head_title); - $('[id^="oe_form_'+listid+result.fields[relational].relation+'"]').find("[data-role=header]").find('#home').click(function(){ - $.mobile.changePage("#oe_menu", "slide", false, true); - }); - $('[id^="oe_form_'+listid+result.fields[relational].relation+'"]').find("[data-role=footer]").find('#shrotcuts').click(function(){ - if(!$('#oe_shortcuts').html().length){ - this.shortcuts = new openerp.web_mobile.Shortcuts(self, "oe_shortcuts"); - this.shortcuts.start(); - }else{ - $.mobile.changePage("#oe_shortcuts", "slide", false, true); - } - }); - $('[id^="oe_form_'+listid+result.fields[relational].relation+'"]').find("[data-role=footer]").find('#preference').click(function(){ - if(!$('#oe_options').html().length){ - this.options = new openerp.web_mobile.Options(self, "oe_options"); - this.options.start(); - }else{ - $.mobile.changePage("#oe_options", "slide", false, true); - } - }); - $('[id^="oe_form_'+listid+result.fields[relational].relation+'"]').find('#formbutton').click(function(){ - var head = $(this).prev().find('select').find("option:selected").text(); - var selected_id = $(this).prev().find('select').val(); - var select_model = $(this).prev().find('select').attr('for'); - if(selected_id!="false"){ - if(!$('[id^="oe_form_'+selected_id+select_model+'"]').html()){ - $('
').appendTo('#moe'); - this.formview = new openerp.web_mobile.FormView(self, "oe_form_"+selected_id+select_model, selected_id, '', head, select_model, false); - this.formview.start(); - }else{ - $.mobile.changePage('#oe_form_'+selected_id+select_model, "slide", false, true); - } - } - }); - $.mobile.changePage('#oe_form_'+listid+result.fields[relational].relation, "slide", false, true); + } + $('[id^="oe_form_'+listid+result.fields[relational].relation+'"]').html(self.render({'get_fields': get_fields_test, 'notebooks': false, 'fields' : fields_test, 'values' : data_relational, 'temp_flag':'1' })); + $('[id^="oe_form_'+listid+result.fields[relational].relation+'"]').find("[data-role=header]").find('h1').html(head_title); + $('[id^="oe_form_'+listid+result.fields[relational].relation+'"]').find("[data-role=header]").find('#home').click(function(){ + $.mobile.changePage("#oe_menu", "slide", false, true); + }); + $('[id^="oe_form_'+listid+result.fields[relational].relation+'"]').find("[data-role=footer]").find('#shrotcuts').click(function(){ + if(!$('#oe_shortcuts').html().length){ + this.shortcuts = new openerp.web_mobile.Shortcuts(self, "oe_shortcuts"); + this.shortcuts.start(); }else{ - $.mobile.changePage('#oe_form_'+listid+result.fields[relational].relation, "slide", false, true); + $.mobile.changePage("#oe_shortcuts", "slide", false, true); } }); - }); - $.mobile.changePage("#oe_list_"+relational+"_"+self.element_id, "slide", false, true); - }else{ - $.mobile.changePage("#oe_list_"+relational+"_"+self.element_id, "slide", false, true); - } + $('[id^="oe_form_'+listid+result.fields[relational].relation+'"]').find("[data-role=footer]").find('#preference').click(function(){ + if(!$('#oe_options').html().length){ + this.options = new openerp.web_mobile.Options(self, "oe_options"); + this.options.start(); + }else{ + $.mobile.changePage("#oe_options", "slide", false, true); + } + }); + $('[id^="oe_form_'+listid+result.fields[relational].relation+'"]').find('#formbutton').click(function(){ + var head = $(this).prev().find('select').find("option:selected").text(); + var selected_id = $(this).prev().find('select').val(); + var select_model = $(this).prev().find('select').attr('for'); + if(selected_id!="false"){ + if(!$('[id^="oe_form_'+selected_id+select_model+'"]').html()){ + $('
').appendTo('#moe'); + this.formview = new openerp.web_mobile.FormView(self, "oe_form_"+selected_id+select_model, selected_id, '', head, select_model, false); + this.formview.start(); + }else{ + $.mobile.changePage('#oe_form_'+selected_id+select_model, "slide", false, true); + } + } + }); + $.mobile.changePage('#oe_form_'+listid+result.fields[relational].relation, "slide", false, true); + }else{ + $.mobile.changePage('#oe_form_'+listid+result.fields[relational].relation, "slide", false, true); + } + }); }); + $.mobile.changePage("#oe_list_"+relational+"_"+self.element_id, "slide", false, true); + }else{ + $.mobile.changePage("#oe_list_"+relational+"_"+self.element_id, "slide", false, true); } }); - $.mobile.changePage("#"+self.element_id, "slide", false, true); + } + }); + $.mobile.changePage("#"+self.element_id, "slide", false, true); }, get_fields: function(view_fields, fields) { this.fields = fields || []; From 77f51325c65673210c6468a9afaae616db744423 Mon Sep 17 00:00:00 2001 From: Jigar Amin - OpenERP Date: Fri, 30 Sep 2011 12:50:04 +0530 Subject: [PATCH 100/409] [BUG/FIX] survey : safe_evaling the dict of the stored answer in select survey wizard defalt_get bzr revid: jam@tinyerp.com-20110930072004-vkev6k0bsr6imld4 --- addons/survey/wizard/survey_answer.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/addons/survey/wizard/survey_answer.py b/addons/survey/wizard/survey_answer.py index be43a409774..332b74cc660 100644 --- a/addons/survey/wizard/survey_answer.py +++ b/addons/survey/wizard/survey_answer.py @@ -30,6 +30,7 @@ from osv import fields from tools import to_xml from tools.translate import _ import addons +from tools.safe_eval import safe_eval class survey_question_wiz(osv.osv_memory): _name = 'survey.question.wiz' @@ -522,7 +523,7 @@ class survey_question_wiz(osv.osv_memory): sur_name_read = surv_name_wiz.read(cr, uid, context.get('sur_name_id',False)) ans_list = [] - for key,val in sur_name_read['store_ans'].items(): + for key,val in safe_eval(sur_name_read.get('store_ans',"{}")).items(): for field in fields_list: if field in list(val): value[field] = val[field] From ff43f2198a8215ac63454ebed86f2538fe30daad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Valyi?= Date: Sun, 2 Oct 2011 14:39:52 -0300 Subject: [PATCH 101/409] [REF] sale: totally reworked the action_ship_create modularization: - action_ship_create now calls into a a method allowing to partition the picking - picking, moves and procurements can be customized BEFORE they are created into the database - there is no empty method - I commented in the shipment exception management as it looks buggy. I just reproduced the original code however bzr revid: rvalyi@gmail.com-20111002173952-xlcmt6u1pgj6k9k4 --- addons/sale/sale.py | 238 +++++++++++++++++++++----------------------- 1 file changed, 115 insertions(+), 123 deletions(-) diff --git a/addons/sale/sale.py b/addons/sale/sale.py index fc702181206..3e7e58c72c6 100644 --- a/addons/sale/sale.py +++ b/addons/sale/sale.py @@ -673,135 +673,127 @@ class sale_order(osv.osv): return False return canceled - def _get_order_picking_key(self, cr, uid, order, order_data, order_line, pickings_dict, context=None): - """Allows to partition a sale over several pickings: just override it and return - a unique key for the given parameters. If key changes, a new picking will be created. - :params order: the sale order - :params order_data: a place holder to store data while iterating the order lines - :params line: the sale order line - :params pickings_dict: the current dictionary of key / picking_id for this order - :return: unique key for those parameters - :rtype: string - """ - return "default" - def _create_order_picking(self, cr, uid, order, order_data, line, picking_dict, picking_vals, context=None): - picking_vals['name'] = self.pool.get('ir.sequence').get(cr, uid, 'stock.picking.out') - return self.pool.get('stock.picking').create(cr, uid, picking_vals) + def _prepare_proc(self, cr, uid, order, line, move_id, date_planned, *args): + return { + 'name': line.name, + 'origin': order.name, + 'date_planned': date_planned, + 'product_id': line.product_id.id, + 'product_qty': line.product_uom_qty, + 'product_uom': line.product_uom.id, + 'product_uos_qty': (line.product_uos and line.product_uos_qty)\ + or line.product_uom_qty, + 'product_uos': (line.product_uos and line.product_uos.id)\ + or line.product_uom.id, + 'location_id': order.shop_id.warehouse_id.lot_stock_id.id, + 'procure_method': line.type, + 'move_id': move_id, + 'property_ids': [(6, 0, [x.id for x in line.property_ids])], + 'company_id': order.company_id.id, + 'sale_line_id': line.id, + } - def _create_line_move(self, cr, uid, order, order_data, line, move_vals, context=None): - return self.pool.get('stock.move').create(cr, uid, move_vals) - def _create_line_procurement(self, cr, uid, order, order_data, line, move_id, proc_vals, context=None): - return self.pool.get('procurement.order').create(cr, uid, proc_vals) + def _prepare_move(self, cr, uid, order, line, picking_id, date_planned, *args): + location_id = order.shop_id.warehouse_id.lot_stock_id.id + output_id = order.shop_id.warehouse_id.lot_output_id.id + return { + 'name': line.name[:64], + 'picking_id': picking_id, + 'product_id': line.product_id.id, + 'date': date_planned, + 'date_expected': date_planned, + 'product_qty': line.product_uom_qty, + 'product_uom': line.product_uom.id, + 'product_uos_qty': line.product_uos_qty, + 'product_uos': (line.product_uos and line.product_uos.id)\ + or line.product_uom.id, + 'product_packaging': line.product_packaging.id, + 'address_id': line.address_allotment_id.id or order.partner_shipping_id.id, + 'location_id': location_id, + 'location_dest_id': output_id, + 'sale_line_id': line.id, + 'tracking_id': False, + 'state': 'draft', + #'state': 'waiting', + 'note': line.notes, + 'company_id': order.company_id.id, + 'price_unit': line.product_id.standard_price or 0.0 + } + + + + def _prepare_picking(self, cr, uid, order, *args): + pick_name = self.pool.get('ir.sequence').get(cr, uid, 'stock.picking.out') + return { + 'name': pick_name, + 'origin': order.name, + 'type': 'out', + 'state': 'auto', + 'move_type': order.picking_policy, + 'sale_id': order.id, + 'address_id': order.partner_shipping_id.id, + 'note': order.note, + 'invoice_state': (order.order_policy=='picking' and '2binvoiced') or 'none', + 'company_id': order.company_id.id, + } + + + def _create_pickings_an_procurements(self, cr, uid, order, order_lines, picking_id=False, *args): + proc_ids = [] + for line in order_lines: + if line.state == 'done': + continue + + date_planned = datetime.strptime(order.date_order, '%Y-%m-%d') + relativedelta(days=line.delay or 0.0) + date_planned = (date_planned - timedelta(days=order.company_id.security_lead)).strftime('%Y-%m-%d %H:%M:%S') + + if line.product_id: + if line.product_id.product_tmpl_id.type in ('product', 'consu'): + if not picking_id: + picking_id = self.pool.get('stock.picking').create(cr, uid, self._prepare_picking(cr, uid, order, args)) + move_id = self.pool.get('stock.move').create(cr, uid, self._prepare_move(cr, uid, order, line, picking_id, date_planned, args)) + else: #a service has no stock move + move_id = False + + proc_id = self.pool.get('procurement.order').create(cr, uid, self._prepare_proc(cr, uid, order, line, move_id, date_planned, args)) + proc_ids.append(proc_id) + + if order.state == 'shipping_except': #deals with potentially cancelled shipments FIXME seems broken, see below + for pick in order.picking_ids: + for move in pick.move_lines: + if move.state == 'cancel': + mov_ids = self.pool.get('stock.move').search(cr, uid, [('state', '=', 'cancel'),('sale_line_id', '=', line.id),('picking_id', '=', pick.id)]) + if mov_ids: + for mov in move_obj.browse(cr, uid, mov_ids): + #FIXME: the following seems broken: what if move_id doesn't exist? What if there are several mov_ids? Shouldn't that be a sum? + self.pool.get('stock.move').write(cr, uid, [move_id], {'product_qty': mov.product_qty, 'product_uos_qty': mov.product_uos_qty}) + self.pool.get('procurement.order').write(cr, uid, [proc_id], {'product_qty': mov.product_qty, 'product_uos_qty': mov.product_uos_qty}) + + wf_service = netsvc.LocalService("workflow") + if picking_id: + wf_service.trg_validate(uid, 'stock.picking', picking_id, 'button_confirm', cr) + + for proc_id in proc_ids: + wf_service.trg_validate(uid, 'procurement.order', proc_id, 'button_confirm', cr) + + val = {} + if order.state == 'shipping_except': + val['state'] = 'progress' + val['shipped'] = False + + if (order.order_policy == 'manual'): + for line in order.order_line: + if (not line.invoiced) and (line.state not in ('cancel', 'draft')): + val['state'] = 'manual' + break + self.write(cr, uid, [order.id], val) + return True def action_ship_create(self, cr, uid, ids, *args): - wf_service = netsvc.LocalService("workflow") - move_obj = self.pool.get('stock.move') - proc_obj = self.pool.get('procurement.order') for order in self.browse(cr, uid, ids, context={}): - output_id = order.shop_id.warehouse_id.lot_output_id.id - proc_ids = [] - pickings = {} - order_data = {} #a place holder to store data while iteraing the lines if needed - - for line in order.order_line: - date_planned = datetime.strptime(order.date_order, '%Y-%m-%d') + relativedelta(days=line.delay or 0.0) - date_planned = (date_planned - timedelta(days=order.company_id.security_lead)).strftime('%Y-%m-%d %H:%M:%S') - - if line.state == 'done': - continue - move_id = False - if line.product_id and line.product_id.product_tmpl_id.type in ('product', 'consu'): - location_id = order.shop_id.warehouse_id.lot_stock_id.id - picking_key = self._get_order_picking_key(cr, uid, order, order_data, line, pickings, *args) - picking_id = pickings.get(picking_key, False) - if not picking_id: - picking_id = self._create_order_picking(cr, uid, order, order_data, line, pickings, { - 'origin': order.name, - 'type': 'out', - 'state': 'auto', - 'move_type': order.picking_policy, - 'sale_id': order.id, - 'address_id': order.partner_shipping_id.id, - 'note': order.note, - 'invoice_state': (order.order_policy=='picking' and '2binvoiced') or 'none', - 'company_id': order.company_id.id, - }, *args) - pickings[picking_key] = picking_id - - move_id = self._create_line_move(cr, uid, order, order_data, line, { - 'name': line.name[:64], - 'picking_id': picking_id, - 'product_id': line.product_id.id, - 'date': date_planned, - 'date_expected': date_planned, - 'product_qty': line.product_uom_qty, - 'product_uom': line.product_uom.id, - 'product_uos_qty': line.product_uos_qty, - 'product_uos': (line.product_uos and line.product_uos.id)\ - or line.product_uom.id, - 'product_packaging': line.product_packaging.id, - 'address_id': line.address_allotment_id.id or order.partner_shipping_id.id, - 'location_id': location_id, - 'location_dest_id': output_id, - 'sale_line_id': line.id, - 'tracking_id': False, - 'state': 'draft', - 'note': line.notes, - 'company_id': order.company_id.id, - 'price_unit': line.product_id.standard_price or 0.0 - }, *args) - - if line.product_id: - proc_id = self._create_line_procurement(cr, uid, order, order_data, line, move_id, { - 'name': line.name, - 'origin': order.name, - 'date_planned': date_planned, - 'product_id': line.product_id.id, - 'product_qty': line.product_uom_qty, - 'product_uom': line.product_uom.id, - 'product_uos_qty': (line.product_uos and line.product_uos_qty)\ - or line.product_uom_qty, - 'product_uos': (line.product_uos and line.product_uos.id)\ - or line.product_uom.id, - 'location_id': order.shop_id.warehouse_id.lot_stock_id.id, - 'procure_method': line.type, - 'move_id': move_id, - 'property_ids': [(6, 0, [x.id for x in line.property_ids])], - 'company_id': order.company_id.id, - 'sale_line_id': line.id, - }, *args) - proc_ids.append(proc_id) - self.pool.get('sale.order.line').write(cr, uid, [line.id], {'procurement_id': proc_id}) - if order.state == 'shipping_except': - for pick in order.picking_ids: - for move in pick.move_lines: - if move.state == 'cancel': - mov_ids = move_obj.search(cr, uid, [('state', '=', 'cancel'),('sale_line_id', '=', line.id),('picking_id', '=', pick.id)]) - if mov_ids: - for mov in move_obj.browse(cr, uid, mov_ids): - move_obj.write(cr, uid, [move_id], {'product_qty': mov.product_qty, 'product_uos_qty': mov.product_uos_qty}) - proc_obj.write(cr, uid, [proc_id], {'product_qty': mov.product_qty, 'product_uos_qty': mov.product_uos_qty}) - - val = {} - - for k, picking_id in pickings.items(): - wf_service.trg_validate(uid, 'stock.picking', picking_id, 'button_confirm', cr) - - for proc_id in proc_ids: - wf_service.trg_validate(uid, 'procurement.order', proc_id, 'button_confirm', cr) - - if order.state == 'shipping_except': - val['state'] = 'progress' - val['shipped'] = False - - if (order.order_policy == 'manual'): - for line in order.order_line: - if (not line.invoiced) and (line.state not in ('cancel', 'draft')): - val['state'] = 'manual' - break - self.write(cr, uid, [order.id], val) + self._create_pickings_an_procurements(cr, uid, order, [line for line in order.order_line], False, args) return True def action_ship_end(self, cr, uid, ids, context=None): From 0815c7b05647bba7155a024445f91ce01a3e231c Mon Sep 17 00:00:00 2001 From: Valentin Lab Date: Mon, 3 Oct 2011 09:21:42 +0200 Subject: [PATCH 102/409] [fix] remove QWeb2 instanciation which is not used anymore. bzr revid: valentin.lab@kalysto.org-20111003072142-9n7lo6y2j0xsu39o --- addons/web_mobile/static/src/web_mobile.html | 1 - 1 file changed, 1 deletion(-) diff --git a/addons/web_mobile/static/src/web_mobile.html b/addons/web_mobile/static/src/web_mobile.html index 8875d61f9eb..3900b8ba0cc 100755 --- a/addons/web_mobile/static/src/web_mobile.html +++ b/addons/web_mobile/static/src/web_mobile.html @@ -30,7 +30,6 @@ - """ + output[end_body:] - - return output - - -class HeaderTemplateEditor(openobject.templating.TemplateEditor): - templates = ['/openerp/controllers/templates/header.mako'] - - def edit(self, template, template_text): - output = super(HeaderTemplateEditor, self).edit(template, template_text) - - PAT = '
    ' - - ul = output.index(PAT) - output = output[:ul] + (""" - - """ % _('feedback')) + output[ul:] - - - return output - From 683a56683d96d0802e08a051973a9762c607a28d Mon Sep 17 00:00:00 2001 From: Christophe Simonis Date: Mon, 3 Oct 2011 16:07:59 +0200 Subject: [PATCH 105/409] [FIX] web_uservoice: hook the menu click in the correct way bzr revid: chs@openerp.com-20111003140759-b4fwjn3ux3bdb094 --- addons/web_uservoice/static/src/js/web_uservoice.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/web_uservoice/static/src/js/web_uservoice.js b/addons/web_uservoice/static/src/js/web_uservoice.js index ec1ddd65d28..48aebc233ba 100644 --- a/addons/web_uservoice/static/src/js/web_uservoice.js +++ b/addons/web_uservoice/static/src/js/web_uservoice.js @@ -24,7 +24,7 @@ instance.web_uservoice.UserVoice = instance.web.Widget.extend({ showTab: false }; - instance.webclient.menu.do_menu_click.add_last(this.do_menu_click); + instance.webclient.menu.do_menu_click.add_first(this.do_menu_click); }, start: function() { From 4ec0047127620e137acb46b5edeb17d31cc193b5 Mon Sep 17 00:00:00 2001 From: niv-openerp Date: Mon, 3 Oct 2011 16:21:08 +0200 Subject: [PATCH 106/409] [imp] now stop to use coffee script bzr revid: nicolas.vanhoren@openerp.com-20111003142108-u74bp0q4mf20izsa --- addons/point_of_sale/static/src/js/pos.coffee | 641 ------------------ addons/point_of_sale/static/src/js/pos.js | 7 +- 2 files changed, 3 insertions(+), 645 deletions(-) delete mode 100644 addons/point_of_sale/static/src/js/pos.coffee diff --git a/addons/point_of_sale/static/src/js/pos.coffee b/addons/point_of_sale/static/src/js/pos.coffee deleted file mode 100644 index f5f0a940107..00000000000 --- a/addons/point_of_sale/static/src/js/pos.coffee +++ /dev/null @@ -1,641 +0,0 @@ -db = openerp.init() - -### -Local store access. Read once from localStorage upon construction and persist on every change. -There should only be one store active at any given time to ensure data consistency. -### -class Store - constructor: -> - store = localStorage['pos'] - @data = (store && JSON.parse store) || {} - get: (key) -> - @data[key] - set: (key, value) -> - @data[key] = value - localStorage['pos'] = JSON.stringify @data - -### -Gets all the necessary data from the OpenERP web client (session, shop data etc.) -### -class Pos - constructor: -> - @session.session_login 'web-trunk-pos', 'admin', 'admin', => - $.when( - @fetch( - 'pos.category', - ['name','parent_id','child_id'] - ), - @fetch( - 'product.product', - ['name','list_price','pos_categ_id','taxes_id','img'], - [['pos_categ_id','!=','false']] - ), - @fetch( - 'account.bank.statement', - ['account_id', 'currency', 'journal_id', 'state', 'name'] - ), - @fetch( - 'account.journal', - ['auto_cash', 'check_dtls', 'currency', 'name', 'type'] - ) - ).then @build_tree - ready: $.Deferred() - session: new db.base.Session 'DEBUG' - store: new Store - fetch: (osvModel, fields, domain, callback, errorCallback) -> - callback = callback || (result) => @store.set osvModel, result - dataSetSearch = new db.base.DataSetSearch this, osvModel, null, domain - dataSetSearch.read_slice fields, 0, null, callback - push: (osvModel, record, callback, errorCallback) -> - dataSet = new db.base.DataSet(this, osvModel, null) - dataSet.create record, callback, errorCallback - categories: {} - build_tree: => - for c in @store.get 'pos.category' - @categories[c.id] = id:c.id, name:c.name, children:c.child_id, - parent:c.parent_id[0], ancestors:[c.id], subtree:[c.id] - for id, c of @categories - @current_category = c - @build_ancestors c.parent - @build_subtree c - @categories[0] = - ancestors: [] - children: c.id for c in @store.get 'pos.category' when not c.parent_id[0]? - subtree: c.id for c in @store.get 'pos.category' - @ready.resolve() - build_ancestors: (parent) -> - if parent? - @current_category.ancestors.unshift parent - @build_ancestors @categories[parent].parent - build_subtree: (category) -> - for c in category.children - @current_category.subtree.push c - @build_subtree @categories[c] - -window.pos = new Pos - -$ -> - $('#steps').buttonset() # jQuery UI buttonset - - ### - --- - Models - --- - ### - - class CashRegister extends Backbone.Model - - class CashRegisterCollection extends Backbone.Collection - model: CashRegister - - class Product extends Backbone.Model - - class ProductCollection extends Backbone.Collection - model: Product - - class Category extends Backbone.Model - - class CategoryCollection extends Backbone.Collection - model: Category - - ### - Each Order contains zero or more Orderlines (i.e. the content of the "shopping cart".) - There should only ever be one Orderline per distinct product in an Order. - To add more of the same product, just update the quantity accordingly. - The Order also contains payment information. - ### - class Orderline extends Backbone.Model - defaults: { - quantity: 1, - list_price: 0, - discount: 0 - } - incrementQuantity: -> @set quantity: (@get 'quantity') + 1 - getTotal: -> (@get 'quantity') * (@get 'list_price') * (1 - (@get 'discount')/100) - exportAsJSON: -> - result = { - qty: (@get 'quantity'), - price_unit: (@get 'list_price'), - discount: (@get 'discount'), - product_id: (@get 'id') - } - return result - - class OrderlineCollection extends Backbone.Collection - model: Orderline - - ### - Every PaymentLine has all the attributes of the corresponding CashRegister. - ### - class Paymentline extends Backbone.Model - defaults: { - amount: 0 - } - getAmount: -> @get 'amount' - exportAsJSON: -> - result = { - name: "Payment line", - statement_id: (@get 'id'), - account_id: (@get 'account_id')[0], - journal_id: (@get 'journal_id')[0], - amount: @getAmount() - } - return result - - class PaymentlineCollection extends Backbone.Collection - model: Paymentline - - class Order extends Backbone.Model - defaults: { - validated: false - } - initialize: -> - @set orderLines: new OrderlineCollection - @set paymentLines: new PaymentlineCollection - @set name: "Order " + @generateUniqueId() - generateUniqueId: -> - new Date().getTime() - addProduct: (product) -> - existing = (@get 'orderLines').get product.id - if existing? - existing.incrementQuantity() - else - (@get 'orderLines').add new Orderline product.toJSON() - addPaymentLine: (cashRegister) -> - newPaymentline = new Paymentline cashRegister - ### TODO: Should be 0 for cash-like accounts ### - newPaymentline.set amount: @getDueLeft() - (@get 'paymentLines').add newPaymentline - getName: -> - return @get 'name' - getTotal: -> - return (@get 'orderLines').reduce ((sum, orderLine) -> sum + orderLine.getTotal()), 0 - getTotalTaxExcluded: -> - return @getTotal()/1.21 - getTax: -> - return @getTotal()/1.21*0.21 - getPaidTotal: -> - return (@get 'paymentLines').reduce ((sum, paymentLine) -> sum + paymentLine.getAmount()), 0 - getChange: -> - return @getPaidTotal() - @getTotal() - getDueLeft: -> - return @getTotal() - @getPaidTotal() - exportAsJSON: -> - orderLines = [] - (@get 'orderLines').each (item) => orderLines.push [0, 0, item.exportAsJSON()] - paymentLines = [] - (@get 'paymentLines').each (item) => paymentLines.push [0, 0, item.exportAsJSON()] - result = { - name: @getName(), - amount_paid: @getPaidTotal(), - amount_total: @getTotal(), - amount_tax: @getTax(), - amount_return: @getChange(), - lines: orderLines, - statement_ids: paymentLines - } - return result - - class OrderCollection extends Backbone.Collection - model: Order - - class Shop extends Backbone.Model - defaults: { - cashRegisters: (new CashRegisterCollection pos.store.get('account.bank.statement')), - orders: new OrderCollection, - products: new ProductCollection - } - initialize: -> - (@get 'orders').bind 'remove', (removedOrder) => - if (@get 'orders').isEmpty() - @addAndSelectOrder new Order - if (@get 'selectedOrder') is removedOrder - @set selectedOrder: (@get 'orders').last() - addAndSelectOrder: (newOrder) -> - (@get 'orders').add newOrder - @set selectedOrder: newOrder - - - ### - The numpad handles both the choice of the property currently being modified - (quantity, price or discount) and the edition of the corresponding numeric value. - ### - class NumpadState extends Backbone.Model - defaults: { - buffer: "0" - mode: "quantity" - } - initialize: (options) -> - @shop = options.shop - @shop.bind 'change:selectedOrder', @reset, this - appendNewChar: (newChar) -> - oldBuffer = @get 'buffer' - if oldBuffer is '0' - @set buffer: newChar - else if oldBuffer is '-0' - @set buffer: "-" + newChar - else - @set buffer: (@get 'buffer') + newChar - @updateTarget() - deleteLastChar: -> - tempNewBuffer = (@get 'buffer').slice(0, -1) || "0" - if isNaN tempNewBuffer - tempNewBuffer = "0" - @set buffer: tempNewBuffer - @updateTarget() - switchSign: -> - oldBuffer = @get 'buffer' - @set buffer: if oldBuffer[0] is '-' then oldBuffer.substr 1 else "-" + oldBuffer - @updateTarget() - changeMode: (newMode) -> - @set buffer: "0", mode: newMode - reset: -> - @set buffer: "0" - updateTarget: -> - bufferContent = @get 'buffer' - if bufferContent && !isNaN bufferContent - params = {} - params[@get 'mode'] = parseFloat bufferContent - (@shop.get 'selectedOrder').selected.set params - - ### - --- - Views - --- - ### - - class NumpadView extends Backbone.View - initialize: (options) -> - @state = options.state - events: { - 'click button#numpad-backspace': 'clickDeleteLastChar', - 'click button#numpad-minus': 'clickSwitchSign', - 'click button.number-char': 'clickAppendNewChar', - 'click button.mode-button': 'clickChangeMode' - } - clickDeleteLastChar: -> - @state.deleteLastChar() - clickSwitchSign: -> - @state.switchSign() - clickAppendNewChar: (event) -> - newChar = event.currentTarget.innerText - @state.appendNewChar newChar - clickChangeMode: (event) -> - $('.selected-mode').removeClass 'selected-mode' - $(event.currentTarget).addClass 'selected-mode' - newMode = event.currentTarget.attributes['data-mode'].nodeValue - @state.changeMode newMode - - ### - Gives access to the payment methods (aka. 'cash registers') - ### - class PaypadView extends Backbone.View - initialize: (options) -> - @shop = options.shop - events: { - 'click button': 'performPayment' - } - performPayment: (event) -> - cashRegisterId = event.currentTarget.attributes['cash-register-id'].nodeValue - cashRegisterCollection = (@shop.get 'cashRegisters') - cashRegister = cashRegisterCollection.find (item) => (item.get 'id') is parseInt cashRegisterId, 10 - (@shop.get 'selectedOrder').addPaymentLine cashRegister - render: -> - $(@el).empty() - (@shop.get 'cashRegisters').each (cashRegister) => $(@el).append (new PaymentButtonView model: cashRegister).render() - - class PaymentButtonView extends Backbone.View - template: _.template $('#payment-button-template').html() - render: -> - $(@el).html @template {id: (@model.get 'id'), name: (@model.get 'journal_id')[1]} - - - ### - There are 3 steps in a POS workflow: - 1. prepare the order (i.e. chose products, quantities etc.) - 2. choose payment method(s) and amount(s) - 3. validae order and print receipt - It should be possible to go back to any step as long as step 3 hasn't been completed. - Modifying an order after validation shouldn't be allowed. - ### - class StepsView extends Backbone.View - initialize: (options) -> - @step = "products" - events: { - 'click input.step-button': 'clickChangeStep' - } - clickChangeStep: (event) -> - newStep = event.currentTarget.attributes['data-step'].nodeValue - $('.step-screen').hide() - $('#' + newStep + '-screen').show() - @step = newStep - - ### - Shopping carts. - ### - class OrderlineView extends Backbone.View - tagName: 'tr' - template: _.template $('#orderline-template').html() - initialize: (options) -> - @model.bind 'change', => $(@el).hide(); @render() - @model.bind 'remove', => $(@el).remove() - @order = options.order - @numpadState = options.numpadState - events: { - 'click': 'clickHandler' - } - clickHandler: -> - @numpadState.reset() - @select() - render: -> - @select() - $(@el).html(@template @model.toJSON()).fadeIn 400, -> $('#current-order').scrollTop $(@).offset().top - select: -> - $('tr.selected').removeClass 'selected' - $(@el).addClass 'selected' - @order.selected = @model - - class OrderView extends Backbone.View - initialize: (options) -> - @shop = options.shop - @numpadState = options.numpadState - @shop.bind 'change:selectedOrder', @changeSelectedOrder, this - @bindOrderLineEvents() - changeSelectedOrder: -> - @currentOrderLines.unbind() - @bindOrderLineEvents() - @render() - bindOrderLineEvents: -> - @currentOrderLines = (@shop.get 'selectedOrder' ).get 'orderLines' - @currentOrderLines.bind 'add', @addLine, this - @currentOrderLines.bind 'change', @render, this - @currentOrderLines.bind 'remove', @render, this - addLine: (newLine) -> - $(@el).append (new OrderlineView model: newLine, order: (@shop.get 'selectedOrder'), numpadState: @numpadState).render() - @updateSummary() - render: -> - $(@el).empty() - @currentOrderLines.each (orderLine) => - $(@el).append (new OrderlineView model: orderLine, order: (@shop.get 'selectedOrder'), numpadState: @numpadState).render() - @updateSummary() - updateSummary: -> - currentOrder = @shop.get 'selectedOrder' - total = currentOrder.getTotal() - totalTaxExcluded = currentOrder.getTotalTaxExcluded() - tax = currentOrder.getTax() - $('#subtotal').html(totalTaxExcluded.toFixed 2).hide().fadeIn() - $('#tax').html(tax.toFixed 2).hide().fadeIn() - $('#total').html(total.toFixed 2).hide().fadeIn() - - ### - "Products" step. - ### - class CategoryView extends Backbone.View - template: _.template $('#category-template').html() - render: (ancestors, children) -> - $(@el).html @template - breadcrumb: pos.categories[c] for c in ancestors - categories: pos.categories[c] for c in children - - class ProductView extends Backbone.View - tagName: 'li' - className: 'product' - template: _.template $('#product-template').html() - events: { - 'click a': 'addToOrder' - } - initialize: (options) -> - @shop = options.shop - addToOrder: (event) -> - ### Preserve the category URL ### - event.preventDefault() - (@shop.get 'selectedOrder').addProduct @model - render: -> - $(@el).html @template @model.toJSON() - - class ProductListView extends Backbone.View - tagName: 'ol' - className: 'product-list' - initialize: (options) -> - @shop = options.shop - (@shop.get 'products').bind 'reset', @render, this - render: -> - $(@el).empty() - (@shop.get 'products').each (product) => $(@el).append (new ProductView model: product, shop: @shop).render() - $('#products-screen').append @el - - ### - "Payment" step. - ### - class PaymentlineView extends Backbone.View - tagName: 'li' - className: 'paymentline' - template: _.template $('#paymentline-template').html() - initialize: -> - @model.bind 'change', @render, this - events: { - 'keyup input': 'changeAmount' - } - changeAmount: (event) -> - newAmount = event.currentTarget.value - if newAmount && !isNaN(newAmount) - @model.set amount: parseFloat(newAmount) - render: -> - $(@el).html @template {name: (@model.get 'journal_id')[1], amount: (@model.get 'amount')} - - class PaymentView extends Backbone.View - initialize: (options) -> - @shop = options.shop - @shop.bind 'change:selectedOrder', @changeSelectedOrder, this - @bindPaymentLineEvents() - @bindOrderLineEvents() - paymentLineList: -> - $(@el).find '#paymentlines' - events: { - 'click button#validate-order': 'validateCurrentOrder' - } - validateCurrentOrder: -> - currentOrder = @shop.get 'selectedOrder' - callback = => currentOrder.set validated: true - pos.push 'pos.order', currentOrder.exportAsJSON(), callback - bindPaymentLineEvents: -> - @currentPaymentLines = (@shop.get 'selectedOrder').get 'paymentLines' - @currentPaymentLines.bind 'add', @addPaymentLine, this - @currentPaymentLines.bind 'change', @render, this - @currentPaymentLines.bind 'remove', @render, this - @currentPaymentLines.bind 'all', @updatePaymentSummary, this - bindOrderLineEvents: -> - @currentOrderLines = (@shop.get 'selectedOrder').get 'orderLines' - @currentOrderLines.bind 'all', @updatePaymentSummary, this - changeSelectedOrder: -> - @currentPaymentLines.unbind() - @bindPaymentLineEvents() - @currentOrderLines.unbind() - @bindOrderLineEvents() - @render() - addPaymentLine: (newPaymentLine) -> - @paymentLineList().append (new PaymentlineView model: newPaymentLine).render() - render: -> - @paymentLineList().empty() - @currentPaymentLines.each (paymentLine) => @paymentLineList().append (new PaymentlineView model: paymentLine).render() - @updatePaymentSummary() - updatePaymentSummary: -> - currentOrder = @shop.get 'selectedOrder' - paidTotal = currentOrder.getPaidTotal() - dueTotal = currentOrder.getTotal() - $(@el).find('#payment-due-total').html dueTotal.toFixed 2 - $(@el).find('#payment-paid-total').html paidTotal.toFixed 2 - remainingAmount = dueTotal-paidTotal - remaining = if remainingAmount > 0 then "Due left: " + remainingAmount.toFixed 2 else "Change: " + (-remainingAmount).toFixed 2 - $('#payment-remaining').html remaining - - ### - "Receipt" step. - ### - class ReceiptLineView extends Backbone.View - tagName: 'li' - className: 'receiptline' - template: _.template $('#receiptline-template').html() - initialize: -> - @model.bind 'change', @render, this - render: -> - $(@el).html @template @model.toJSON() - - class ReceiptView extends Backbone.View - initialize: (options) -> - @shop = options.shop - @shop.bind 'change:selectedOrder', @changeSelectedOrder, this - @bindOrderLineEvents() - @bindPaymentLineEvents() - receiptLineList: -> - $(@el).find('#receiptlines') - bindOrderLineEvents: -> - @currentOrderLines = (@shop.get 'selectedOrder').get 'orderLines' - @currentOrderLines.bind 'add', @addReceiptLine, this - @currentOrderLines.bind 'change', @render, this - @currentOrderLines.bind 'remove', @render, this - bindPaymentLineEvents: -> - @currentPaymentLines = (@shop.get 'selectedOrder').get 'paymentLines' - @currentPaymentLines.bind 'all', @updateReceiptSummary, this - changeSelectedOrder: -> - @currentOrderLines.unbind() - @bindOrderLineEvents() - @currentPaymentLines.unbind() - @bindPaymentLineEvents() - @render() - addReceiptLine: (newOrderItem) -> - @receiptLineList().append (new ReceiptLineView model: newOrderItem).render() - @updateReceiptSummary() - render: -> - @receiptLineList().empty() - @currentOrderLines.each (orderItem) => @receiptLineList().append (new ReceiptLineView model: orderItem).render() - @updateReceiptSummary() - updateReceiptSummary: -> - currentOrder = @shop.get 'selectedOrder' - total = currentOrder.getTotal() - tax = currentOrder.getTax() - change = currentOrder.getPaidTotal() - total - $('#receipt-summary-tax').html tax.toFixed 2 - $('#receipt-summary-total').html total.toFixed 2 - $('#receipt-summary-change').html change.toFixed 2 - - class OrderButtonView extends Backbone.View - tagName: 'li' - className: 'order-selector-button' - template: _.template $('#order-selector-button-template').html() - initialize: (options) -> - @order = options.order - @shop = options.shop - @order.bind 'destroy', => $(@el).remove() - @shop.bind 'change:selectedOrder', (shop) => - selectedOrder = shop.get 'selectedOrder' - if @order is selectedOrder - @setButtonSelected() - events: { - 'click button.select-order': 'selectOrder', - 'click button.close-order': 'closeOrder' - } - selectOrder: (event) -> - @shop.set selectedOrder: @order - setButtonSelected: -> - $('.selected-order').removeClass 'selected-order' - $(@el).addClass 'selected-order' - closeOrder: (event) -> - @order.destroy() - render: -> - $(@el).html @template @order.toJSON() - - class ShopView extends Backbone.View - initialize: (options) -> - @shop = options.shop - (@shop.get 'orders').bind 'add', @orderAdded, this - (@shop.get 'orders').add new Order - @numpadState = new NumpadState - shop: @shop - @productListView = new ProductListView - shop: @shop - @paypadView = new PaypadView - shop: @shop - el: $('#paypad') - @paypadView.render() - @orderView = new OrderView - shop: @shop, - numpadState: @numpadState - el: $('#current-order-content') - @paymentView = new PaymentView - shop: @shop, - el: $('#payment-screen') - @receiptView = new ReceiptView - shop: @shop, - el: $('#receipt-screen') - @numpadView = new NumpadView - state: @numpadState, - el: $('#numpad') - @stepsView = new StepsView - el: $('#steps') - events: { - 'click button#neworder-button': 'createNewOrder' - } - createNewOrder: -> - newOrder = new Order - (@shop.get 'orders').add newOrder - @shop.set selectedOrder: newOrder - orderAdded: (newOrder) -> - newOrderButton = new OrderButtonView - order: newOrder, - shop: @shop - $('#orders').append (newOrderButton).render() - newOrderButton.selectOrder() - - class App extends Backbone.Router - routes: - '': 'category' - 'category/:id': 'category' - initialize: -> - @shop = new Shop - @shopView = new ShopView - shop: @shop - el: $('body') - @categoryView = new CategoryView - category: (id = 0) -> - c = pos.categories[id] - $('#products-screen').html(@categoryView.render c.ancestors, c.children) - products = pos.store.get('product.product').filter (p) -> p.pos_categ_id[0] in c.subtree - (@shop.get 'products').reset products - $('.searchbox input').keyup -> - s = $(@).val().toLowerCase() - if s - m = products.filter (p) -> p.name.toLowerCase().indexOf s - $('.search-clear').fadeIn() - else - m = products - $('.search-clear').fadeOut() - (@shop.get 'products').reset m - $('.search-clear').click -> - (@shop.get 'products').reset products - $('.searchbox input').val('').focus() - $('.search-clear').fadeOut() - - pos.ready.then -> - pos.app = new App - Backbone.history.start() diff --git a/addons/point_of_sale/static/src/js/pos.js b/addons/point_of_sale/static/src/js/pos.js index 04b7d9991eb..ddc98cb95d1 100644 --- a/addons/point_of_sale/static/src/js/pos.js +++ b/addons/point_of_sale/static/src/js/pos.js @@ -1,5 +1,4 @@ (function() { - var Pos, Store, db; var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, __hasProp = Object.prototype.hasOwnProperty, __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } @@ -13,12 +12,12 @@ } return -1; }; - db = openerp.init(); + var db = openerp.init(); /* Local store access. Read once from localStorage upon construction and persist on every change. There should only be one store active at any given time to ensure data consistency. */ - Store = (function() { + var Store = (function() { function Store() { var store; store = localStorage['pos']; @@ -36,7 +35,7 @@ /* Gets all the necessary data from the OpenERP web client (session, shop data etc.) */ - Pos = (function() { + var Pos = (function() { function Pos() { this.build_tree = __bind(this.build_tree, this); this.session.session_login('web-trunk-pos', 'admin', 'admin', __bind(function() { return $.when(this.fetch('pos.category', ['name', 'parent_id', 'child_id']), this.fetch('product.product', ['name', 'list_price', 'pos_categ_id', 'taxes_id', 'img'], [['pos_categ_id', '!=', 'false']]), this.fetch('account.bank.statement', ['account_id', 'currency', 'journal_id', 'state', 'name']), this.fetch('account.journal', ['auto_cash', 'check_dtls', 'currency', 'name', 'type'])).then(this.build_tree); From ec101c5a357012c37e36e683578e012176795086 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Valyi?= Date: Mon, 3 Oct 2011 11:25:54 -0300 Subject: [PATCH 107/409] [REF] sale: more explicit method names, added docstring on main non trivial method bzr revid: rvalyi@gmail.com-20111003142554-yest0alzq00h83b7 --- addons/sale/sale.py | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/addons/sale/sale.py b/addons/sale/sale.py index 3e7e58c72c6..8cf54f4057e 100644 --- a/addons/sale/sale.py +++ b/addons/sale/sale.py @@ -673,8 +673,7 @@ class sale_order(osv.osv): return False return canceled - - def _prepare_proc(self, cr, uid, order, line, move_id, date_planned, *args): + def _prepare_order_line_procurement(self, cr, uid, order, line, move_id, date_planned, *args): return { 'name': line.name, 'origin': order.name, @@ -694,8 +693,7 @@ class sale_order(osv.osv): 'sale_line_id': line.id, } - - def _prepare_move(self, cr, uid, order, line, picking_id, date_planned, *args): + def _prepare_order_line_move(self, cr, uid, order, line, picking_id, date_planned, *args): location_id = order.shop_id.warehouse_id.lot_stock_id.id output_id = order.shop_id.warehouse_id.lot_output_id.id return { @@ -722,9 +720,7 @@ class sale_order(osv.osv): 'price_unit': line.product_id.standard_price or 0.0 } - - - def _prepare_picking(self, cr, uid, order, *args): + def _prepare_order_picking(self, cr, uid, order, *args): pick_name = self.pool.get('ir.sequence').get(cr, uid, 'stock.picking.out') return { 'name': pick_name, @@ -739,8 +735,18 @@ class sale_order(osv.osv): 'company_id': order.company_id.id, } - def _create_pickings_an_procurements(self, cr, uid, order, order_lines, picking_id=False, *args): + """ + Create pickings and procurements for given order lines. Filtering the order lines allows to partition the delivery + over several pickings. + + :param cr: database cursor + :param uid: current user id + :param order: sale order object + :param order_lines: sale order line objects + :param picking_id: id of picking to use evenually + :return: True + """ proc_ids = [] for line in order_lines: if line.state == 'done': @@ -752,12 +758,12 @@ class sale_order(osv.osv): if line.product_id: if line.product_id.product_tmpl_id.type in ('product', 'consu'): if not picking_id: - picking_id = self.pool.get('stock.picking').create(cr, uid, self._prepare_picking(cr, uid, order, args)) - move_id = self.pool.get('stock.move').create(cr, uid, self._prepare_move(cr, uid, order, line, picking_id, date_planned, args)) + picking_id = self.pool.get('stock.picking').create(cr, uid, self._prepare_order_picking(cr, uid, order, args)) + move_id = self.pool.get('stock.move').create(cr, uid, self._prepare_order_line_move(cr, uid, order, line, picking_id, date_planned, args)) else: #a service has no stock move move_id = False - proc_id = self.pool.get('procurement.order').create(cr, uid, self._prepare_proc(cr, uid, order, line, move_id, date_planned, args)) + proc_id = self.pool.get('procurement.order').create(cr, uid, self._prepare_order_line_procurement(cr, uid, order, line, move_id, date_planned, args)) proc_ids.append(proc_id) if order.state == 'shipping_except': #deals with potentially cancelled shipments FIXME seems broken, see below From 3fe3aeeda69f798905dce4ba307de4b1ca7c704d Mon Sep 17 00:00:00 2001 From: niv-openerp Date: Mon, 3 Oct 2011 16:59:03 +0200 Subject: [PATCH 108/409] [imp] reformatted the generated js bzr revid: nicolas.vanhoren@openerp.com-20111003145903-yef9eigw61r2t3me --- addons/point_of_sale/static/src/js/pos.js | 2222 +++++++++++---------- 1 file changed, 1137 insertions(+), 1085 deletions(-) diff --git a/addons/point_of_sale/static/src/js/pos.js b/addons/point_of_sale/static/src/js/pos.js index ddc98cb95d1..5a685b3b4a6 100644 --- a/addons/point_of_sale/static/src/js/pos.js +++ b/addons/point_of_sale/static/src/js/pos.js @@ -1,1096 +1,1148 @@ (function() { - var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, __hasProp = Object.prototype.hasOwnProperty, __extends = function(child, parent) { - for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } - function ctor() { this.constructor = child; } - ctor.prototype = parent.prototype; - child.prototype = new ctor; - child.__super__ = parent.prototype; - return child; - }, __indexOf = Array.prototype.indexOf || function(item) { - for (var i = 0, l = this.length; i < l; i++) { - if (this[i] === item) return i; - } - return -1; - }; - var db = openerp.init(); - /* - Local store access. Read once from localStorage upon construction and persist on every change. - There should only be one store active at any given time to ensure data consistency. - */ - var Store = (function() { - function Store() { - var store; - store = localStorage['pos']; - this.data = (store && JSON.parse(store)) || {}; - } - Store.prototype.get = function(key) { - return this.data[key]; - }; - Store.prototype.set = function(key, value) { - this.data[key] = value; - return localStorage['pos'] = JSON.stringify(this.data); - }; - return Store; - })(); - /* - Gets all the necessary data from the OpenERP web client (session, shop data etc.) - */ - var Pos = (function() { - function Pos() { - this.build_tree = __bind(this.build_tree, this); this.session.session_login('web-trunk-pos', 'admin', 'admin', __bind(function() { - return $.when(this.fetch('pos.category', ['name', 'parent_id', 'child_id']), this.fetch('product.product', ['name', 'list_price', 'pos_categ_id', 'taxes_id', 'img'], [['pos_categ_id', '!=', 'false']]), this.fetch('account.bank.statement', ['account_id', 'currency', 'journal_id', 'state', 'name']), this.fetch('account.journal', ['auto_cash', 'check_dtls', 'currency', 'name', 'type'])).then(this.build_tree); - }, this)); - } - Pos.prototype.ready = $.Deferred(); - Pos.prototype.session = new db.base.Session('DEBUG'); - Pos.prototype.store = new Store; - Pos.prototype.fetch = function(osvModel, fields, domain, callback, errorCallback) { - var dataSetSearch; - callback = callback || __bind(function(result) { - return this.store.set(osvModel, result); - }, this); - dataSetSearch = new db.base.DataSetSearch(this, osvModel, null, domain); - return dataSetSearch.read_slice(fields, 0, null, callback); - }; - Pos.prototype.push = function(osvModel, record, callback, errorCallback) { - var dataSet; - dataSet = new db.base.DataSet(this, osvModel, null); - return dataSet.create(record, callback, errorCallback); - }; - Pos.prototype.categories = {}; - Pos.prototype.build_tree = function() { - var c, id, _i, _len, _ref, _ref2; - _ref = this.store.get('pos.category'); - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - c = _ref[_i]; - this.categories[c.id] = { - id: c.id, - name: c.name, - children: c.child_id, - parent: c.parent_id[0], - ancestors: [c.id], - subtree: [c.id] + /* Some utility functions defined by Coffee script */ + var __bind = function(fn, me) { + return function() { + return fn.apply(me, arguments); }; - } - _ref2 = this.categories; - for (id in _ref2) { - c = _ref2[id]; - this.current_category = c; - this.build_ancestors(c.parent); - this.build_subtree(c); - } - this.categories[0] = { - ancestors: [], - children: (function() { - var _j, _len2, _ref3, _results; - _ref3 = this.store.get('pos.category'); - _results = []; - for (_j = 0, _len2 = _ref3.length; _j < _len2; _j++) { - c = _ref3[_j]; - if (!(c.parent_id[0] != null)) { - _results.push(c.id); + }; + var __hasProp = Object.prototype.hasOwnProperty; + var __extends = function(child, parent) { + for (var key in parent) { + if (__hasProp.call(parent, key)) + child[key] = parent[key]; + } + function ctor() { + this.constructor = child; + } + + ctor.prototype = parent.prototype; + child.prototype = new ctor; + child.__super__ = parent.prototype; + return child; + }; + var __indexOf = Array.prototype.indexOf || + function(item) { + for (var i = 0, l = this.length; i < l; i++) { + if (this[i] === item) + return i; + } + return -1; + }; + /* end */ + + var db = openerp.init(); + /* + Local store access. Read once from localStorage upon construction and persist on every change. + There should only be one store active at any given time to ensure data consistency. + */ + var Store = (function() { + function Store() { + var store; + store = localStorage['pos']; + this.data = (store && JSON.parse(store)) || {}; + } + + Store.prototype.get = function(key) { + return this.data[key]; + }; + Store.prototype.set = function(key, value) { + this.data[key] = value; + return localStorage['pos'] = JSON.stringify(this.data); + }; + return Store; + })(); + /* + Gets all the necessary data from the OpenERP web client (session, shop data etc.) + */ + var Pos = (function() { + function Pos() { + this.build_tree = __bind(this.build_tree, this); + this.session.session_login('web-trunk-pos', 'admin', 'admin', __bind( function() { + return $.when(this.fetch('pos.category', ['name', 'parent_id', 'child_id']), this.fetch('product.product', ['name', 'list_price', 'pos_categ_id', 'taxes_id', 'img'], [['pos_categ_id', '!=', 'false']]), this.fetch('account.bank.statement', ['account_id', 'currency', 'journal_id', 'state', 'name']), this.fetch('account.journal', ['auto_cash', 'check_dtls', 'currency', 'name', 'type'])).then(this.build_tree); + }, this)); + } + + Pos.prototype.ready = $.Deferred(); + Pos.prototype.session = new db.base.Session('DEBUG'); + Pos.prototype.store = new Store; + Pos.prototype.fetch = function(osvModel, fields, domain, callback, errorCallback) { + var dataSetSearch; + callback = callback || __bind( function(result) { + return this.store.set(osvModel, result); + }, this); + dataSetSearch = new db.base.DataSetSearch(this, osvModel, null, domain); + return dataSetSearch.read_slice(fields, 0, null, callback); + }; + Pos.prototype.push = function(osvModel, record, callback, errorCallback) { + var dataSet; + dataSet = new db.base.DataSet(this, osvModel, null); + return dataSet.create(record, callback, errorCallback); + }; + Pos.prototype.categories = {}; + Pos.prototype.build_tree = function() { + var c, id, _i, _len, _ref, _ref2; + _ref = this.store.get('pos.category'); + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + c = _ref[_i]; + this.categories[c.id] = { + id: c.id, + name: c.name, + children: c.child_id, + parent: c.parent_id[0], + ancestors: [c.id], + subtree: [c.id] + }; } - } - return _results; - }).call(this), - subtree: (function() { - var _j, _len2, _ref3, _results; - _ref3 = this.store.get('pos.category'); - _results = []; - for (_j = 0, _len2 = _ref3.length; _j < _len2; _j++) { - c = _ref3[_j]; - _results.push(c.id); - } - return _results; - }).call(this) - }; - return this.ready.resolve(); - }; - Pos.prototype.build_ancestors = function(parent) { - if (parent != null) { - this.current_category.ancestors.unshift(parent); - return this.build_ancestors(this.categories[parent].parent); - } - }; - Pos.prototype.build_subtree = function(category) { - var c, _i, _len, _ref, _results; - _ref = category.children; - _results = []; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - c = _ref[_i]; - this.current_category.subtree.push(c); - _results.push(this.build_subtree(this.categories[c])); - } - return _results; - }; - return Pos; - })(); - window.pos = new Pos; - $(function() { - var App, CashRegister, CashRegisterCollection, Category, CategoryCollection, CategoryView, NumpadState, NumpadView, Order, OrderButtonView, OrderCollection, OrderView, Orderline, OrderlineCollection, OrderlineView, PaymentButtonView, PaymentView, Paymentline, PaymentlineCollection, PaymentlineView, PaypadView, Product, ProductCollection, ProductListView, ProductView, ReceiptLineView, ReceiptView, Shop, ShopView, StepsView; - $('#steps').buttonset(); - /* - --- - Models - --- - */ - CashRegister = (function() { - __extends(CashRegister, Backbone.Model); - function CashRegister() { - CashRegister.__super__.constructor.apply(this, arguments); - } - return CashRegister; - })(); - CashRegisterCollection = (function() { - __extends(CashRegisterCollection, Backbone.Collection); - function CashRegisterCollection() { - CashRegisterCollection.__super__.constructor.apply(this, arguments); - } - CashRegisterCollection.prototype.model = CashRegister; - return CashRegisterCollection; - })(); - Product = (function() { - __extends(Product, Backbone.Model); - function Product() { - Product.__super__.constructor.apply(this, arguments); - } - return Product; - })(); - ProductCollection = (function() { - __extends(ProductCollection, Backbone.Collection); - function ProductCollection() { - ProductCollection.__super__.constructor.apply(this, arguments); - } - ProductCollection.prototype.model = Product; - return ProductCollection; - })(); - Category = (function() { - __extends(Category, Backbone.Model); - function Category() { - Category.__super__.constructor.apply(this, arguments); - } - return Category; - })(); - CategoryCollection = (function() { - __extends(CategoryCollection, Backbone.Collection); - function CategoryCollection() { - CategoryCollection.__super__.constructor.apply(this, arguments); - } - CategoryCollection.prototype.model = Category; - return CategoryCollection; - })(); - /* - Each Order contains zero or more Orderlines (i.e. the content of the "shopping cart".) - There should only ever be one Orderline per distinct product in an Order. - To add more of the same product, just update the quantity accordingly. - The Order also contains payment information. - */ - Orderline = (function() { - __extends(Orderline, Backbone.Model); - function Orderline() { - Orderline.__super__.constructor.apply(this, arguments); - } - Orderline.prototype.defaults = { - quantity: 1, - list_price: 0, - discount: 0 - }; - Orderline.prototype.incrementQuantity = function() { - return this.set({ - quantity: (this.get('quantity')) + 1 - }); - }; - Orderline.prototype.getTotal = function() { - return (this.get('quantity')) * (this.get('list_price')) * (1 - (this.get('discount')) / 100); - }; - Orderline.prototype.exportAsJSON = function() { - var result; - result = { - qty: this.get('quantity'), - price_unit: this.get('list_price'), - discount: this.get('discount'), - product_id: this.get('id') + _ref2 = this.categories; + for (id in _ref2) { + c = _ref2[id]; + this.current_category = c; + this.build_ancestors(c.parent); + this.build_subtree(c); + } + this.categories[0] = { + ancestors: [], + children: (function() { + var _j, _len2, _ref3, _results; + _ref3 = this.store.get('pos.category'); + _results = []; + for (_j = 0, _len2 = _ref3.length; _j < _len2; _j++) { + c = _ref3[_j]; + if (!(c.parent_id[0] != null)) { + _results.push(c.id); + } + } + return _results; + }).call(this), + subtree: (function() { + var _j, _len2, _ref3, _results; + _ref3 = this.store.get('pos.category'); + _results = []; + for (_j = 0, _len2 = _ref3.length; _j < _len2; _j++) { + c = _ref3[_j]; + _results.push(c.id); + } + return _results; + }).call(this) + }; + return this.ready.resolve(); }; - return result; - }; - return Orderline; - })(); - OrderlineCollection = (function() { - __extends(OrderlineCollection, Backbone.Collection); - function OrderlineCollection() { - OrderlineCollection.__super__.constructor.apply(this, arguments); - } - OrderlineCollection.prototype.model = Orderline; - return OrderlineCollection; - })(); - /* - Every PaymentLine has all the attributes of the corresponding CashRegister. - */ - Paymentline = (function() { - __extends(Paymentline, Backbone.Model); - function Paymentline() { - Paymentline.__super__.constructor.apply(this, arguments); - } - Paymentline.prototype.defaults = { - amount: 0 - }; - Paymentline.prototype.getAmount = function() { - return this.get('amount'); - }; - Paymentline.prototype.exportAsJSON = function() { - var result; - result = { - name: "Payment line", - statement_id: this.get('id'), - account_id: (this.get('account_id'))[0], - journal_id: (this.get('journal_id'))[0], - amount: this.getAmount() + Pos.prototype.build_ancestors = function(parent) { + if (parent != null) { + this.current_category.ancestors.unshift(parent); + return this.build_ancestors(this.categories[parent].parent); + } }; - return result; - }; - return Paymentline; - })(); - PaymentlineCollection = (function() { - __extends(PaymentlineCollection, Backbone.Collection); - function PaymentlineCollection() { - PaymentlineCollection.__super__.constructor.apply(this, arguments); - } - PaymentlineCollection.prototype.model = Paymentline; - return PaymentlineCollection; - })(); - Order = (function() { - __extends(Order, Backbone.Model); - function Order() { - Order.__super__.constructor.apply(this, arguments); - } - Order.prototype.defaults = { - validated: false - }; - Order.prototype.initialize = function() { - this.set({ - orderLines: new OrderlineCollection - }); - this.set({ - paymentLines: new PaymentlineCollection - }); - return this.set({ - name: "Order " + this.generateUniqueId() - }); - }; - Order.prototype.generateUniqueId = function() { - return new Date().getTime(); - }; - Order.prototype.addProduct = function(product) { - var existing; - existing = (this.get('orderLines')).get(product.id); - if (existing != null) { - return existing.incrementQuantity(); - } else { - return (this.get('orderLines')).add(new Orderline(product.toJSON())); - } - }; - Order.prototype.addPaymentLine = function(cashRegister) { - var newPaymentline; - newPaymentline = new Paymentline(cashRegister); - /* TODO: Should be 0 for cash-like accounts */ - newPaymentline.set({ - amount: this.getDueLeft() - }); - return (this.get('paymentLines')).add(newPaymentline); - }; - Order.prototype.getName = function() { - return this.get('name'); - }; - Order.prototype.getTotal = function() { - return (this.get('orderLines')).reduce((function(sum, orderLine) { - return sum + orderLine.getTotal(); - }), 0); - }; - Order.prototype.getTotalTaxExcluded = function() { - return this.getTotal() / 1.21; - }; - Order.prototype.getTax = function() { - return this.getTotal() / 1.21 * 0.21; - }; - Order.prototype.getPaidTotal = function() { - return (this.get('paymentLines')).reduce((function(sum, paymentLine) { - return sum + paymentLine.getAmount(); - }), 0); - }; - Order.prototype.getChange = function() { - return this.getPaidTotal() - this.getTotal(); - }; - Order.prototype.getDueLeft = function() { - return this.getTotal() - this.getPaidTotal(); - }; - Order.prototype.exportAsJSON = function() { - var orderLines, paymentLines, result; - orderLines = []; - (this.get('orderLines')).each(__bind(function(item) { - return orderLines.push([0, 0, item.exportAsJSON()]); - }, this)); - paymentLines = []; - (this.get('paymentLines')).each(__bind(function(item) { - return paymentLines.push([0, 0, item.exportAsJSON()]); - }, this)); - result = { - name: this.getName(), - amount_paid: this.getPaidTotal(), - amount_total: this.getTotal(), - amount_tax: this.getTax(), - amount_return: this.getChange(), - lines: orderLines, - statement_ids: paymentLines - }; - return result; - }; - return Order; - })(); - OrderCollection = (function() { - __extends(OrderCollection, Backbone.Collection); - function OrderCollection() { - OrderCollection.__super__.constructor.apply(this, arguments); - } - OrderCollection.prototype.model = Order; - return OrderCollection; - })(); - Shop = (function() { - __extends(Shop, Backbone.Model); - function Shop() { - Shop.__super__.constructor.apply(this, arguments); - } - Shop.prototype.defaults = { - cashRegisters: new CashRegisterCollection(pos.store.get('account.bank.statement')), - orders: new OrderCollection, - products: new ProductCollection - }; - Shop.prototype.initialize = function() { - return (this.get('orders')).bind('remove', __bind(function(removedOrder) { - if ((this.get('orders')).isEmpty()) { - this.addAndSelectOrder(new Order); - } - if ((this.get('selectedOrder')) === removedOrder) { - return this.set({ - selectedOrder: (this.get('orders')).last() - }); - } - }, this)); - }; - Shop.prototype.addAndSelectOrder = function(newOrder) { - (this.get('orders')).add(newOrder); - return this.set({ - selectedOrder: newOrder - }); - }; - return Shop; - })(); - /* - The numpad handles both the choice of the property currently being modified - (quantity, price or discount) and the edition of the corresponding numeric value. - */ - NumpadState = (function() { - __extends(NumpadState, Backbone.Model); - function NumpadState() { - NumpadState.__super__.constructor.apply(this, arguments); - } - NumpadState.prototype.defaults = { - buffer: "0", - mode: "quantity" - }; - NumpadState.prototype.initialize = function(options) { - this.shop = options.shop; - return this.shop.bind('change:selectedOrder', this.reset, this); - }; - NumpadState.prototype.appendNewChar = function(newChar) { - var oldBuffer; - oldBuffer = this.get('buffer'); - if (oldBuffer === '0') { - this.set({ - buffer: newChar - }); - } else if (oldBuffer === '-0') { - this.set({ - buffer: "-" + newChar - }); - } else { - this.set({ - buffer: (this.get('buffer')) + newChar - }); - } - return this.updateTarget(); - }; - NumpadState.prototype.deleteLastChar = function() { - var tempNewBuffer; - tempNewBuffer = (this.get('buffer')).slice(0, -1) || "0"; - if (isNaN(tempNewBuffer)) { - tempNewBuffer = "0"; - } - this.set({ - buffer: tempNewBuffer - }); - return this.updateTarget(); - }; - NumpadState.prototype.switchSign = function() { - var oldBuffer; - oldBuffer = this.get('buffer'); - this.set({ - buffer: oldBuffer[0] === '-' ? oldBuffer.substr(1) : "-" + oldBuffer - }); - return this.updateTarget(); - }; - NumpadState.prototype.changeMode = function(newMode) { - return this.set({ - buffer: "0", - mode: newMode - }); - }; - NumpadState.prototype.reset = function() { - return this.set({ - buffer: "0" - }); - }; - NumpadState.prototype.updateTarget = function() { - var bufferContent, params; - bufferContent = this.get('buffer'); - if (bufferContent && !isNaN(bufferContent)) { - params = {}; - params[this.get('mode')] = parseFloat(bufferContent); - return (this.shop.get('selectedOrder')).selected.set(params); - } - }; - return NumpadState; - })(); - /* - --- - Views - --- - */ - NumpadView = (function() { - __extends(NumpadView, Backbone.View); - function NumpadView() { - NumpadView.__super__.constructor.apply(this, arguments); - } - NumpadView.prototype.initialize = function(options) { - return this.state = options.state; - }; - NumpadView.prototype.events = { - 'click button#numpad-backspace': 'clickDeleteLastChar', - 'click button#numpad-minus': 'clickSwitchSign', - 'click button.number-char': 'clickAppendNewChar', - 'click button.mode-button': 'clickChangeMode' - }; - NumpadView.prototype.clickDeleteLastChar = function() { - return this.state.deleteLastChar(); - }; - NumpadView.prototype.clickSwitchSign = function() { - return this.state.switchSign(); - }; - NumpadView.prototype.clickAppendNewChar = function(event) { - var newChar; - newChar = event.currentTarget.innerText; - return this.state.appendNewChar(newChar); - }; - NumpadView.prototype.clickChangeMode = function(event) { - var newMode; - $('.selected-mode').removeClass('selected-mode'); - $(event.currentTarget).addClass('selected-mode'); - newMode = event.currentTarget.attributes['data-mode'].nodeValue; - return this.state.changeMode(newMode); - }; - return NumpadView; - })(); - /* - Gives access to the payment methods (aka. 'cash registers') - */ - PaypadView = (function() { - __extends(PaypadView, Backbone.View); - function PaypadView() { - PaypadView.__super__.constructor.apply(this, arguments); - } - PaypadView.prototype.initialize = function(options) { - return this.shop = options.shop; - }; - PaypadView.prototype.events = { - 'click button': 'performPayment' - }; - PaypadView.prototype.performPayment = function(event) { - var cashRegister, cashRegisterCollection, cashRegisterId; - cashRegisterId = event.currentTarget.attributes['cash-register-id'].nodeValue; - cashRegisterCollection = this.shop.get('cashRegisters'); - cashRegister = cashRegisterCollection.find(__bind(function(item) { - return (item.get('id')) === parseInt(cashRegisterId, 10); - }, this)); - return (this.shop.get('selectedOrder')).addPaymentLine(cashRegister); - }; - PaypadView.prototype.render = function() { - $(this.el).empty(); - return (this.shop.get('cashRegisters')).each(__bind(function(cashRegister) { - return $(this.el).append((new PaymentButtonView({ - model: cashRegister - })).render()); - }, this)); - }; - return PaypadView; - })(); - PaymentButtonView = (function() { - __extends(PaymentButtonView, Backbone.View); - function PaymentButtonView() { - PaymentButtonView.__super__.constructor.apply(this, arguments); - } - PaymentButtonView.prototype.template = _.template($('#payment-button-template').html()); - PaymentButtonView.prototype.render = function() { - return $(this.el).html(this.template({ - id: this.model.get('id'), - name: (this.model.get('journal_id'))[1] - })); - }; - return PaymentButtonView; - })(); - /* - There are 3 steps in a POS workflow: - 1. prepare the order (i.e. chose products, quantities etc.) - 2. choose payment method(s) and amount(s) - 3. validae order and print receipt - It should be possible to go back to any step as long as step 3 hasn't been completed. - Modifying an order after validation shouldn't be allowed. - */ - StepsView = (function() { - __extends(StepsView, Backbone.View); - function StepsView() { - StepsView.__super__.constructor.apply(this, arguments); - } - StepsView.prototype.initialize = function(options) { - return this.step = "products"; - }; - StepsView.prototype.events = { - 'click input.step-button': 'clickChangeStep' - }; - StepsView.prototype.clickChangeStep = function(event) { - var newStep; - newStep = event.currentTarget.attributes['data-step'].nodeValue; - $('.step-screen').hide(); - $('#' + newStep + '-screen').show(); - return this.step = newStep; - }; - return StepsView; - })(); - /* - Shopping carts. - */ - OrderlineView = (function() { - __extends(OrderlineView, Backbone.View); - function OrderlineView() { - OrderlineView.__super__.constructor.apply(this, arguments); - } - OrderlineView.prototype.tagName = 'tr'; - OrderlineView.prototype.template = _.template($('#orderline-template').html()); - OrderlineView.prototype.initialize = function(options) { - this.model.bind('change', __bind(function() { - $(this.el).hide(); - return this.render(); - }, this)); - this.model.bind('remove', __bind(function() { - return $(this.el).remove(); - }, this)); - this.order = options.order; - return this.numpadState = options.numpadState; - }; - OrderlineView.prototype.events = { - 'click': 'clickHandler' - }; - OrderlineView.prototype.clickHandler = function() { - this.numpadState.reset(); - return this.select(); - }; - OrderlineView.prototype.render = function() { - this.select(); - return $(this.el).html(this.template(this.model.toJSON())).fadeIn(400, function() { - return $('#current-order').scrollTop($(this).offset().top); - }); - }; - OrderlineView.prototype.select = function() { - $('tr.selected').removeClass('selected'); - $(this.el).addClass('selected'); - return this.order.selected = this.model; - }; - return OrderlineView; - })(); - OrderView = (function() { - __extends(OrderView, Backbone.View); - function OrderView() { - OrderView.__super__.constructor.apply(this, arguments); - } - OrderView.prototype.initialize = function(options) { - this.shop = options.shop; - this.numpadState = options.numpadState; - this.shop.bind('change:selectedOrder', this.changeSelectedOrder, this); - return this.bindOrderLineEvents(); - }; - OrderView.prototype.changeSelectedOrder = function() { - this.currentOrderLines.unbind(); - this.bindOrderLineEvents(); - return this.render(); - }; - OrderView.prototype.bindOrderLineEvents = function() { - this.currentOrderLines = (this.shop.get('selectedOrder')).get('orderLines'); - this.currentOrderLines.bind('add', this.addLine, this); - this.currentOrderLines.bind('change', this.render, this); - return this.currentOrderLines.bind('remove', this.render, this); - }; - OrderView.prototype.addLine = function(newLine) { - $(this.el).append((new OrderlineView({ - model: newLine, - order: this.shop.get('selectedOrder'), - numpadState: this.numpadState - })).render()); - return this.updateSummary(); - }; - OrderView.prototype.render = function() { - $(this.el).empty(); - this.currentOrderLines.each(__bind(function(orderLine) { - return $(this.el).append((new OrderlineView({ - model: orderLine, - order: this.shop.get('selectedOrder'), - numpadState: this.numpadState - })).render()); - }, this)); - return this.updateSummary(); - }; - OrderView.prototype.updateSummary = function() { - var currentOrder, tax, total, totalTaxExcluded; - currentOrder = this.shop.get('selectedOrder'); - total = currentOrder.getTotal(); - totalTaxExcluded = currentOrder.getTotalTaxExcluded(); - tax = currentOrder.getTax(); - $('#subtotal').html(totalTaxExcluded.toFixed(2)).hide().fadeIn(); - $('#tax').html(tax.toFixed(2)).hide().fadeIn(); - return $('#total').html(total.toFixed(2)).hide().fadeIn(); - }; - return OrderView; - })(); - /* - "Products" step. - */ - CategoryView = (function() { - __extends(CategoryView, Backbone.View); - function CategoryView() { - CategoryView.__super__.constructor.apply(this, arguments); - } - CategoryView.prototype.template = _.template($('#category-template').html()); - CategoryView.prototype.render = function(ancestors, children) { - var c; - return $(this.el).html(this.template({ - breadcrumb: (function() { - var _i, _len, _results; + Pos.prototype.build_subtree = function(category) { + var c, _i, _len, _ref, _results; + _ref = category.children; _results = []; - for (_i = 0, _len = ancestors.length; _i < _len; _i++) { - c = ancestors[_i]; - _results.push(pos.categories[c]); + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + c = _ref[_i]; + this.current_category.subtree.push(c); + _results.push(this.build_subtree(this.categories[c])); } return _results; - })(), - categories: (function() { - var _i, _len, _results; - _results = []; - for (_i = 0, _len = children.length; _i < _len; _i++) { - c = children[_i]; - _results.push(pos.categories[c]); + }; + return Pos; + })(); + window.pos = new Pos; + $( function() { + var App, CashRegister, CashRegisterCollection, Category, CategoryCollection, CategoryView, NumpadState, NumpadView, Order, OrderButtonView, OrderCollection, OrderView, Orderline, OrderlineCollection, OrderlineView, PaymentButtonView, PaymentView, Paymentline, PaymentlineCollection, PaymentlineView, PaypadView, Product, ProductCollection, ProductListView, ProductView, ReceiptLineView, ReceiptView, Shop, ShopView, StepsView; + $('#steps').buttonset(); + /* + --- + Models + --- + */ + CashRegister = (function() { + __extends(CashRegister, Backbone.Model); + function CashRegister() { + CashRegister.__super__.constructor.apply(this, arguments); } - return _results; - })() - })); - }; - return CategoryView; - })(); - ProductView = (function() { - __extends(ProductView, Backbone.View); - function ProductView() { - ProductView.__super__.constructor.apply(this, arguments); - } - ProductView.prototype.tagName = 'li'; - ProductView.prototype.className = 'product'; - ProductView.prototype.template = _.template($('#product-template').html()); - ProductView.prototype.events = { - 'click a': 'addToOrder' - }; - ProductView.prototype.initialize = function(options) { - return this.shop = options.shop; - }; - ProductView.prototype.addToOrder = function(event) { - /* Preserve the category URL */ event.preventDefault(); - return (this.shop.get('selectedOrder')).addProduct(this.model); - }; - ProductView.prototype.render = function() { - return $(this.el).html(this.template(this.model.toJSON())); - }; - return ProductView; - })(); - ProductListView = (function() { - __extends(ProductListView, Backbone.View); - function ProductListView() { - ProductListView.__super__.constructor.apply(this, arguments); - } - ProductListView.prototype.tagName = 'ol'; - ProductListView.prototype.className = 'product-list'; - ProductListView.prototype.initialize = function(options) { - this.shop = options.shop; - return (this.shop.get('products')).bind('reset', this.render, this); - }; - ProductListView.prototype.render = function() { - $(this.el).empty(); - (this.shop.get('products')).each(__bind(function(product) { - return $(this.el).append((new ProductView({ - model: product, - shop: this.shop - })).render()); - }, this)); - return $('#products-screen').append(this.el); - }; - return ProductListView; - })(); - /* - "Payment" step. - */ - PaymentlineView = (function() { - __extends(PaymentlineView, Backbone.View); - function PaymentlineView() { - PaymentlineView.__super__.constructor.apply(this, arguments); - } - PaymentlineView.prototype.tagName = 'li'; - PaymentlineView.prototype.className = 'paymentline'; - PaymentlineView.prototype.template = _.template($('#paymentline-template').html()); - PaymentlineView.prototype.initialize = function() { - return this.model.bind('change', this.render, this); - }; - PaymentlineView.prototype.events = { - 'keyup input': 'changeAmount' - }; - PaymentlineView.prototype.changeAmount = function(event) { - var newAmount; - newAmount = event.currentTarget.value; - if (newAmount && !isNaN(newAmount)) { - return this.model.set({ - amount: parseFloat(newAmount) - }); - } - }; - PaymentlineView.prototype.render = function() { - return $(this.el).html(this.template({ - name: (this.model.get('journal_id'))[1], - amount: this.model.get('amount') - })); - }; - return PaymentlineView; - })(); - PaymentView = (function() { - __extends(PaymentView, Backbone.View); - function PaymentView() { - PaymentView.__super__.constructor.apply(this, arguments); - } - PaymentView.prototype.initialize = function(options) { - this.shop = options.shop; - this.shop.bind('change:selectedOrder', this.changeSelectedOrder, this); - this.bindPaymentLineEvents(); - return this.bindOrderLineEvents(); - }; - PaymentView.prototype.paymentLineList = function() { - return $(this.el).find('#paymentlines'); - }; - PaymentView.prototype.events = { - 'click button#validate-order': 'validateCurrentOrder' - }; - PaymentView.prototype.validateCurrentOrder = function() { - var callback, currentOrder; - currentOrder = this.shop.get('selectedOrder'); - callback = __bind(function() { - return currentOrder.set({ - validated: true - }); - }, this); - return pos.push('pos.order', currentOrder.exportAsJSON(), callback); - }; - PaymentView.prototype.bindPaymentLineEvents = function() { - this.currentPaymentLines = (this.shop.get('selectedOrder')).get('paymentLines'); - this.currentPaymentLines.bind('add', this.addPaymentLine, this); - this.currentPaymentLines.bind('change', this.render, this); - this.currentPaymentLines.bind('remove', this.render, this); - return this.currentPaymentLines.bind('all', this.updatePaymentSummary, this); - }; - PaymentView.prototype.bindOrderLineEvents = function() { - this.currentOrderLines = (this.shop.get('selectedOrder')).get('orderLines'); - return this.currentOrderLines.bind('all', this.updatePaymentSummary, this); - }; - PaymentView.prototype.changeSelectedOrder = function() { - this.currentPaymentLines.unbind(); - this.bindPaymentLineEvents(); - this.currentOrderLines.unbind(); - this.bindOrderLineEvents(); - return this.render(); - }; - PaymentView.prototype.addPaymentLine = function(newPaymentLine) { - return this.paymentLineList().append((new PaymentlineView({ - model: newPaymentLine - })).render()); - }; - PaymentView.prototype.render = function() { - this.paymentLineList().empty(); - this.currentPaymentLines.each(__bind(function(paymentLine) { - return this.paymentLineList().append((new PaymentlineView({ - model: paymentLine - })).render()); - }, this)); - return this.updatePaymentSummary(); - }; - PaymentView.prototype.updatePaymentSummary = function() { - var currentOrder, dueTotal, paidTotal, remaining, remainingAmount; - currentOrder = this.shop.get('selectedOrder'); - paidTotal = currentOrder.getPaidTotal(); - dueTotal = currentOrder.getTotal(); - $(this.el).find('#payment-due-total').html(dueTotal.toFixed(2)); - $(this.el).find('#payment-paid-total').html(paidTotal.toFixed(2)); - remainingAmount = dueTotal - paidTotal; - remaining = remainingAmount > 0 ? "Due left: " + remainingAmount.toFixed(2) : "Change: " + (-remainingAmount).toFixed(2); - return $('#payment-remaining').html(remaining); - }; - return PaymentView; - })(); - /* - "Receipt" step. - */ - ReceiptLineView = (function() { - __extends(ReceiptLineView, Backbone.View); - function ReceiptLineView() { - ReceiptLineView.__super__.constructor.apply(this, arguments); - } - ReceiptLineView.prototype.tagName = 'li'; - ReceiptLineView.prototype.className = 'receiptline'; - ReceiptLineView.prototype.template = _.template($('#receiptline-template').html()); - ReceiptLineView.prototype.initialize = function() { - return this.model.bind('change', this.render, this); - }; - ReceiptLineView.prototype.render = function() { - return $(this.el).html(this.template(this.model.toJSON())); - }; - return ReceiptLineView; - })(); - ReceiptView = (function() { - __extends(ReceiptView, Backbone.View); - function ReceiptView() { - ReceiptView.__super__.constructor.apply(this, arguments); - } - ReceiptView.prototype.initialize = function(options) { - this.shop = options.shop; - this.shop.bind('change:selectedOrder', this.changeSelectedOrder, this); - this.bindOrderLineEvents(); - return this.bindPaymentLineEvents(); - }; - ReceiptView.prototype.receiptLineList = function() { - return $(this.el).find('#receiptlines'); - }; - ReceiptView.prototype.bindOrderLineEvents = function() { - this.currentOrderLines = (this.shop.get('selectedOrder')).get('orderLines'); - this.currentOrderLines.bind('add', this.addReceiptLine, this); - this.currentOrderLines.bind('change', this.render, this); - return this.currentOrderLines.bind('remove', this.render, this); - }; - ReceiptView.prototype.bindPaymentLineEvents = function() { - this.currentPaymentLines = (this.shop.get('selectedOrder')).get('paymentLines'); - return this.currentPaymentLines.bind('all', this.updateReceiptSummary, this); - }; - ReceiptView.prototype.changeSelectedOrder = function() { - this.currentOrderLines.unbind(); - this.bindOrderLineEvents(); - this.currentPaymentLines.unbind(); - this.bindPaymentLineEvents(); - return this.render(); - }; - ReceiptView.prototype.addReceiptLine = function(newOrderItem) { - this.receiptLineList().append((new ReceiptLineView({ - model: newOrderItem - })).render()); - return this.updateReceiptSummary(); - }; - ReceiptView.prototype.render = function() { - this.receiptLineList().empty(); - this.currentOrderLines.each(__bind(function(orderItem) { - return this.receiptLineList().append((new ReceiptLineView({ - model: orderItem - })).render()); - }, this)); - return this.updateReceiptSummary(); - }; - ReceiptView.prototype.updateReceiptSummary = function() { - var change, currentOrder, tax, total; - currentOrder = this.shop.get('selectedOrder'); - total = currentOrder.getTotal(); - tax = currentOrder.getTax(); - change = currentOrder.getPaidTotal() - total; - $('#receipt-summary-tax').html(tax.toFixed(2)); - $('#receipt-summary-total').html(total.toFixed(2)); - return $('#receipt-summary-change').html(change.toFixed(2)); - }; - return ReceiptView; - })(); - OrderButtonView = (function() { - __extends(OrderButtonView, Backbone.View); - function OrderButtonView() { - OrderButtonView.__super__.constructor.apply(this, arguments); - } - OrderButtonView.prototype.tagName = 'li'; - OrderButtonView.prototype.className = 'order-selector-button'; - OrderButtonView.prototype.template = _.template($('#order-selector-button-template').html()); - OrderButtonView.prototype.initialize = function(options) { - this.order = options.order; - this.shop = options.shop; - this.order.bind('destroy', __bind(function() { - return $(this.el).remove(); - }, this)); - return this.shop.bind('change:selectedOrder', __bind(function(shop) { - var selectedOrder; - selectedOrder = shop.get('selectedOrder'); - if (this.order === selectedOrder) { - return this.setButtonSelected(); - } - }, this)); - }; - OrderButtonView.prototype.events = { - 'click button.select-order': 'selectOrder', - 'click button.close-order': 'closeOrder' - }; - OrderButtonView.prototype.selectOrder = function(event) { - return this.shop.set({ - selectedOrder: this.order + + return CashRegister; + })(); + CashRegisterCollection = (function() { + __extends(CashRegisterCollection, Backbone.Collection); + function CashRegisterCollection() { + CashRegisterCollection.__super__.constructor.apply(this, arguments); + } + + CashRegisterCollection.prototype.model = CashRegister; + return CashRegisterCollection; + })(); + Product = (function() { + __extends(Product, Backbone.Model); + function Product() { + Product.__super__.constructor.apply(this, arguments); + } + + return Product; + })(); + ProductCollection = (function() { + __extends(ProductCollection, Backbone.Collection); + function ProductCollection() { + ProductCollection.__super__.constructor.apply(this, arguments); + } + + ProductCollection.prototype.model = Product; + return ProductCollection; + })(); + Category = (function() { + __extends(Category, Backbone.Model); + function Category() { + Category.__super__.constructor.apply(this, arguments); + } + + return Category; + })(); + CategoryCollection = (function() { + __extends(CategoryCollection, Backbone.Collection); + function CategoryCollection() { + CategoryCollection.__super__.constructor.apply(this, arguments); + } + + CategoryCollection.prototype.model = Category; + return CategoryCollection; + })(); + /* + Each Order contains zero or more Orderlines (i.e. the content of the "shopping cart".) + There should only ever be one Orderline per distinct product in an Order. + To add more of the same product, just update the quantity accordingly. + The Order also contains payment information. + */ + Orderline = (function() { + __extends(Orderline, Backbone.Model); + function Orderline() { + Orderline.__super__.constructor.apply(this, arguments); + } + + Orderline.prototype.defaults = { + quantity: 1, + list_price: 0, + discount: 0 + }; + Orderline.prototype.incrementQuantity = function() { + return this.set({ + quantity: (this.get('quantity')) + 1 + }); + }; + Orderline.prototype.getTotal = function() { + return (this.get('quantity')) * (this.get('list_price')) * (1 - (this.get('discount')) / 100); + }; + Orderline.prototype.exportAsJSON = function() { + var result; + result = { + qty: this.get('quantity'), + price_unit: this.get('list_price'), + discount: this.get('discount'), + product_id: this.get('id') + }; + return result; + }; + return Orderline; + })(); + OrderlineCollection = (function() { + __extends(OrderlineCollection, Backbone.Collection); + function OrderlineCollection() { + OrderlineCollection.__super__.constructor.apply(this, arguments); + } + + OrderlineCollection.prototype.model = Orderline; + return OrderlineCollection; + })(); + /* + Every PaymentLine has all the attributes of the corresponding CashRegister. + */ + Paymentline = (function() { + __extends(Paymentline, Backbone.Model); + function Paymentline() { + Paymentline.__super__.constructor.apply(this, arguments); + } + + Paymentline.prototype.defaults = { + amount: 0 + }; + Paymentline.prototype.getAmount = function() { + return this.get('amount'); + }; + Paymentline.prototype.exportAsJSON = function() { + var result; + result = { + name: "Payment line", + statement_id: this.get('id'), + account_id: (this.get('account_id'))[0], + journal_id: (this.get('journal_id'))[0], + amount: this.getAmount() + }; + return result; + }; + return Paymentline; + })(); + PaymentlineCollection = (function() { + __extends(PaymentlineCollection, Backbone.Collection); + function PaymentlineCollection() { + PaymentlineCollection.__super__.constructor.apply(this, arguments); + } + + PaymentlineCollection.prototype.model = Paymentline; + return PaymentlineCollection; + })(); + Order = (function() { + __extends(Order, Backbone.Model); + function Order() { + Order.__super__.constructor.apply(this, arguments); + } + + Order.prototype.defaults = { + validated: false + }; + Order.prototype.initialize = function() { + this.set({ + orderLines: new OrderlineCollection + }); + this.set({ + paymentLines: new PaymentlineCollection + }); + return this.set({ + name: "Order " + this.generateUniqueId() + }); + }; + Order.prototype.generateUniqueId = function() { + return new Date().getTime(); + }; + Order.prototype.addProduct = function(product) { + var existing; + existing = (this.get('orderLines')).get(product.id); + if (existing != null) { + return existing.incrementQuantity(); + } else { + return (this.get('orderLines')).add(new Orderline(product.toJSON())); + } + }; + Order.prototype.addPaymentLine = function(cashRegister) { + var newPaymentline; + newPaymentline = new Paymentline(cashRegister); + /* TODO: Should be 0 for cash-like accounts */ + newPaymentline.set({ + amount: this.getDueLeft() + }); + return (this.get('paymentLines')).add(newPaymentline); + }; + Order.prototype.getName = function() { + return this.get('name'); + }; + Order.prototype.getTotal = function() { + return (this.get('orderLines')).reduce((function(sum, orderLine) { + return sum + orderLine.getTotal(); + }), 0); + }; + Order.prototype.getTotalTaxExcluded = function() { + return this.getTotal() / 1.21; + }; + Order.prototype.getTax = function() { + return this.getTotal() / 1.21 * 0.21; + }; + Order.prototype.getPaidTotal = function() { + return (this.get('paymentLines')).reduce((function(sum, paymentLine) { + return sum + paymentLine.getAmount(); + }), 0); + }; + Order.prototype.getChange = function() { + return this.getPaidTotal() - this.getTotal(); + }; + Order.prototype.getDueLeft = function() { + return this.getTotal() - this.getPaidTotal(); + }; + Order.prototype.exportAsJSON = function() { + var orderLines, paymentLines, result; + orderLines = []; + (this.get('orderLines')).each(__bind( function(item) { + return orderLines.push([0, 0, item.exportAsJSON()]); + }, this)); + paymentLines = []; + (this.get('paymentLines')).each(__bind( function(item) { + return paymentLines.push([0, 0, item.exportAsJSON()]); + }, this)); + result = { + name: this.getName(), + amount_paid: this.getPaidTotal(), + amount_total: this.getTotal(), + amount_tax: this.getTax(), + amount_return: this.getChange(), + lines: orderLines, + statement_ids: paymentLines + }; + return result; + }; + return Order; + })(); + OrderCollection = (function() { + __extends(OrderCollection, Backbone.Collection); + function OrderCollection() { + OrderCollection.__super__.constructor.apply(this, arguments); + } + + OrderCollection.prototype.model = Order; + return OrderCollection; + })(); + Shop = (function() { + __extends(Shop, Backbone.Model); + function Shop() { + Shop.__super__.constructor.apply(this, arguments); + } + + Shop.prototype.defaults = { + cashRegisters: new CashRegisterCollection(pos.store.get('account.bank.statement')), + orders: new OrderCollection, + products: new ProductCollection + }; + Shop.prototype.initialize = function() { + return (this.get('orders')).bind('remove', __bind( function(removedOrder) { + if ((this.get('orders')).isEmpty()) { + this.addAndSelectOrder(new Order); + } + if ((this.get('selectedOrder')) === removedOrder) { + return this.set({ + selectedOrder: (this.get('orders')).last() + }); + } + }, this)); + }; + Shop.prototype.addAndSelectOrder = function(newOrder) { + (this.get('orders')).add(newOrder); + return this.set({ + selectedOrder: newOrder + }); + }; + return Shop; + })(); + /* + The numpad handles both the choice of the property currently being modified + (quantity, price or discount) and the edition of the corresponding numeric value. + */ + NumpadState = (function() { + __extends(NumpadState, Backbone.Model); + function NumpadState() { + NumpadState.__super__.constructor.apply(this, arguments); + } + + NumpadState.prototype.defaults = { + buffer: "0", + mode: "quantity" + }; + NumpadState.prototype.initialize = function(options) { + this.shop = options.shop; + return this.shop.bind('change:selectedOrder', this.reset, this); + }; + NumpadState.prototype.appendNewChar = function(newChar) { + var oldBuffer; + oldBuffer = this.get('buffer'); + if (oldBuffer === '0') { + this.set({ + buffer: newChar + }); + } else if (oldBuffer === '-0') { + this.set({ + buffer: "-" + newChar + }); + } else { + this.set({ + buffer: (this.get('buffer')) + newChar + }); + } + return this.updateTarget(); + }; + NumpadState.prototype.deleteLastChar = function() { + var tempNewBuffer; + tempNewBuffer = (this.get('buffer')).slice(0, -1) || "0"; + if (isNaN(tempNewBuffer)) { + tempNewBuffer = "0"; + } + this.set({ + buffer: tempNewBuffer + }); + return this.updateTarget(); + }; + NumpadState.prototype.switchSign = function() { + var oldBuffer; + oldBuffer = this.get('buffer'); + this.set({ + buffer: oldBuffer[0] === '-' ? oldBuffer.substr(1) : "-" + oldBuffer + }); + return this.updateTarget(); + }; + NumpadState.prototype.changeMode = function(newMode) { + return this.set({ + buffer: "0", + mode: newMode + }); + }; + NumpadState.prototype.reset = function() { + return this.set({ + buffer: "0" + }); + }; + NumpadState.prototype.updateTarget = function() { + var bufferContent, params; + bufferContent = this.get('buffer'); + if (bufferContent && !isNaN(bufferContent)) { + params = {}; + params[this.get('mode')] = parseFloat(bufferContent); + return (this.shop.get('selectedOrder')).selected.set(params); + } + }; + return NumpadState; + })(); + /* + --- + Views + --- + */ + NumpadView = (function() { + __extends(NumpadView, Backbone.View); + function NumpadView() { + NumpadView.__super__.constructor.apply(this, arguments); + } + + NumpadView.prototype.initialize = function(options) { + return this.state = options.state; + }; + NumpadView.prototype.events = { + 'click button#numpad-backspace': 'clickDeleteLastChar', + 'click button#numpad-minus': 'clickSwitchSign', + 'click button.number-char': 'clickAppendNewChar', + 'click button.mode-button': 'clickChangeMode' + }; + NumpadView.prototype.clickDeleteLastChar = function() { + return this.state.deleteLastChar(); + }; + NumpadView.prototype.clickSwitchSign = function() { + return this.state.switchSign(); + }; + NumpadView.prototype.clickAppendNewChar = function(event) { + var newChar; + newChar = event.currentTarget.innerText; + return this.state.appendNewChar(newChar); + }; + NumpadView.prototype.clickChangeMode = function(event) { + var newMode; + $('.selected-mode').removeClass('selected-mode'); + $(event.currentTarget).addClass('selected-mode'); + newMode = event.currentTarget.attributes['data-mode'].nodeValue; + return this.state.changeMode(newMode); + }; + return NumpadView; + })(); + /* + Gives access to the payment methods (aka. 'cash registers') + */ + PaypadView = (function() { + __extends(PaypadView, Backbone.View); + function PaypadView() { + PaypadView.__super__.constructor.apply(this, arguments); + } + + PaypadView.prototype.initialize = function(options) { + return this.shop = options.shop; + }; + PaypadView.prototype.events = { + 'click button': 'performPayment' + }; + PaypadView.prototype.performPayment = function(event) { + var cashRegister, cashRegisterCollection, cashRegisterId; + cashRegisterId = event.currentTarget.attributes['cash-register-id'].nodeValue; + cashRegisterCollection = this.shop.get('cashRegisters'); + cashRegister = cashRegisterCollection.find(__bind( function(item) { + return (item.get('id')) === parseInt(cashRegisterId, 10); + }, this)); + return (this.shop.get('selectedOrder')).addPaymentLine(cashRegister); + }; + PaypadView.prototype.render = function() { + $(this.el).empty(); + return (this.shop.get('cashRegisters')).each(__bind( function(cashRegister) { + return $(this.el).append((new PaymentButtonView({ + model: cashRegister + })).render()); + }, this)); + }; + return PaypadView; + })(); + PaymentButtonView = (function() { + __extends(PaymentButtonView, Backbone.View); + function PaymentButtonView() { + PaymentButtonView.__super__.constructor.apply(this, arguments); + } + + PaymentButtonView.prototype.template = _.template($('#payment-button-template').html()); + PaymentButtonView.prototype.render = function() { + return $(this.el).html(this.template({ + id: this.model.get('id'), + name: (this.model.get('journal_id'))[1] + })); + }; + return PaymentButtonView; + })(); + /* + There are 3 steps in a POS workflow: + 1. prepare the order (i.e. chose products, quantities etc.) + 2. choose payment method(s) and amount(s) + 3. validae order and print receipt + It should be possible to go back to any step as long as step 3 hasn't been completed. + Modifying an order after validation shouldn't be allowed. + */ + StepsView = (function() { + __extends(StepsView, Backbone.View); + function StepsView() { + StepsView.__super__.constructor.apply(this, arguments); + } + + StepsView.prototype.initialize = function(options) { + return this.step = "products"; + }; + StepsView.prototype.events = { + 'click input.step-button': 'clickChangeStep' + }; + StepsView.prototype.clickChangeStep = function(event) { + var newStep; + newStep = event.currentTarget.attributes['data-step'].nodeValue; + $('.step-screen').hide(); + $('#' + newStep + '-screen').show(); + return this.step = newStep; + }; + return StepsView; + })(); + /* + Shopping carts. + */ + OrderlineView = (function() { + __extends(OrderlineView, Backbone.View); + function OrderlineView() { + OrderlineView.__super__.constructor.apply(this, arguments); + } + + OrderlineView.prototype.tagName = 'tr'; + OrderlineView.prototype.template = _.template($('#orderline-template').html()); + OrderlineView.prototype.initialize = function(options) { + this.model.bind('change', __bind( function() { + $(this.el).hide(); + return this.render(); + }, this)); + this.model.bind('remove', __bind( function() { + return $(this.el).remove(); + }, this)); + this.order = options.order; + return this.numpadState = options.numpadState; + }; + OrderlineView.prototype.events = { + 'click': 'clickHandler' + }; + OrderlineView.prototype.clickHandler = function() { + this.numpadState.reset(); + return this.select(); + }; + OrderlineView.prototype.render = function() { + this.select(); + return $(this.el).html(this.template(this.model.toJSON())).fadeIn(400, function() { + return $('#current-order').scrollTop($(this).offset().top); + }); + }; + OrderlineView.prototype.select = function() { + $('tr.selected').removeClass('selected'); + $(this.el).addClass('selected'); + return this.order.selected = this.model; + }; + return OrderlineView; + })(); + OrderView = (function() { + __extends(OrderView, Backbone.View); + function OrderView() { + OrderView.__super__.constructor.apply(this, arguments); + } + + OrderView.prototype.initialize = function(options) { + this.shop = options.shop; + this.numpadState = options.numpadState; + this.shop.bind('change:selectedOrder', this.changeSelectedOrder, this); + return this.bindOrderLineEvents(); + }; + OrderView.prototype.changeSelectedOrder = function() { + this.currentOrderLines.unbind(); + this.bindOrderLineEvents(); + return this.render(); + }; + OrderView.prototype.bindOrderLineEvents = function() { + this.currentOrderLines = (this.shop.get('selectedOrder')).get('orderLines'); + this.currentOrderLines.bind('add', this.addLine, this); + this.currentOrderLines.bind('change', this.render, this); + return this.currentOrderLines.bind('remove', this.render, this); + }; + OrderView.prototype.addLine = function(newLine) { + $(this.el).append((new OrderlineView({ + model: newLine, + order: this.shop.get('selectedOrder'), + numpadState: this.numpadState + })).render()); + return this.updateSummary(); + }; + OrderView.prototype.render = function() { + $(this.el).empty(); + this.currentOrderLines.each(__bind( function(orderLine) { + return $(this.el).append((new OrderlineView({ + model: orderLine, + order: this.shop.get('selectedOrder'), + numpadState: this.numpadState + })).render()); + }, this)); + return this.updateSummary(); + }; + OrderView.prototype.updateSummary = function() { + var currentOrder, tax, total, totalTaxExcluded; + currentOrder = this.shop.get('selectedOrder'); + total = currentOrder.getTotal(); + totalTaxExcluded = currentOrder.getTotalTaxExcluded(); + tax = currentOrder.getTax(); + $('#subtotal').html(totalTaxExcluded.toFixed(2)).hide().fadeIn(); + $('#tax').html(tax.toFixed(2)).hide().fadeIn(); + return $('#total').html(total.toFixed(2)).hide().fadeIn(); + }; + return OrderView; + })(); + /* + "Products" step. + */ + CategoryView = (function() { + __extends(CategoryView, Backbone.View); + function CategoryView() { + CategoryView.__super__.constructor.apply(this, arguments); + } + + CategoryView.prototype.template = _.template($('#category-template').html()); + CategoryView.prototype.render = function(ancestors, children) { + var c; + return $(this.el).html(this.template({ + breadcrumb: (function() { + var _i, _len, _results; + _results = []; + for (_i = 0, _len = ancestors.length; _i < _len; _i++) { + c = ancestors[_i]; + _results.push(pos.categories[c]); + } + return _results; + })(), + categories: (function() { + var _i, _len, _results; + _results = []; + for (_i = 0, _len = children.length; _i < _len; _i++) { + c = children[_i]; + _results.push(pos.categories[c]); + } + return _results; + })() + })); + }; + return CategoryView; + })(); + ProductView = (function() { + __extends(ProductView, Backbone.View); + function ProductView() { + ProductView.__super__.constructor.apply(this, arguments); + } + + ProductView.prototype.tagName = 'li'; + ProductView.prototype.className = 'product'; + ProductView.prototype.template = _.template($('#product-template').html()); + ProductView.prototype.events = { + 'click a': 'addToOrder' + }; + ProductView.prototype.initialize = function(options) { + return this.shop = options.shop; + }; + ProductView.prototype.addToOrder = function(event) { + /* Preserve the category URL */ + event.preventDefault(); + return (this.shop.get('selectedOrder')).addProduct(this.model); + }; + ProductView.prototype.render = function() { + return $(this.el).html(this.template(this.model.toJSON())); + }; + return ProductView; + })(); + ProductListView = (function() { + __extends(ProductListView, Backbone.View); + function ProductListView() { + ProductListView.__super__.constructor.apply(this, arguments); + } + + ProductListView.prototype.tagName = 'ol'; + ProductListView.prototype.className = 'product-list'; + ProductListView.prototype.initialize = function(options) { + this.shop = options.shop; + return (this.shop.get('products')).bind('reset', this.render, this); + }; + ProductListView.prototype.render = function() { + $(this.el).empty(); + (this.shop.get('products')).each(__bind( function(product) { + return $(this.el).append((new ProductView({ + model: product, + shop: this.shop + })).render()); + }, this)); + return $('#products-screen').append(this.el); + }; + return ProductListView; + })(); + /* + "Payment" step. + */ + PaymentlineView = (function() { + __extends(PaymentlineView, Backbone.View); + function PaymentlineView() { + PaymentlineView.__super__.constructor.apply(this, arguments); + } + + PaymentlineView.prototype.tagName = 'li'; + PaymentlineView.prototype.className = 'paymentline'; + PaymentlineView.prototype.template = _.template($('#paymentline-template').html()); + PaymentlineView.prototype.initialize = function() { + return this.model.bind('change', this.render, this); + }; + PaymentlineView.prototype.events = { + 'keyup input': 'changeAmount' + }; + PaymentlineView.prototype.changeAmount = function(event) { + var newAmount; + newAmount = event.currentTarget.value; + if (newAmount && !isNaN(newAmount)) { + return this.model.set({ + amount: parseFloat(newAmount) + }); + } + }; + PaymentlineView.prototype.render = function() { + return $(this.el).html(this.template({ + name: (this.model.get('journal_id'))[1], + amount: this.model.get('amount') + })); + }; + return PaymentlineView; + })(); + PaymentView = (function() { + __extends(PaymentView, Backbone.View); + function PaymentView() { + PaymentView.__super__.constructor.apply(this, arguments); + } + + PaymentView.prototype.initialize = function(options) { + this.shop = options.shop; + this.shop.bind('change:selectedOrder', this.changeSelectedOrder, this); + this.bindPaymentLineEvents(); + return this.bindOrderLineEvents(); + }; + PaymentView.prototype.paymentLineList = function() { + return $(this.el).find('#paymentlines'); + }; + PaymentView.prototype.events = { + 'click button#validate-order': 'validateCurrentOrder' + }; + PaymentView.prototype.validateCurrentOrder = function() { + var callback, currentOrder; + currentOrder = this.shop.get('selectedOrder'); + callback = __bind( function() { + return currentOrder.set({ + validated: true + }); + }, this); + return pos.push('pos.order', currentOrder.exportAsJSON(), callback); + }; + PaymentView.prototype.bindPaymentLineEvents = function() { + this.currentPaymentLines = (this.shop.get('selectedOrder')).get('paymentLines'); + this.currentPaymentLines.bind('add', this.addPaymentLine, this); + this.currentPaymentLines.bind('change', this.render, this); + this.currentPaymentLines.bind('remove', this.render, this); + return this.currentPaymentLines.bind('all', this.updatePaymentSummary, this); + }; + PaymentView.prototype.bindOrderLineEvents = function() { + this.currentOrderLines = (this.shop.get('selectedOrder')).get('orderLines'); + return this.currentOrderLines.bind('all', this.updatePaymentSummary, this); + }; + PaymentView.prototype.changeSelectedOrder = function() { + this.currentPaymentLines.unbind(); + this.bindPaymentLineEvents(); + this.currentOrderLines.unbind(); + this.bindOrderLineEvents(); + return this.render(); + }; + PaymentView.prototype.addPaymentLine = function(newPaymentLine) { + return this.paymentLineList().append((new PaymentlineView({ + model: newPaymentLine + })).render()); + }; + PaymentView.prototype.render = function() { + this.paymentLineList().empty(); + this.currentPaymentLines.each(__bind( function(paymentLine) { + return this.paymentLineList().append((new PaymentlineView({ + model: paymentLine + })).render()); + }, this)); + return this.updatePaymentSummary(); + }; + PaymentView.prototype.updatePaymentSummary = function() { + var currentOrder, dueTotal, paidTotal, remaining, remainingAmount; + currentOrder = this.shop.get('selectedOrder'); + paidTotal = currentOrder.getPaidTotal(); + dueTotal = currentOrder.getTotal(); + $(this.el).find('#payment-due-total').html(dueTotal.toFixed(2)); + $(this.el).find('#payment-paid-total').html(paidTotal.toFixed(2)); + remainingAmount = dueTotal - paidTotal; + remaining = remainingAmount > 0 ? "Due left: " + remainingAmount.toFixed(2) : "Change: " + (-remainingAmount).toFixed(2); + return $('#payment-remaining').html(remaining); + }; + return PaymentView; + })(); + /* + "Receipt" step. + */ + ReceiptLineView = (function() { + __extends(ReceiptLineView, Backbone.View); + function ReceiptLineView() { + ReceiptLineView.__super__.constructor.apply(this, arguments); + } + + ReceiptLineView.prototype.tagName = 'li'; + ReceiptLineView.prototype.className = 'receiptline'; + ReceiptLineView.prototype.template = _.template($('#receiptline-template').html()); + ReceiptLineView.prototype.initialize = function() { + return this.model.bind('change', this.render, this); + }; + ReceiptLineView.prototype.render = function() { + return $(this.el).html(this.template(this.model.toJSON())); + }; + return ReceiptLineView; + })(); + ReceiptView = (function() { + __extends(ReceiptView, Backbone.View); + function ReceiptView() { + ReceiptView.__super__.constructor.apply(this, arguments); + } + + ReceiptView.prototype.initialize = function(options) { + this.shop = options.shop; + this.shop.bind('change:selectedOrder', this.changeSelectedOrder, this); + this.bindOrderLineEvents(); + return this.bindPaymentLineEvents(); + }; + ReceiptView.prototype.receiptLineList = function() { + return $(this.el).find('#receiptlines'); + }; + ReceiptView.prototype.bindOrderLineEvents = function() { + this.currentOrderLines = (this.shop.get('selectedOrder')).get('orderLines'); + this.currentOrderLines.bind('add', this.addReceiptLine, this); + this.currentOrderLines.bind('change', this.render, this); + return this.currentOrderLines.bind('remove', this.render, this); + }; + ReceiptView.prototype.bindPaymentLineEvents = function() { + this.currentPaymentLines = (this.shop.get('selectedOrder')).get('paymentLines'); + return this.currentPaymentLines.bind('all', this.updateReceiptSummary, this); + }; + ReceiptView.prototype.changeSelectedOrder = function() { + this.currentOrderLines.unbind(); + this.bindOrderLineEvents(); + this.currentPaymentLines.unbind(); + this.bindPaymentLineEvents(); + return this.render(); + }; + ReceiptView.prototype.addReceiptLine = function(newOrderItem) { + this.receiptLineList().append((new ReceiptLineView({ + model: newOrderItem + })).render()); + return this.updateReceiptSummary(); + }; + ReceiptView.prototype.render = function() { + this.receiptLineList().empty(); + this.currentOrderLines.each(__bind( function(orderItem) { + return this.receiptLineList().append((new ReceiptLineView({ + model: orderItem + })).render()); + }, this)); + return this.updateReceiptSummary(); + }; + ReceiptView.prototype.updateReceiptSummary = function() { + var change, currentOrder, tax, total; + currentOrder = this.shop.get('selectedOrder'); + total = currentOrder.getTotal(); + tax = currentOrder.getTax(); + change = currentOrder.getPaidTotal() - total; + $('#receipt-summary-tax').html(tax.toFixed(2)); + $('#receipt-summary-total').html(total.toFixed(2)); + return $('#receipt-summary-change').html(change.toFixed(2)); + }; + return ReceiptView; + })(); + OrderButtonView = (function() { + __extends(OrderButtonView, Backbone.View); + function OrderButtonView() { + OrderButtonView.__super__.constructor.apply(this, arguments); + } + + OrderButtonView.prototype.tagName = 'li'; + OrderButtonView.prototype.className = 'order-selector-button'; + OrderButtonView.prototype.template = _.template($('#order-selector-button-template').html()); + OrderButtonView.prototype.initialize = function(options) { + this.order = options.order; + this.shop = options.shop; + this.order.bind('destroy', __bind( function() { + return $(this.el).remove(); + }, this)); + return this.shop.bind('change:selectedOrder', __bind( function(shop) { + var selectedOrder; + selectedOrder = shop.get('selectedOrder'); + if (this.order === selectedOrder) { + return this.setButtonSelected(); + } + }, this)); + }; + OrderButtonView.prototype.events = { + 'click button.select-order': 'selectOrder', + 'click button.close-order': 'closeOrder' + }; + OrderButtonView.prototype.selectOrder = function(event) { + return this.shop.set({ + selectedOrder: this.order + }); + }; + OrderButtonView.prototype.setButtonSelected = function() { + $('.selected-order').removeClass('selected-order'); + return $(this.el).addClass('selected-order'); + }; + OrderButtonView.prototype.closeOrder = function(event) { + return this.order.destroy(); + }; + OrderButtonView.prototype.render = function() { + return $(this.el).html(this.template(this.order.toJSON())); + }; + return OrderButtonView; + })(); + ShopView = (function() { + __extends(ShopView, Backbone.View); + function ShopView() { + ShopView.__super__.constructor.apply(this, arguments); + } + + ShopView.prototype.initialize = function(options) { + this.shop = options.shop; + (this.shop.get('orders')).bind('add', this.orderAdded, this); + (this.shop.get('orders')).add(new Order); + this.numpadState = new NumpadState({ + shop: this.shop + }); + this.productListView = new ProductListView({ + shop: this.shop + }); + this.paypadView = new PaypadView({ + shop: this.shop, + el: $('#paypad') + }); + this.paypadView.render(); + this.orderView = new OrderView({ + shop: this.shop, + numpadState: this.numpadState, + el: $('#current-order-content') + }); + this.paymentView = new PaymentView({ + shop: this.shop, + el: $('#payment-screen') + }); + this.receiptView = new ReceiptView({ + shop: this.shop, + el: $('#receipt-screen') + }); + this.numpadView = new NumpadView({ + state: this.numpadState, + el: $('#numpad') + }); + return this.stepsView = new StepsView({ + el: $('#steps') + }); + }; + ShopView.prototype.events = { + 'click button#neworder-button': 'createNewOrder' + }; + ShopView.prototype.createNewOrder = function() { + var newOrder; + newOrder = new Order; + (this.shop.get('orders')).add(newOrder); + return this.shop.set({ + selectedOrder: newOrder + }); + }; + ShopView.prototype.orderAdded = function(newOrder) { + var newOrderButton; + newOrderButton = new OrderButtonView({ + order: newOrder, + shop: this.shop + }); + $('#orders').append(newOrderButton.render()); + return newOrderButton.selectOrder(); + }; + return ShopView; + })(); + App = (function() { + __extends(App, Backbone.Router); + function App() { + App.__super__.constructor.apply(this, arguments); + } + + App.prototype.routes = { + '': 'category', + 'category/:id': 'category' + }; + App.prototype.initialize = function() { + this.shop = new Shop; + this.shopView = new ShopView({ + shop: this.shop, + el: $('body') + }); + return this.categoryView = new CategoryView; + }; + App.prototype.category = function(id) { + var c, products; + if (id == null) { + id = 0; + } + c = pos.categories[id]; + $('#products-screen').html(this.categoryView.render(c.ancestors, c.children)); + products = pos.store.get('product.product').filter( function(p) { + var _ref; + return _ref = p.pos_categ_id[0], __indexOf.call(c.subtree, _ref) >= 0; + }); + (this.shop.get('products')).reset(products); + $('.searchbox input').keyup( function() { + var m, s; + s = $(this).val().toLowerCase(); + if (s) { + m = products.filter( function(p) { + return p.name.toLowerCase().indexOf(s); + }); + $('.search-clear').fadeIn(); + } else { + m = products; + $('.search-clear').fadeOut(); + } + return (this.shop.get('products')).reset(m); + }); + return $('.search-clear').click( function() { + (this.shop.get('products')).reset(products); + $('.searchbox input').val('').focus(); + return $('.search-clear').fadeOut(); + }); + }; + return App; + })(); + return pos.ready.then( function() { + pos.app = new App; + return Backbone.history.start(); }); - }; - OrderButtonView.prototype.setButtonSelected = function() { - $('.selected-order').removeClass('selected-order'); - return $(this.el).addClass('selected-order'); - }; - OrderButtonView.prototype.closeOrder = function(event) { - return this.order.destroy(); - }; - OrderButtonView.prototype.render = function() { - return $(this.el).html(this.template(this.order.toJSON())); - }; - return OrderButtonView; - })(); - ShopView = (function() { - __extends(ShopView, Backbone.View); - function ShopView() { - ShopView.__super__.constructor.apply(this, arguments); - } - ShopView.prototype.initialize = function(options) { - this.shop = options.shop; - (this.shop.get('orders')).bind('add', this.orderAdded, this); - (this.shop.get('orders')).add(new Order); - this.numpadState = new NumpadState({ - shop: this.shop - }); - this.productListView = new ProductListView({ - shop: this.shop - }); - this.paypadView = new PaypadView({ - shop: this.shop, - el: $('#paypad') - }); - this.paypadView.render(); - this.orderView = new OrderView({ - shop: this.shop, - numpadState: this.numpadState, - el: $('#current-order-content') - }); - this.paymentView = new PaymentView({ - shop: this.shop, - el: $('#payment-screen') - }); - this.receiptView = new ReceiptView({ - shop: this.shop, - el: $('#receipt-screen') - }); - this.numpadView = new NumpadView({ - state: this.numpadState, - el: $('#numpad') - }); - return this.stepsView = new StepsView({ - el: $('#steps') - }); - }; - ShopView.prototype.events = { - 'click button#neworder-button': 'createNewOrder' - }; - ShopView.prototype.createNewOrder = function() { - var newOrder; - newOrder = new Order; - (this.shop.get('orders')).add(newOrder); - return this.shop.set({ - selectedOrder: newOrder - }); - }; - ShopView.prototype.orderAdded = function(newOrder) { - var newOrderButton; - newOrderButton = new OrderButtonView({ - order: newOrder, - shop: this.shop - }); - $('#orders').append(newOrderButton.render()); - return newOrderButton.selectOrder(); - }; - return ShopView; - })(); - App = (function() { - __extends(App, Backbone.Router); - function App() { - App.__super__.constructor.apply(this, arguments); - } - App.prototype.routes = { - '': 'category', - 'category/:id': 'category' - }; - App.prototype.initialize = function() { - this.shop = new Shop; - this.shopView = new ShopView({ - shop: this.shop, - el: $('body') - }); - return this.categoryView = new CategoryView; - }; - App.prototype.category = function(id) { - var c, products; - if (id == null) { - id = 0; - } - c = pos.categories[id]; - $('#products-screen').html(this.categoryView.render(c.ancestors, c.children)); - products = pos.store.get('product.product').filter(function(p) { - var _ref; - return _ref = p.pos_categ_id[0], __indexOf.call(c.subtree, _ref) >= 0; - }); - (this.shop.get('products')).reset(products); - $('.searchbox input').keyup(function() { - var m, s; - s = $(this).val().toLowerCase(); - if (s) { - m = products.filter(function(p) { - return p.name.toLowerCase().indexOf(s); - }); - $('.search-clear').fadeIn(); - } else { - m = products; - $('.search-clear').fadeOut(); - } - return (this.shop.get('products')).reset(m); - }); - return $('.search-clear').click(function() { - (this.shop.get('products')).reset(products); - $('.searchbox input').val('').focus(); - return $('.search-clear').fadeOut(); - }); - }; - return App; - })(); - return pos.ready.then(function() { - pos.app = new App; - return Backbone.history.start(); }); - }); -}).call(this); +}).call(this); \ No newline at end of file From c56c938814f690a56f5a4ccfb53860820efb767d Mon Sep 17 00:00:00 2001 From: niv-openerp Date: Mon, 3 Oct 2011 17:05:57 +0200 Subject: [PATCH 109/409] [fix] corrected some inconsistencies bzr revid: nicolas.vanhoren@openerp.com-20111003150557-3ltyat0ldh0g3n7i --- addons/point_of_sale/static/src/js/pos.js | 6 +++--- addons/point_of_sale/static/src/pos.html | 14 +++++++------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/addons/point_of_sale/static/src/js/pos.js b/addons/point_of_sale/static/src/js/pos.js index 5a685b3b4a6..40c8c3987f5 100644 --- a/addons/point_of_sale/static/src/js/pos.js +++ b/addons/point_of_sale/static/src/js/pos.js @@ -63,19 +63,19 @@ } Pos.prototype.ready = $.Deferred(); - Pos.prototype.session = new db.base.Session('DEBUG'); + Pos.prototype.session = new db.web.Session('DEBUG'); Pos.prototype.store = new Store; Pos.prototype.fetch = function(osvModel, fields, domain, callback, errorCallback) { var dataSetSearch; callback = callback || __bind( function(result) { return this.store.set(osvModel, result); }, this); - dataSetSearch = new db.base.DataSetSearch(this, osvModel, null, domain); + dataSetSearch = new db.web.DataSetSearch(this, osvModel, null, domain); return dataSetSearch.read_slice(fields, 0, null, callback); }; Pos.prototype.push = function(osvModel, record, callback, errorCallback) { var dataSet; - dataSet = new db.base.DataSet(this, osvModel, null); + dataSet = new db.web.DataSet(this, osvModel, null); return dataSet.create(record, callback, errorCallback); }; Pos.prototype.categories = {}; diff --git a/addons/point_of_sale/static/src/pos.html b/addons/point_of_sale/static/src/pos.html index 3d8cfab7ba1..1862146376a 100644 --- a/addons/point_of_sale/static/src/pos.html +++ b/addons/point_of_sale/static/src/pos.html @@ -24,14 +24,14 @@
    -
    +
    - + - +
    @@ -91,7 +91,7 @@
    @@ -134,18 +134,18 @@
    From dd5f24517e6e4fea40bbeb9c3dd5dbdd05d437d5 Mon Sep 17 00:00:00 2001 From: niv-openerp Date: Mon, 3 Oct 2011 18:28:33 +0200 Subject: [PATCH 110/409] [fix] some problem with web client api modified bzr revid: nicolas.vanhoren@openerp.com-20111003162833-j911gi9au934thh6 --- .../static/lib/backbone/backbone-0.5.3.min.js | 33 ------------------- addons/point_of_sale/static/src/js/pos.js | 23 ++++++++----- addons/point_of_sale/static/src/pos.html | 2 +- 3 files changed, 16 insertions(+), 42 deletions(-) delete mode 100644 addons/point_of_sale/static/lib/backbone/backbone-0.5.3.min.js diff --git a/addons/point_of_sale/static/lib/backbone/backbone-0.5.3.min.js b/addons/point_of_sale/static/lib/backbone/backbone-0.5.3.min.js deleted file mode 100644 index 3f0d495dc39..00000000000 --- a/addons/point_of_sale/static/lib/backbone/backbone-0.5.3.min.js +++ /dev/null @@ -1,33 +0,0 @@ -// Backbone.js 0.5.3 -// (c) 2010 Jeremy Ashkenas, DocumentCloud Inc. -// Backbone may be freely distributed under the MIT license. -// For all details and documentation: -// http://documentcloud.github.com/backbone -(function(){var h=this,p=h.Backbone,e;e=typeof exports!=="undefined"?exports:h.Backbone={};e.VERSION="0.5.3";var f=h._;if(!f&&typeof require!=="undefined")f=require("underscore")._;var g=h.jQuery||h.Zepto;e.noConflict=function(){h.Backbone=p;return this};e.emulateHTTP=!1;e.emulateJSON=!1;e.Events={bind:function(a,b,c){var d=this._callbacks||(this._callbacks={});(d[a]||(d[a]=[])).push([b,c]);return this},unbind:function(a,b){var c;if(a){if(c=this._callbacks)if(b){c=c[a];if(!c)return this;for(var d= -0,e=c.length;d/g,">").replace(/"/g,""").replace(/'/g,"'").replace(/\//g,"/")},has:function(a){return this.attributes[a]!=null},set:function(a,b){b||(b={});if(!a)return this;if(a.attributes)a=a.attributes;var c=this.attributes,d=this._escapedAttributes;if(!b.silent&&this.validate&&!this._performValidation(a,b))return!1;if(this.idAttribute in a)this.id=a[this.idAttribute]; -var e=this._changing;this._changing=!0;for(var g in a){var h=a[g];if(!f.isEqual(c[g],h))c[g]=h,delete d[g],this._changed=!0,b.silent||this.trigger("change:"+g,this,h,b)}!e&&!b.silent&&this._changed&&this.change(b);this._changing=!1;return this},unset:function(a,b){if(!(a in this.attributes))return this;b||(b={});var c={};c[a]=void 0;if(!b.silent&&this.validate&&!this._performValidation(c,b))return!1;delete this.attributes[a];delete this._escapedAttributes[a];a==this.idAttribute&&delete this.id;this._changed= -!0;b.silent||(this.trigger("change:"+a,this,void 0,b),this.change(b));return this},clear:function(a){a||(a={});var b,c=this.attributes,d={};for(b in c)d[b]=void 0;if(!a.silent&&this.validate&&!this._performValidation(d,a))return!1;this.attributes={};this._escapedAttributes={};this._changed=!0;if(!a.silent){for(b in c)this.trigger("change:"+b,this,void 0,a);this.change(a)}return this},fetch:function(a){a||(a={});var b=this,c=a.success;a.success=function(d,e,f){if(!b.set(b.parse(d,f),a))return!1;c&& -c(b,d)};a.error=i(a.error,b,a);return(this.sync||e.sync).call(this,"read",this,a)},save:function(a,b){b||(b={});if(a&&!this.set(a,b))return!1;var c=this,d=b.success;b.success=function(a,e,f){if(!c.set(c.parse(a,f),b))return!1;d&&d(c,a,f)};b.error=i(b.error,c,b);var f=this.isNew()?"create":"update";return(this.sync||e.sync).call(this,f,this,b)},destroy:function(a){a||(a={});if(this.isNew())return this.trigger("destroy",this,this.collection,a);var b=this,c=a.success;a.success=function(d){b.trigger("destroy", -b,b.collection,a);c&&c(b,d)};a.error=i(a.error,b,a);return(this.sync||e.sync).call(this,"delete",this,a)},url:function(){var a=k(this.collection)||this.urlRoot||l();if(this.isNew())return a;return a+(a.charAt(a.length-1)=="/"?"":"/")+encodeURIComponent(this.id)},parse:function(a){return a},clone:function(){return new this.constructor(this)},isNew:function(){return this.id==null},change:function(a){this.trigger("change",this,a);this._previousAttributes=f.clone(this.attributes);this._changed=!1},hasChanged:function(a){if(a)return this._previousAttributes[a]!= -this.attributes[a];return this._changed},changedAttributes:function(a){a||(a=this.attributes);var b=this._previousAttributes,c=!1,d;for(d in a)f.isEqual(b[d],a[d])||(c=c||{},c[d]=a[d]);return c},previous:function(a){if(!a||!this._previousAttributes)return null;return this._previousAttributes[a]},previousAttributes:function(){return f.clone(this._previousAttributes)},_performValidation:function(a,b){var c=this.validate(a);if(c)return b.error?b.error(this,c,b):this.trigger("error",this,c,b),!1;return!0}}); -e.Collection=function(a,b){b||(b={});if(b.comparator)this.comparator=b.comparator;f.bindAll(this,"_onModelEvent","_removeReference");this._reset();a&&this.reset(a,{silent:!0});this.initialize.apply(this,arguments)};f.extend(e.Collection.prototype,e.Events,{model:e.Model,initialize:function(){},toJSON:function(){return this.map(function(a){return a.toJSON()})},add:function(a,b){if(f.isArray(a))for(var c=0,d=a.length;c').hide().appendTo("body")[0].contentWindow,this.navigate(a); -this._hasPushState?g(window).bind("popstate",this.checkUrl):"onhashchange"in window&&!b?g(window).bind("hashchange",this.checkUrl):setInterval(this.checkUrl,this.interval);this.fragment=a;m=!0;a=window.location;b=a.pathname==this.options.root;if(this._wantsPushState&&!this._hasPushState&&!b)return this.fragment=this.getFragment(null,!0),window.location.replace(this.options.root+"#"+this.fragment),!0;else if(this._wantsPushState&&this._hasPushState&&b&&a.hash)this.fragment=a.hash.replace(j,""),window.history.replaceState({}, -document.title,a.protocol+"//"+a.host+this.options.root+this.fragment);if(!this.options.silent)return this.loadUrl()},route:function(a,b){this.handlers.unshift({route:a,callback:b})},checkUrl:function(){var a=this.getFragment();a==this.fragment&&this.iframe&&(a=this.getFragment(this.iframe.location.hash));if(a==this.fragment||a==decodeURIComponent(this.fragment))return!1;this.iframe&&this.navigate(a);this.loadUrl()||this.loadUrl(window.location.hash)},loadUrl:function(a){var b=this.fragment=this.getFragment(a); -return f.any(this.handlers,function(a){if(a.route.test(b))return a.callback(b),!0})},navigate:function(a,b){var c=(a||"").replace(j,"");if(!(this.fragment==c||this.fragment==decodeURIComponent(c))){if(this._hasPushState){var d=window.location;c.indexOf(this.options.root)!=0&&(c=this.options.root+c);this.fragment=c;window.history.pushState({},document.title,d.protocol+"//"+d.host+c)}else if(window.location.hash=this.fragment=c,this.iframe&&c!=this.getFragment(this.iframe.location.hash))this.iframe.document.open().close(), -this.iframe.location.hash=c;b&&this.loadUrl(a)}}});e.View=function(a){this.cid=f.uniqueId("view");this._configure(a||{});this._ensureElement();this.delegateEvents();this.initialize.apply(this,arguments)};var u=/^(\S+)\s*(.*)$/,n=["model","collection","el","id","attributes","className","tagName"];f.extend(e.View.prototype,e.Events,{tagName:"div",$:function(a){return g(a,this.el)},initialize:function(){},render:function(){return this},remove:function(){g(this.el).remove();return this},make:function(a, -b,c){a=document.createElement(a);b&&g(a).attr(b);c&&g(a).html(c);return a},delegateEvents:function(a){if(a||(a=this.events))for(var b in f.isFunction(a)&&(a=a.call(this)),g(this.el).unbind(".delegateEvents"+this.cid),a){var c=this[a[b]];if(!c)throw Error('Event "'+a[b]+'" does not exist');var d=b.match(u),e=d[1];d=d[2];c=f.bind(c,this);e+=".delegateEvents"+this.cid;d===""?g(this.el).bind(e,c):g(this.el).delegate(d,e,c)}},_configure:function(a){this.options&&(a=f.extend({},this.options,a));for(var b= -0,c=n.length;b - + From ed023b8f81baf3cbcbdff358723f56699580af4d Mon Sep 17 00:00:00 2001 From: "Yogesh (OpenERP)" Date: Tue, 4 Oct 2011 11:31:17 +0530 Subject: [PATCH 111/409] [FIX] show all configuration wizard in administrator dashboard. lp bug: https://launchpad.net/bugs/865162 fixed bzr revid: ysa@tinyerp.com-20111004060117-bylyhk4ye781q33r --- addons/web_dashboard/static/src/js/dashboard.js | 1 - 1 file changed, 1 deletion(-) diff --git a/addons/web_dashboard/static/src/js/dashboard.js b/addons/web_dashboard/static/src/js/dashboard.js index 4672492821d..808fe64e5c7 100644 --- a/addons/web_dashboard/static/src/js/dashboard.js +++ b/addons/web_dashboard/static/src/js/dashboard.js @@ -292,7 +292,6 @@ openerp.web_dashboard.ConfigOverview = openerp.web.View.extend({ this._super(parent); this.dataset = new openerp.web.DataSetSearch( this, 'ir.actions.todo'); - this.dataset.domain = [['type', '=', 'manual']]; }, start: function () { this._super(); From e6dd80d6b5f8fce6b62b5b9a1517e0239726cba0 Mon Sep 17 00:00:00 2001 From: niv-openerp Date: Tue, 4 Oct 2011 10:04:55 +0200 Subject: [PATCH 112/409] [fix] forgot some things bzr revid: nicolas.vanhoren@openerp.com-20111004080455-qyd2z0rfdxbi4ecf --- .../static/lib/backbone/backbone-0.5.3.js | 1158 +++++++++++++++++ addons/point_of_sale/static/src/js/pos.js | 1 - 2 files changed, 1158 insertions(+), 1 deletion(-) create mode 100644 addons/point_of_sale/static/lib/backbone/backbone-0.5.3.js diff --git a/addons/point_of_sale/static/lib/backbone/backbone-0.5.3.js b/addons/point_of_sale/static/lib/backbone/backbone-0.5.3.js new file mode 100644 index 00000000000..8958c798466 --- /dev/null +++ b/addons/point_of_sale/static/lib/backbone/backbone-0.5.3.js @@ -0,0 +1,1158 @@ +// Backbone.js 0.5.3 +// (c) 2010 Jeremy Ashkenas, DocumentCloud Inc. +// Backbone may be freely distributed under the MIT license. +// For all details and documentation: +// http://documentcloud.github.com/backbone + +(function(){ + + // Initial Setup + // ------------- + + // Save a reference to the global object. + var root = this; + + // Save the previous value of the `Backbone` variable. + var previousBackbone = root.Backbone; + + // The top-level namespace. All public Backbone classes and modules will + // be attached to this. Exported for both CommonJS and the browser. + var Backbone; + if (typeof exports !== 'undefined') { + Backbone = exports; + } else { + Backbone = root.Backbone = {}; + } + + // Current version of the library. Keep in sync with `package.json`. + Backbone.VERSION = '0.5.3'; + + // Require Underscore, if we're on the server, and it's not already present. + var _ = root._; + if (!_ && (typeof require !== 'undefined')) _ = require('underscore')._; + + // For Backbone's purposes, jQuery or Zepto owns the `$` variable. + var $ = root.jQuery || root.Zepto; + + // Runs Backbone.js in *noConflict* mode, returning the `Backbone` variable + // to its previous owner. Returns a reference to this Backbone object. + Backbone.noConflict = function() { + root.Backbone = previousBackbone; + return this; + }; + + // Turn on `emulateHTTP` to support legacy HTTP servers. Setting this option will + // fake `"PUT"` and `"DELETE"` requests via the `_method` parameter and set a + // `X-Http-Method-Override` header. + Backbone.emulateHTTP = false; + + // Turn on `emulateJSON` to support legacy servers that can't deal with direct + // `application/json` requests ... will encode the body as + // `application/x-www-form-urlencoded` instead and will send the model in a + // form param named `model`. + Backbone.emulateJSON = false; + + // Backbone.Events + // ----------------- + + // A module that can be mixed in to *any object* in order to provide it with + // custom events. You may `bind` or `unbind` a callback function to an event; + // `trigger`-ing an event fires all callbacks in succession. + // + // var object = {}; + // _.extend(object, Backbone.Events); + // object.bind('expand', function(){ alert('expanded'); }); + // object.trigger('expand'); + // + Backbone.Events = { + + // Bind an event, specified by a string name, `ev`, to a `callback` function. + // Passing `"all"` will bind the callback to all events fired. + bind : function(ev, callback, context) { + var calls = this._callbacks || (this._callbacks = {}); + var list = calls[ev] || (calls[ev] = []); + list.push([callback, context]); + return this; + }, + + // Remove one or many callbacks. If `callback` is null, removes all + // callbacks for the event. If `ev` is null, removes all bound callbacks + // for all events. + unbind : function(ev, callback) { + var calls; + if (!ev) { + this._callbacks = {}; + } else if (calls = this._callbacks) { + if (!callback) { + calls[ev] = []; + } else { + var list = calls[ev]; + if (!list) return this; + for (var i = 0, l = list.length; i < l; i++) { + if (list[i] && callback === list[i][0]) { + list[i] = null; + break; + } + } + } + } + return this; + }, + + // Trigger an event, firing all bound callbacks. Callbacks are passed the + // same arguments as `trigger` is, apart from the event name. + // Listening for `"all"` passes the true event name as the first argument. + trigger : function(eventName) { + var list, calls, ev, callback, args; + var both = 2; + if (!(calls = this._callbacks)) return this; + while (both--) { + ev = both ? eventName : 'all'; + if (list = calls[ev]) { + for (var i = 0, l = list.length; i < l; i++) { + if (!(callback = list[i])) { + list.splice(i, 1); i--; l--; + } else { + args = both ? Array.prototype.slice.call(arguments, 1) : arguments; + callback[0].apply(callback[1] || this, args); + } + } + } + } + return this; + } + + }; + + // Backbone.Model + // -------------- + + // Create a new model, with defined attributes. A client id (`cid`) + // is automatically generated and assigned for you. + Backbone.Model = function(attributes, options) { + var defaults; + attributes || (attributes = {}); + if (defaults = this.defaults) { + if (_.isFunction(defaults)) defaults = defaults.call(this); + attributes = _.extend({}, defaults, attributes); + } + this.attributes = {}; + this._escapedAttributes = {}; + this.cid = _.uniqueId('c'); + this.set(attributes, {silent : true}); + this._changed = false; + this._previousAttributes = _.clone(this.attributes); + if (options && options.collection) this.collection = options.collection; + this.initialize(attributes, options); + }; + + // Attach all inheritable methods to the Model prototype. + _.extend(Backbone.Model.prototype, Backbone.Events, { + + // A snapshot of the model's previous attributes, taken immediately + // after the last `"change"` event was fired. + _previousAttributes : null, + + // Has the item been changed since the last `"change"` event? + _changed : false, + + // The default name for the JSON `id` attribute is `"id"`. MongoDB and + // CouchDB users may want to set this to `"_id"`. + idAttribute : 'id', + + // Initialize is an empty function by default. Override it with your own + // initialization logic. + initialize : function(){}, + + // Return a copy of the model's `attributes` object. + toJSON : function() { + return _.clone(this.attributes); + }, + + // Get the value of an attribute. + get : function(attr) { + return this.attributes[attr]; + }, + + // Get the HTML-escaped value of an attribute. + escape : function(attr) { + var html; + if (html = this._escapedAttributes[attr]) return html; + var val = this.attributes[attr]; + return this._escapedAttributes[attr] = escapeHTML(val == null ? '' : '' + val); + }, + + // Returns `true` if the attribute contains a value that is not null + // or undefined. + has : function(attr) { + return this.attributes[attr] != null; + }, + + // Set a hash of model attributes on the object, firing `"change"` unless you + // choose to silence it. + set : function(attrs, options) { + + // Extract attributes and options. + options || (options = {}); + if (!attrs) return this; + if (attrs.attributes) attrs = attrs.attributes; + var now = this.attributes, escaped = this._escapedAttributes; + + // Run validation. + if (!options.silent && this.validate && !this._performValidation(attrs, options)) return false; + + // Check for changes of `id`. + if (this.idAttribute in attrs) this.id = attrs[this.idAttribute]; + + // We're about to start triggering change events. + var alreadyChanging = this._changing; + this._changing = true; + + // Update attributes. + for (var attr in attrs) { + var val = attrs[attr]; + if (!_.isEqual(now[attr], val)) { + now[attr] = val; + delete escaped[attr]; + this._changed = true; + if (!options.silent) this.trigger('change:' + attr, this, val, options); + } + } + + // Fire the `"change"` event, if the model has been changed. + if (!alreadyChanging && !options.silent && this._changed) this.change(options); + this._changing = false; + return this; + }, + + // Remove an attribute from the model, firing `"change"` unless you choose + // to silence it. `unset` is a noop if the attribute doesn't exist. + unset : function(attr, options) { + if (!(attr in this.attributes)) return this; + options || (options = {}); + var value = this.attributes[attr]; + + // Run validation. + var validObj = {}; + validObj[attr] = void 0; + if (!options.silent && this.validate && !this._performValidation(validObj, options)) return false; + + // Remove the attribute. + delete this.attributes[attr]; + delete this._escapedAttributes[attr]; + if (attr == this.idAttribute) delete this.id; + this._changed = true; + if (!options.silent) { + this.trigger('change:' + attr, this, void 0, options); + this.change(options); + } + return this; + }, + + // Clear all attributes on the model, firing `"change"` unless you choose + // to silence it. + clear : function(options) { + options || (options = {}); + var attr; + var old = this.attributes; + + // Run validation. + var validObj = {}; + for (attr in old) validObj[attr] = void 0; + if (!options.silent && this.validate && !this._performValidation(validObj, options)) return false; + + this.attributes = {}; + this._escapedAttributes = {}; + this._changed = true; + if (!options.silent) { + for (attr in old) { + this.trigger('change:' + attr, this, void 0, options); + } + this.change(options); + } + return this; + }, + + // Fetch the model from the server. If the server's representation of the + // model differs from its current attributes, they will be overriden, + // triggering a `"change"` event. + fetch : function(options) { + options || (options = {}); + var model = this; + var success = options.success; + options.success = function(resp, status, xhr) { + if (!model.set(model.parse(resp, xhr), options)) return false; + if (success) success(model, resp); + }; + options.error = wrapError(options.error, model, options); + return (this.sync || Backbone.sync).call(this, 'read', this, options); + }, + + // Set a hash of model attributes, and sync the model to the server. + // If the server returns an attributes hash that differs, the model's + // state will be `set` again. + save : function(attrs, options) { + options || (options = {}); + if (attrs && !this.set(attrs, options)) return false; + var model = this; + var success = options.success; + options.success = function(resp, status, xhr) { + if (!model.set(model.parse(resp, xhr), options)) return false; + if (success) success(model, resp, xhr); + }; + options.error = wrapError(options.error, model, options); + var method = this.isNew() ? 'create' : 'update'; + return (this.sync || Backbone.sync).call(this, method, this, options); + }, + + // Destroy this model on the server if it was already persisted. Upon success, the model is removed + // from its collection, if it has one. + destroy : function(options) { + options || (options = {}); + if (this.isNew()) return this.trigger('destroy', this, this.collection, options); + var model = this; + var success = options.success; + options.success = function(resp) { + model.trigger('destroy', model, model.collection, options); + if (success) success(model, resp); + }; + options.error = wrapError(options.error, model, options); + return (this.sync || Backbone.sync).call(this, 'delete', this, options); + }, + + // Default URL for the model's representation on the server -- if you're + // using Backbone's restful methods, override this to change the endpoint + // that will be called. + url : function() { + var base = getUrl(this.collection) || this.urlRoot || urlError(); + if (this.isNew()) return base; + return base + (base.charAt(base.length - 1) == '/' ? '' : '/') + encodeURIComponent(this.id); + }, + + // **parse** converts a response into the hash of attributes to be `set` on + // the model. The default implementation is just to pass the response along. + parse : function(resp, xhr) { + return resp; + }, + + // Create a new model with identical attributes to this one. + clone : function() { + return new this.constructor(this); + }, + + // A model is new if it has never been saved to the server, and lacks an id. + isNew : function() { + return this.id == null; + }, + + // Call this method to manually fire a `change` event for this model. + // Calling this will cause all objects observing the model to update. + change : function(options) { + this.trigger('change', this, options); + this._previousAttributes = _.clone(this.attributes); + this._changed = false; + }, + + // Determine if the model has changed since the last `"change"` event. + // If you specify an attribute name, determine if that attribute has changed. + hasChanged : function(attr) { + if (attr) return this._previousAttributes[attr] != this.attributes[attr]; + return this._changed; + }, + + // Return an object containing all the attributes that have changed, or false + // if there are no changed attributes. Useful for determining what parts of a + // view need to be updated and/or what attributes need to be persisted to + // the server. + changedAttributes : function(now) { + now || (now = this.attributes); + var old = this._previousAttributes; + var changed = false; + for (var attr in now) { + if (!_.isEqual(old[attr], now[attr])) { + changed = changed || {}; + changed[attr] = now[attr]; + } + } + return changed; + }, + + // Get the previous value of an attribute, recorded at the time the last + // `"change"` event was fired. + previous : function(attr) { + if (!attr || !this._previousAttributes) return null; + return this._previousAttributes[attr]; + }, + + // Get all of the attributes of the model at the time of the previous + // `"change"` event. + previousAttributes : function() { + return _.clone(this._previousAttributes); + }, + + // Run validation against a set of incoming attributes, returning `true` + // if all is well. If a specific `error` callback has been passed, + // call that instead of firing the general `"error"` event. + _performValidation : function(attrs, options) { + var error = this.validate(attrs); + if (error) { + if (options.error) { + options.error(this, error, options); + } else { + this.trigger('error', this, error, options); + } + return false; + } + return true; + } + + }); + + // Backbone.Collection + // ------------------- + + // Provides a standard collection class for our sets of models, ordered + // or unordered. If a `comparator` is specified, the Collection will maintain + // its models in sort order, as they're added and removed. + Backbone.Collection = function(models, options) { + options || (options = {}); + if (options.comparator) this.comparator = options.comparator; + _.bindAll(this, '_onModelEvent', '_removeReference'); + this._reset(); + if (models) this.reset(models, {silent: true}); + this.initialize.apply(this, arguments); + }; + + // Define the Collection's inheritable methods. + _.extend(Backbone.Collection.prototype, Backbone.Events, { + + // The default model for a collection is just a **Backbone.Model**. + // This should be overridden in most cases. + model : Backbone.Model, + + // Initialize is an empty function by default. Override it with your own + // initialization logic. + initialize : function(){}, + + // The JSON representation of a Collection is an array of the + // models' attributes. + toJSON : function() { + return this.map(function(model){ return model.toJSON(); }); + }, + + // Add a model, or list of models to the set. Pass **silent** to avoid + // firing the `added` event for every new model. + add : function(models, options) { + if (_.isArray(models)) { + for (var i = 0, l = models.length; i < l; i++) { + this._add(models[i], options); + } + } else { + this._add(models, options); + } + return this; + }, + + // Remove a model, or a list of models from the set. Pass silent to avoid + // firing the `removed` event for every model removed. + remove : function(models, options) { + if (_.isArray(models)) { + for (var i = 0, l = models.length; i < l; i++) { + this._remove(models[i], options); + } + } else { + this._remove(models, options); + } + return this; + }, + + // Get a model from the set by id. + get : function(id) { + if (id == null) return null; + return this._byId[id.id != null ? id.id : id]; + }, + + // Get a model from the set by client id. + getByCid : function(cid) { + return cid && this._byCid[cid.cid || cid]; + }, + + // Get the model at the given index. + at: function(index) { + return this.models[index]; + }, + + // Force the collection to re-sort itself. You don't need to call this under normal + // circumstances, as the set will maintain sort order as each item is added. + sort : function(options) { + options || (options = {}); + if (!this.comparator) throw new Error('Cannot sort a set without a comparator'); + this.models = this.sortBy(this.comparator); + if (!options.silent) this.trigger('reset', this, options); + return this; + }, + + // Pluck an attribute from each model in the collection. + pluck : function(attr) { + return _.map(this.models, function(model){ return model.get(attr); }); + }, + + // When you have more items than you want to add or remove individually, + // you can reset the entire set with a new list of models, without firing + // any `added` or `removed` events. Fires `reset` when finished. + reset : function(models, options) { + models || (models = []); + options || (options = {}); + this.each(this._removeReference); + this._reset(); + this.add(models, {silent: true}); + if (!options.silent) this.trigger('reset', this, options); + return this; + }, + + // Fetch the default set of models for this collection, resetting the + // collection when they arrive. If `add: true` is passed, appends the + // models to the collection instead of resetting. + fetch : function(options) { + options || (options = {}); + var collection = this; + var success = options.success; + options.success = function(resp, status, xhr) { + collection[options.add ? 'add' : 'reset'](collection.parse(resp, xhr), options); + if (success) success(collection, resp); + }; + options.error = wrapError(options.error, collection, options); + return (this.sync || Backbone.sync).call(this, 'read', this, options); + }, + + // Create a new instance of a model in this collection. After the model + // has been created on the server, it will be added to the collection. + // Returns the model, or 'false' if validation on a new model fails. + create : function(model, options) { + var coll = this; + options || (options = {}); + model = this._prepareModel(model, options); + if (!model) return false; + var success = options.success; + options.success = function(nextModel, resp, xhr) { + coll.add(nextModel, options); + if (success) success(nextModel, resp, xhr); + }; + model.save(null, options); + return model; + }, + + // **parse** converts a response into a list of models to be added to the + // collection. The default implementation is just to pass it through. + parse : function(resp, xhr) { + return resp; + }, + + // Proxy to _'s chain. Can't be proxied the same way the rest of the + // underscore methods are proxied because it relies on the underscore + // constructor. + chain: function () { + return _(this.models).chain(); + }, + + // Reset all internal state. Called when the collection is reset. + _reset : function(options) { + this.length = 0; + this.models = []; + this._byId = {}; + this._byCid = {}; + }, + + // Prepare a model to be added to this collection + _prepareModel: function(model, options) { + if (!(model instanceof Backbone.Model)) { + var attrs = model; + model = new this.model(attrs, {collection: this}); + if (model.validate && !model._performValidation(attrs, options)) model = false; + } else if (!model.collection) { + model.collection = this; + } + return model; + }, + + // Internal implementation of adding a single model to the set, updating + // hash indexes for `id` and `cid` lookups. + // Returns the model, or 'false' if validation on a new model fails. + _add : function(model, options) { + options || (options = {}); + model = this._prepareModel(model, options); + if (!model) return false; + var already = this.getByCid(model); + if (already) throw new Error(["Can't add the same model to a set twice", already.id]); + this._byId[model.id] = model; + this._byCid[model.cid] = model; + var index = options.at != null ? options.at : + this.comparator ? this.sortedIndex(model, this.comparator) : + this.length; + this.models.splice(index, 0, model); + model.bind('all', this._onModelEvent); + this.length++; + if (!options.silent) model.trigger('add', model, this, options); + return model; + }, + + // Internal implementation of removing a single model from the set, updating + // hash indexes for `id` and `cid` lookups. + _remove : function(model, options) { + options || (options = {}); + model = this.getByCid(model) || this.get(model); + if (!model) return null; + delete this._byId[model.id]; + delete this._byCid[model.cid]; + this.models.splice(this.indexOf(model), 1); + this.length--; + if (!options.silent) model.trigger('remove', model, this, options); + this._removeReference(model); + return model; + }, + + // Internal method to remove a model's ties to a collection. + _removeReference : function(model) { + if (this == model.collection) { + delete model.collection; + } + model.unbind('all', this._onModelEvent); + }, + + // Internal method called every time a model in the set fires an event. + // Sets need to update their indexes when models change ids. All other + // events simply proxy through. "add" and "remove" events that originate + // in other collections are ignored. + _onModelEvent : function(ev, model, collection, options) { + if ((ev == 'add' || ev == 'remove') && collection != this) return; + if (ev == 'destroy') { + this._remove(model, options); + } + if (model && ev === 'change:' + model.idAttribute) { + delete this._byId[model.previous(model.idAttribute)]; + this._byId[model.id] = model; + } + this.trigger.apply(this, arguments); + } + + }); + + // Underscore methods that we want to implement on the Collection. + var methods = ['forEach', 'each', 'map', 'reduce', 'reduceRight', 'find', 'detect', + 'filter', 'select', 'reject', 'every', 'all', 'some', 'any', 'include', + 'contains', 'invoke', 'max', 'min', 'sortBy', 'sortedIndex', 'toArray', 'size', + 'first', 'rest', 'last', 'without', 'indexOf', 'lastIndexOf', 'isEmpty', 'groupBy']; + + // Mix in each Underscore method as a proxy to `Collection#models`. + _.each(methods, function(method) { + Backbone.Collection.prototype[method] = function() { + return _[method].apply(_, [this.models].concat(_.toArray(arguments))); + }; + }); + + // Backbone.Router + // ------------------- + + // Routers map faux-URLs to actions, and fire events when routes are + // matched. Creating a new one sets its `routes` hash, if not set statically. + Backbone.Router = function(options) { + options || (options = {}); + if (options.routes) this.routes = options.routes; + this._bindRoutes(); + this.initialize.apply(this, arguments); + }; + + // Cached regular expressions for matching named param parts and splatted + // parts of route strings. + var namedParam = /:([\w\d]+)/g; + var splatParam = /\*([\w\d]+)/g; + var escapeRegExp = /[-[\]{}()+?.,\\^$|#\s]/g; + + // Set up all inheritable **Backbone.Router** properties and methods. + _.extend(Backbone.Router.prototype, Backbone.Events, { + + // Initialize is an empty function by default. Override it with your own + // initialization logic. + initialize : function(){}, + + // Manually bind a single named route to a callback. For example: + // + // this.route('search/:query/p:num', 'search', function(query, num) { + // ... + // }); + // + route : function(route, name, callback) { + Backbone.history || (Backbone.history = new Backbone.History); + if (!_.isRegExp(route)) route = this._routeToRegExp(route); + Backbone.history.route(route, _.bind(function(fragment) { + var args = this._extractParameters(route, fragment); + callback.apply(this, args); + this.trigger.apply(this, ['route:' + name].concat(args)); + }, this)); + }, + + // Simple proxy to `Backbone.history` to save a fragment into the history. + navigate : function(fragment, triggerRoute) { + Backbone.history.navigate(fragment, triggerRoute); + }, + + // Bind all defined routes to `Backbone.history`. We have to reverse the + // order of the routes here to support behavior where the most general + // routes can be defined at the bottom of the route map. + _bindRoutes : function() { + if (!this.routes) return; + var routes = []; + for (var route in this.routes) { + routes.unshift([route, this.routes[route]]); + } + for (var i = 0, l = routes.length; i < l; i++) { + this.route(routes[i][0], routes[i][1], this[routes[i][1]]); + } + }, + + // Convert a route string into a regular expression, suitable for matching + // against the current location hash. + _routeToRegExp : function(route) { + route = route.replace(escapeRegExp, "\\$&") + .replace(namedParam, "([^\/]*)") + .replace(splatParam, "(.*?)"); + return new RegExp('^' + route + '$'); + }, + + // Given a route, and a URL fragment that it matches, return the array of + // extracted parameters. + _extractParameters : function(route, fragment) { + return route.exec(fragment).slice(1); + } + + }); + + // Backbone.History + // ---------------- + + // Handles cross-browser history management, based on URL fragments. If the + // browser does not support `onhashchange`, falls back to polling. + Backbone.History = function() { + this.handlers = []; + _.bindAll(this, 'checkUrl'); + }; + + // Cached regex for cleaning hashes. + var hashStrip = /^#*/; + + // Cached regex for detecting MSIE. + var isExplorer = /msie [\w.]+/; + + // Has the history handling already been started? + var historyStarted = false; + + // Set up all inheritable **Backbone.History** properties and methods. + _.extend(Backbone.History.prototype, { + + // The default interval to poll for hash changes, if necessary, is + // twenty times a second. + interval: 50, + + // Get the cross-browser normalized URL fragment, either from the URL, + // the hash, or the override. + getFragment : function(fragment, forcePushState) { + if (fragment == null) { + if (this._hasPushState || forcePushState) { + fragment = window.location.pathname; + var search = window.location.search; + if (search) fragment += search; + if (fragment.indexOf(this.options.root) == 0) fragment = fragment.substr(this.options.root.length); + } else { + fragment = window.location.hash; + } + } + return decodeURIComponent(fragment.replace(hashStrip, '')); + }, + + // Start the hash change handling, returning `true` if the current URL matches + // an existing route, and `false` otherwise. + start : function(options) { + + // Figure out the initial configuration. Do we need an iframe? + // Is pushState desired ... is it available? + if (historyStarted) throw new Error("Backbone.history has already been started"); + this.options = _.extend({}, {root: '/'}, this.options, options); + this._wantsPushState = !!this.options.pushState; + this._hasPushState = !!(this.options.pushState && window.history && window.history.pushState); + var fragment = this.getFragment(); + var docMode = document.documentMode; + var oldIE = (isExplorer.exec(navigator.userAgent.toLowerCase()) && (!docMode || docMode <= 7)); + if (oldIE) { + this.iframe = $('