diff --git a/addons/point_of_sale/__openerp__.py b/addons/point_of_sale/__openerp__.py index 9d945529224..63e8fc84291 100644 --- a/addons/point_of_sale/__openerp__.py +++ b/addons/point_of_sale/__openerp__.py @@ -41,8 +41,8 @@ Main Features 'wizard/pos_discount.xml', 'wizard/pos_open_statement.xml', 'wizard/pos_payment.xml', - 'wizard/pos_session_opening.xml', 'views/templates.xml', + 'point_of_sale.xml', 'point_of_sale_report.xml', 'point_of_sale_view.xml', 'point_of_sale_sequence.xml', @@ -63,6 +63,7 @@ Main Features 'views/report_sessionsummary.xml', 'views/report_userlabel.xml', 'views/point_of_sale.xml', + 'point_of_sale_dashboard.xml', ], 'demo': [ 'point_of_sale_demo.xml', diff --git a/addons/point_of_sale/point_of_sale.py b/addons/point_of_sale/point_of_sale.py index 98ddeb9dae7..510958f4f85 100644 --- a/addons/point_of_sale/point_of_sale.py +++ b/addons/point_of_sale/point_of_sale.py @@ -39,6 +39,45 @@ class pos_config(osv.osv): result[pos_config.id] = currency_id return result + def _get_current_session(self, cr, uid, ids, fieldnames, args, context=None): + result = dict() + + for record in self.browse(cr, uid, ids, context=context): + session_id = record.session_ids.filtered(lambda r: r.user_id.id == uid and not r.state == 'closed') + result[record.id] = { + 'current_session_id': session_id, + 'current_session_state': session_id.state, + } + return result + + def _get_last_session(self, cr, uid, ids, fieldnames, args, context=None): + result = dict() + + for record in self.browse(cr, uid, ids, context=context): + session_ids = self.pool['pos.session'].search_read( + cr, uid, + [('config_id', '=', record.id), ('state', '=', 'closed')], + ['cash_register_balance_end_real', 'stop_at'], + order="stop_at desc", limit=1, context=context) + if session_ids: + result[record.id] = { + 'last_session_closing_cash': session_ids[0]['cash_register_balance_end_real'], + 'last_session_closing_date': session_ids[0]['stop_at'], + } + else: + result[record.id] = { + 'last_session_closing_cash': 0, + 'last_session_closing_date': None, + } + return result + + def _get_current_session_user(self, cr, uid, ids, fieldnames, args, context=None): + result = dict() + + for record in self.browse(cr, uid, ids, context=context): + result[record.id] = record.session_ids.filtered(lambda r: r.state == 'opened').user_id.name + return result + _columns = { 'name' : fields.char('Point of Sale Name', select=1, required=True, help="An internal identification of the point of sale"), @@ -76,6 +115,11 @@ class pos_config(osv.osv): help="This sequence is automatically created by Odoo but you can change it "\ "to customize the reference numbers of your orders.", copy=False), 'session_ids': fields.one2many('pos.session', 'config_id', 'Sessions'), + 'current_session_id': fields.function(_get_current_session, multi="session", type="many2one", relation="pos.session", string="Current Session"), + 'current_session_state': fields.function(_get_current_session, multi="session", type='char'), + 'last_session_closing_cash': fields.function(_get_last_session, multi="last_session", type='float'), + 'last_session_closing_date': fields.function(_get_last_session, multi="last_session", type='date'), + 'pos_session_username': fields.function(_get_current_session_user, type='char'), 'group_by' : fields.boolean('Group Journal Items', help="Check this if you want to group the Journal Items by Product while closing a Session"), 'pricelist_id': fields.many2one('product.pricelist','Pricelist', required=True), 'company_id': fields.many2one('res.company', 'Company', required=True), @@ -231,6 +275,62 @@ class pos_config(osv.osv): obj.sequence_id.unlink() return super(pos_config, self).unlink(cr, uid, ids, context=context) + # Methods to open the POS + + def open_ui(self, cr, uid, ids, context=None): + assert len(ids) == 1, "you can open only one session at a time" + + record = self.browse(cr, uid, ids[0], context=context) + context = dict(context or {}) + context['active_id'] = record.current_session_id.id + return { + 'type': 'ir.actions.act_url', + 'url': '/pos/web/', + 'target': 'self', + } + + def open_existing_session_cb_close(self, cr, uid, ids, context=None): + assert len(ids) == 1, "you can open only one session at a time" + + record = self.browse(cr, uid, ids[0], context=context) + record.current_session_id.signal_workflow('cashbox_control') + return self.open_session_cb(cr, uid, ids, context) + + def open_session_cb(self, cr, uid, ids, context=None): + assert len(ids) == 1, "you can open only one session at a time" + + proxy = self.pool.get('pos.session') + record = self.browse(cr, uid, ids[0], context=context) + current_session_id = record.current_session_id + if not current_session_id: + values = { + 'user_id': uid, + 'config_id': record.id, + } + session_id = proxy.create(cr, uid, values, context=context) + record.current_session_id = proxy.browse(cr, uid, session_id, context=context) + if record.current_session_id.state == 'opened': + return self.open_ui(cr, uid, ids, context=context) + return self._open_session(session_id) + return self._open_session(current_session_id.id) + + def open_existing_session_cb(self, cr, uid, ids, context=None): + assert len(ids) == 1, "you can open only one session at a time" + + record = self.browse(cr, uid, ids[0], context=context) + return self._open_session(record.current_session_id.id) + + def _open_session(self, session_id): + return { + 'name': _('Session'), + 'view_type': 'form', + 'view_mode': 'form,tree', + 'res_model': 'pos.session', + 'res_id': session_id, + 'view_id': False, + 'type': 'ir.actions.act_window', + } + class pos_session(osv.osv): _name = 'pos.session' _order = 'id desc' @@ -739,7 +839,7 @@ class pos_order(osv.osv): domain="[('state', '=', 'opened')]", states={'draft' : [('readonly', False)]}, readonly=True), - + 'config_id': fields.related('session_id', 'config_id', string="Point of Sale", type='many2one' , relation='pos.config'), 'state': fields.selection([('draft', 'New'), ('cancel', 'Cancelled'), ('paid', 'Paid'), diff --git a/addons/point_of_sale/point_of_sale.xml b/addons/point_of_sale/point_of_sale.xml new file mode 100644 index 00000000000..979c8e98d4c --- /dev/null +++ b/addons/point_of_sale/point_of_sale.xml @@ -0,0 +1,8 @@ + + + + diff --git a/addons/point_of_sale/point_of_sale_dashboard.xml b/addons/point_of_sale/point_of_sale_dashboard.xml new file mode 100644 index 00000000000..56fe1d5e667 --- /dev/null +++ b/addons/point_of_sale/point_of_sale_dashboard.xml @@ -0,0 +1,140 @@ + + + + + Sessions + ir.actions.act_window + pos.session + form + tree,form + + { + 'search_default_config_id': [active_id], + 'default_config_id': active_id} + + + + + Orders + ir.actions.act_window + pos.order + form + tree,form + + { + 'search_default_config_id': [active_id], + 'default_config_id': active_id} + + + + + Orders Analysis + report.pos.order + form + pivot,graph + + { + 'search_default_config_id': [active_id], + 'default_config_id': active_id} + + + + + + + pos.config.kanban.view + pos.config + + + + + + + + + + + + +
+
+
+
+
+
+
+ More +
+
+
+
+
+ + + + + + + + +
+
+ +
+
+ Last Closing Date +
+
+ +
+
+ +
+
+ Cash Balance +
+
+ +
+
+
+
+
+
+
+
+ View +
+ +
+ Sessions +
+ +
+
+
+ Reports +
+ +
+ Orders +
+
+
+ +
+
+ Settings +
+
+
+
+
+
+
+
+
+ +
diff --git a/addons/point_of_sale/point_of_sale_view.xml b/addons/point_of_sale/point_of_sale_view.xml index b77e8980305..d1b7af2eee0 100644 --- a/addons/point_of_sale/point_of_sale_view.xml +++ b/addons/point_of_sale/point_of_sale_view.xml @@ -160,6 +160,17 @@ + + pos.order.search.view + pos.order + + + + + + + + @@ -943,7 +954,23 @@ - + Point of Sale + ir.actions.act_window + pos.config + form + kanban,tree,form + + +

+ Click to create a new PoS config. +

+ Configure at least one Point of Sale to be able to sell through the PoS interface. +

+
+ + + diff --git a/addons/point_of_sale/report/pos_order_report_view.xml b/addons/point_of_sale/report/pos_order_report_view.xml index ecd0136a2f8..724136b1bb3 100644 --- a/addons/point_of_sale/report/pos_order_report_view.xml +++ b/addons/point_of_sale/report/pos_order_report_view.xml @@ -37,6 +37,7 @@ + diff --git a/addons/point_of_sale/static/src/js/tests.js b/addons/point_of_sale/static/src/js/tests.js index 9e55f318f42..6be47e8ff10 100644 --- a/addons/point_of_sale/static/src/js/tests.js +++ b/addons/point_of_sale/static/src/js/tests.js @@ -6,7 +6,7 @@ var Tour = require('web.Tour'); Tour.register({ id: 'pos_basic_order', name: 'Complete a basic order trough the Front-End', - path: '/web#model=pos.session.opening&action=point_of_sale.action_pos_session_opening', + path: '/web#model=pos.config&action=point_of_sale.action_pos_config_kanban', mode: 'test', steps: [ { diff --git a/addons/point_of_sale/static/src/less/pos_dashboard.less b/addons/point_of_sale/static/src/less/pos_dashboard.less new file mode 100644 index 00000000000..3d8e5f95971 --- /dev/null +++ b/addons/point_of_sale/static/src/less/pos_dashboard.less @@ -0,0 +1,9 @@ +.o_kanban_view.o_kanban_dashboard.o_pos_kanban.o_kanban_ungrouped { + .o_kanban_record { + min-width: 500px; + + @media (max-width: @grid-float-breakpoint) { + min-width: inherit; + } + } +} diff --git a/addons/point_of_sale/test/01_order_to_payment.yml b/addons/point_of_sale/test/01_order_to_payment.yml index 1e339048af2..46b7b2b1444 100644 --- a/addons/point_of_sale/test/01_order_to_payment.yml +++ b/addons/point_of_sale/test/01_order_to_payment.yml @@ -38,26 +38,11 @@ - !record {model: product.product, id: product.product_product_4, view: False}: taxes_id: [account_tax_05_incl, account_tax_05_incl_chicago] -- - I create a new session -- - !record {model: pos.session, id: pos_order_session0}: - user_id: 1 - config_id: point_of_sale.pos_config_main -- - I open a new PoS Session wizard -- - !record {model: pos.session.opening, id: pos_order_session_wizard0}: - pos_config_id: point_of_sale.pos_config_main - I click on create a new session button - - !python {model: pos.session.opening}: | - self.open_existing_session_cb(cr, uid, [ref('pos_order_session_wizard0')]) -- - I open the session after having counted the money -- - !workflow {model: pos.session, action: open, ref: pos_order_session0} + !python {model: pos.config}: | + self.open_session_cb(cr, uid, [ref('pos_config_main')]) - I create a PoS order with 2 units of PCSC234 at 450 EUR (Tax Incl) and 3 units of PCSC349 at 300 EUR. (Tax Excl) - diff --git a/addons/point_of_sale/wizard/__init__.py b/addons/point_of_sale/wizard/__init__.py index f37abc3a6d1..2cd077955ca 100644 --- a/addons/point_of_sale/wizard/__init__.py +++ b/addons/point_of_sale/wizard/__init__.py @@ -7,4 +7,3 @@ import pos_details import pos_discount import pos_open_statement import pos_payment -import pos_session_opening diff --git a/addons/point_of_sale/wizard/pos_session_opening.py b/addons/point_of_sale/wizard/pos_session_opening.py deleted file mode 100644 index 39348a8a5a2..00000000000 --- a/addons/point_of_sale/wizard/pos_session_opening.py +++ /dev/null @@ -1,116 +0,0 @@ - -from openerp.osv import osv, fields -from openerp.tools.translate import _ - -from openerp.addons.point_of_sale.point_of_sale import pos_session - - -class pos_session_opening(osv.osv_memory): - _name = 'pos.session.opening' - - _columns = { - 'pos_config_id' : fields.many2one('pos.config', string='Point of Sale', required=True), - 'pos_session_id' : fields.many2one('pos.session', string='PoS Session'), - 'pos_state' : fields.related('pos_session_id', 'state', - type='selection', - selection=pos_session.POS_SESSION_STATE, - string='Session Status', readonly=True), - 'pos_state_str' : fields.char('Status', readonly=True), - 'show_config' : fields.boolean('Show Config', readonly=True), - 'pos_session_name' : fields.related('pos_session_id', 'name', string="Session Name", - type='char', size=64, readonly=True), - 'pos_session_username' : fields.related('pos_session_id', 'user_id', 'name', - type='char', size=64, readonly=True) - } - - def open_ui(self, cr, uid, ids, context=None): - data = self.browse(cr, uid, ids[0], context=context) - context = dict(context or {}) - context['active_id'] = data.pos_session_id.id - return { - 'type' : 'ir.actions.act_url', - 'url': '/pos/web/', - 'target': 'self', - } - - def open_existing_session_cb_close(self, cr, uid, ids, context=None): - wizard = self.browse(cr, uid, ids[0], context=context) - wizard.pos_session_id.signal_workflow('cashbox_control') - return self.open_session_cb(cr, uid, ids, context) - - def open_session_cb(self, cr, uid, ids, context=None): - assert len(ids) == 1, "you can open only one session at a time" - proxy = self.pool.get('pos.session') - wizard = self.browse(cr, uid, ids[0], context=context) - if not wizard.pos_session_id: - values = { - 'user_id' : uid, - 'config_id' : wizard.pos_config_id.id, - } - session_id = proxy.create(cr, uid, values, context=context) - s = proxy.browse(cr, uid, session_id, context=context) - if s.state=='opened': - return self.open_ui(cr, uid, ids, context=context) - return self._open_session(session_id) - return self._open_session(wizard.pos_session_id.id) - - def open_existing_session_cb(self, cr, uid, ids, context=None): - assert len(ids) == 1 - wizard = self.browse(cr, uid, ids[0], context=context) - return self._open_session(wizard.pos_session_id.id) - - def _open_session(self, session_id): - return { - 'name': _('Session'), - 'view_type': 'form', - 'view_mode': 'form,tree', - 'res_model': 'pos.session', - 'res_id': session_id, - 'view_id': False, - 'type': 'ir.actions.act_window', - } - - def on_change_config(self, cr, uid, ids, config_id, context=None): - result = { - 'pos_session_id': False, - 'pos_state': False, - 'pos_state_str' : '', - 'pos_session_username' : False, - 'pos_session_name' : False, - } - if not config_id: - return {'value' : result} - proxy = self.pool.get('pos.session') - session_ids = proxy.search(cr, uid, [ - ('state', '!=', 'closed'), - ('config_id', '=', config_id), - ('user_id', '=', uid), - ], context=context) - if session_ids: - session = proxy.browse(cr, uid, session_ids[0], context=context) - result['pos_state'] = str(session.state) - result['pos_state_str'] = dict(pos_session.POS_SESSION_STATE).get(session.state, '') - result['pos_session_id'] = session.id - result['pos_session_name'] = session.name - result['pos_session_username'] = session.user_id.name - - return {'value' : result} - - def default_get(self, cr, uid, fieldnames, context=None): - so = self.pool.get('pos.session') - session_ids = so.search(cr, uid, [('state','<>','closed'), ('user_id','=',uid)], context=context) - if session_ids: - result = so.browse(cr, uid, session_ids[0], context=context).config_id.id - else: - current_user = self.pool.get('res.users').browse(cr, uid, uid, context=context) - result = current_user.pos_config and current_user.pos_config.id or False - if not result: - r = self.pool.get('pos.config').search(cr, uid, [], context=context) - result = r and r[0] or False - - count = self.pool.get('pos.config').search_count(cr, uid, [('state', '=', 'active')], context=context) - show_config = bool(count > 1) - return { - 'pos_config_id' : result, - 'show_config' : show_config, - } diff --git a/addons/point_of_sale/wizard/pos_session_opening.xml b/addons/point_of_sale/wizard/pos_session_opening.xml deleted file mode 100644 index d21f6687c8c..00000000000 --- a/addons/point_of_sale/wizard/pos_session_opening.xml +++ /dev/null @@ -1,69 +0,0 @@ - - - - - pos.session.opening.form.view - pos.session.opening - -
- - - - - - - -