From d1c3c6518d8bcaf93b3b679cb836cbbdae75526f Mon Sep 17 00:00:00 2001
From: Martin Trigaux
Date: Wed, 17 Feb 2016 15:40:24 +0100
Subject: [PATCH 1/9] [REF] point_of_sale: move files to appropriate folders
---
addons/point_of_sale/__init__.py | 6 +-----
addons/point_of_sale/{ => data}/point_of_sale_data.xml | 0
addons/point_of_sale/{ => data}/point_of_sale_demo.xml | 0
.../point_of_sale/{ => data}/point_of_sale_workflow.xml | 0
addons/point_of_sale/models/__init__.py | 8 ++++++++
.../point_of_sale/{ => models}/account_bank_statement.py | 0
addons/point_of_sale/{ => models}/point_of_sale.py | 0
addons/point_of_sale/{ => models}/res_config.py | 0
addons/point_of_sale/{ => models}/res_partner.py | 0
addons/point_of_sale/{ => models}/res_users.py | 0
.../{ => views}/account_statement_report.xml | 0
.../point_of_sale/{ => views}/account_statement_view.xml | 0
.../point_of_sale/{ => views}/point_of_sale_dashboard.xml | 0
addons/point_of_sale/{ => views}/point_of_sale_report.xml | 0
.../point_of_sale/{ => views}/point_of_sale_sequence.xml | 0
.../point_of_sale_template.xml} | 0
addons/point_of_sale/{ => views}/point_of_sale_view.xml | 0
.../views/{templates.xml => pos_templates.xml} | 0
addons/point_of_sale/{ => views}/res_config_view.xml | 0
addons/point_of_sale/{ => views}/res_partner_view.xml | 0
addons/point_of_sale/{ => views}/res_users_view.xml | 0
21 files changed, 9 insertions(+), 5 deletions(-)
rename addons/point_of_sale/{ => data}/point_of_sale_data.xml (100%)
rename addons/point_of_sale/{ => data}/point_of_sale_demo.xml (100%)
rename addons/point_of_sale/{ => data}/point_of_sale_workflow.xml (100%)
create mode 100644 addons/point_of_sale/models/__init__.py
rename addons/point_of_sale/{ => models}/account_bank_statement.py (100%)
rename addons/point_of_sale/{ => models}/point_of_sale.py (100%)
rename addons/point_of_sale/{ => models}/res_config.py (100%)
rename addons/point_of_sale/{ => models}/res_partner.py (100%)
rename addons/point_of_sale/{ => models}/res_users.py (100%)
rename addons/point_of_sale/{ => views}/account_statement_report.xml (100%)
rename addons/point_of_sale/{ => views}/account_statement_view.xml (100%)
rename addons/point_of_sale/{ => views}/point_of_sale_dashboard.xml (100%)
rename addons/point_of_sale/{ => views}/point_of_sale_report.xml (100%)
rename addons/point_of_sale/{ => views}/point_of_sale_sequence.xml (100%)
rename addons/point_of_sale/{point_of_sale.xml => views/point_of_sale_template.xml} (100%)
rename addons/point_of_sale/{ => views}/point_of_sale_view.xml (100%)
rename addons/point_of_sale/views/{templates.xml => pos_templates.xml} (100%)
rename addons/point_of_sale/{ => views}/res_config_view.xml (100%)
rename addons/point_of_sale/{ => views}/res_partner_view.xml (100%)
rename addons/point_of_sale/{ => views}/res_users_view.xml (100%)
diff --git a/addons/point_of_sale/__init__.py b/addons/point_of_sale/__init__.py
index f1451fa6494..ff29fd64226 100644
--- a/addons/point_of_sale/__init__.py
+++ b/addons/point_of_sale/__init__.py
@@ -1,11 +1,7 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
-import account_bank_statement
+import models
import controllers
-import point_of_sale
import report
-import res_users
-import res_partner
import wizard
-import res_config
diff --git a/addons/point_of_sale/point_of_sale_data.xml b/addons/point_of_sale/data/point_of_sale_data.xml
similarity index 100%
rename from addons/point_of_sale/point_of_sale_data.xml
rename to addons/point_of_sale/data/point_of_sale_data.xml
diff --git a/addons/point_of_sale/point_of_sale_demo.xml b/addons/point_of_sale/data/point_of_sale_demo.xml
similarity index 100%
rename from addons/point_of_sale/point_of_sale_demo.xml
rename to addons/point_of_sale/data/point_of_sale_demo.xml
diff --git a/addons/point_of_sale/point_of_sale_workflow.xml b/addons/point_of_sale/data/point_of_sale_workflow.xml
similarity index 100%
rename from addons/point_of_sale/point_of_sale_workflow.xml
rename to addons/point_of_sale/data/point_of_sale_workflow.xml
diff --git a/addons/point_of_sale/models/__init__.py b/addons/point_of_sale/models/__init__.py
new file mode 100644
index 00000000000..d529cbf25e0
--- /dev/null
+++ b/addons/point_of_sale/models/__init__.py
@@ -0,0 +1,8 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+import account_bank_statement
+import point_of_sale
+import res_config
+import res_partner
+import res_users
diff --git a/addons/point_of_sale/account_bank_statement.py b/addons/point_of_sale/models/account_bank_statement.py
similarity index 100%
rename from addons/point_of_sale/account_bank_statement.py
rename to addons/point_of_sale/models/account_bank_statement.py
diff --git a/addons/point_of_sale/point_of_sale.py b/addons/point_of_sale/models/point_of_sale.py
similarity index 100%
rename from addons/point_of_sale/point_of_sale.py
rename to addons/point_of_sale/models/point_of_sale.py
diff --git a/addons/point_of_sale/res_config.py b/addons/point_of_sale/models/res_config.py
similarity index 100%
rename from addons/point_of_sale/res_config.py
rename to addons/point_of_sale/models/res_config.py
diff --git a/addons/point_of_sale/res_partner.py b/addons/point_of_sale/models/res_partner.py
similarity index 100%
rename from addons/point_of_sale/res_partner.py
rename to addons/point_of_sale/models/res_partner.py
diff --git a/addons/point_of_sale/res_users.py b/addons/point_of_sale/models/res_users.py
similarity index 100%
rename from addons/point_of_sale/res_users.py
rename to addons/point_of_sale/models/res_users.py
diff --git a/addons/point_of_sale/account_statement_report.xml b/addons/point_of_sale/views/account_statement_report.xml
similarity index 100%
rename from addons/point_of_sale/account_statement_report.xml
rename to addons/point_of_sale/views/account_statement_report.xml
diff --git a/addons/point_of_sale/account_statement_view.xml b/addons/point_of_sale/views/account_statement_view.xml
similarity index 100%
rename from addons/point_of_sale/account_statement_view.xml
rename to addons/point_of_sale/views/account_statement_view.xml
diff --git a/addons/point_of_sale/point_of_sale_dashboard.xml b/addons/point_of_sale/views/point_of_sale_dashboard.xml
similarity index 100%
rename from addons/point_of_sale/point_of_sale_dashboard.xml
rename to addons/point_of_sale/views/point_of_sale_dashboard.xml
diff --git a/addons/point_of_sale/point_of_sale_report.xml b/addons/point_of_sale/views/point_of_sale_report.xml
similarity index 100%
rename from addons/point_of_sale/point_of_sale_report.xml
rename to addons/point_of_sale/views/point_of_sale_report.xml
diff --git a/addons/point_of_sale/point_of_sale_sequence.xml b/addons/point_of_sale/views/point_of_sale_sequence.xml
similarity index 100%
rename from addons/point_of_sale/point_of_sale_sequence.xml
rename to addons/point_of_sale/views/point_of_sale_sequence.xml
diff --git a/addons/point_of_sale/point_of_sale.xml b/addons/point_of_sale/views/point_of_sale_template.xml
similarity index 100%
rename from addons/point_of_sale/point_of_sale.xml
rename to addons/point_of_sale/views/point_of_sale_template.xml
diff --git a/addons/point_of_sale/point_of_sale_view.xml b/addons/point_of_sale/views/point_of_sale_view.xml
similarity index 100%
rename from addons/point_of_sale/point_of_sale_view.xml
rename to addons/point_of_sale/views/point_of_sale_view.xml
diff --git a/addons/point_of_sale/views/templates.xml b/addons/point_of_sale/views/pos_templates.xml
similarity index 100%
rename from addons/point_of_sale/views/templates.xml
rename to addons/point_of_sale/views/pos_templates.xml
diff --git a/addons/point_of_sale/res_config_view.xml b/addons/point_of_sale/views/res_config_view.xml
similarity index 100%
rename from addons/point_of_sale/res_config_view.xml
rename to addons/point_of_sale/views/res_config_view.xml
diff --git a/addons/point_of_sale/res_partner_view.xml b/addons/point_of_sale/views/res_partner_view.xml
similarity index 100%
rename from addons/point_of_sale/res_partner_view.xml
rename to addons/point_of_sale/views/res_partner_view.xml
diff --git a/addons/point_of_sale/res_users_view.xml b/addons/point_of_sale/views/res_users_view.xml
similarity index 100%
rename from addons/point_of_sale/res_users_view.xml
rename to addons/point_of_sale/views/res_users_view.xml
From fcdd43010ac060b29901d7d8e1957c3545d7664e Mon Sep 17 00:00:00 2001
From: Darshan Kalola
Date: Thu, 24 Sep 2015 14:43:25 +0530
Subject: [PATCH 2/9] [REF] point_of_sale: split files per model
---
addons/point_of_sale/__openerp__.py | 39 +-
addons/point_of_sale/models/__init__.py | 8 +-
.../models/account_bank_statement.py | 15 +-
.../point_of_sale/models/account_journal.py | 27 +
addons/point_of_sale/models/barcode_rule.py | 20 +
addons/point_of_sale/models/pos_category.py | 71 ++
addons/point_of_sale/models/pos_config.py | 324 ++++++
.../models/{point_of_sale.py => pos_order.py} | 750 +------------
addons/point_of_sale/models/pos_session.py | 307 ++++++
addons/point_of_sale/models/product.py | 27 +
addons/point_of_sale/models/res_partner.py | 20 +-
.../views/account_invoice_view.xml | 13 +
.../views/account_journal_view.xml | 29 +
.../views/point_of_sale_report.xml | 218 ++++
.../views/point_of_sale_view.xml | 984 ------------------
.../point_of_sale/views/pos_category_view.xml | 59 ++
.../point_of_sale/views/pos_config_view.xml | 147 +++
.../pos_order_report_view.xml | 0
addons/point_of_sale/views/pos_order_view.xml | 279 +++++
.../point_of_sale/views/pos_session_view.xml | 179 ++++
addons/point_of_sale/views/product_view.xml | 59 ++
addons/point_of_sale/wizard/pos_payment.py | 19 +-
22 files changed, 1818 insertions(+), 1776 deletions(-)
create mode 100644 addons/point_of_sale/models/account_journal.py
create mode 100644 addons/point_of_sale/models/barcode_rule.py
create mode 100644 addons/point_of_sale/models/pos_category.py
create mode 100644 addons/point_of_sale/models/pos_config.py
rename addons/point_of_sale/models/{point_of_sale.py => pos_order.py} (53%)
create mode 100644 addons/point_of_sale/models/pos_session.py
create mode 100644 addons/point_of_sale/models/product.py
create mode 100644 addons/point_of_sale/views/account_invoice_view.xml
create mode 100644 addons/point_of_sale/views/account_journal_view.xml
create mode 100644 addons/point_of_sale/views/pos_category_view.xml
create mode 100644 addons/point_of_sale/views/pos_config_view.xml
rename addons/point_of_sale/{report => views}/pos_order_report_view.xml (100%)
create mode 100644 addons/point_of_sale/views/pos_order_view.xml
create mode 100644 addons/point_of_sale/views/pos_session_view.xml
create mode 100644 addons/point_of_sale/views/product_view.xml
diff --git a/addons/point_of_sale/__openerp__.py b/addons/point_of_sale/__openerp__.py
index 6d34f8a5cc1..f3c455b9023 100644
--- a/addons/point_of_sale/__openerp__.py
+++ b/addons/point_of_sale/__openerp__.py
@@ -30,28 +30,35 @@ Main Features
""",
'depends': ['sale_stock', 'barcodes'],
'data': [
- 'data/default_barcode_patterns.xml',
'security/point_of_sale_security.xml',
'security/ir.model.access.csv',
+ 'data/default_barcode_patterns.xml',
'wizard/pos_box.xml',
'wizard/pos_confirm.xml',
'wizard/pos_details.xml',
'wizard/pos_discount.xml',
'wizard/pos_open_statement.xml',
'wizard/pos_payment.xml',
- 'views/templates.xml',
- 'point_of_sale.xml',
- 'point_of_sale_report.xml',
- 'point_of_sale_view.xml',
- 'point_of_sale_sequence.xml',
- 'point_of_sale_data.xml',
- 'report/pos_order_report_view.xml',
- 'point_of_sale_workflow.xml',
- 'account_statement_view.xml',
- 'account_statement_report.xml',
- 'res_users_view.xml',
- 'res_partner_view.xml',
- 'res_config_view.xml',
+ 'views/pos_templates.xml',
+ 'views/point_of_sale_template.xml',
+ 'views/point_of_sale_report.xml',
+ 'views/point_of_sale_view.xml',
+ 'views/pos_order_view.xml',
+ 'views/product_view.xml',
+ 'views/pos_category_view.xml',
+ 'views/account_journal_view.xml',
+ 'views/pos_config_view.xml',
+ 'views/pos_session_view.xml',
+ 'views/account_invoice_view.xml',
+ 'views/point_of_sale_sequence.xml',
+ 'data/point_of_sale_data.xml',
+ 'data/point_of_sale_workflow.xml',
+ 'views/pos_order_report_view.xml',
+ 'views/account_statement_view.xml',
+ 'views/account_statement_report.xml',
+ 'views/res_users_view.xml',
+ 'views/res_partner_view.xml',
+ 'views/res_config_view.xml',
'views/report_statement.xml',
'views/report_usersproduct.xml',
'views/report_saleslines.xml',
@@ -60,10 +67,10 @@ Main Features
'views/report_sessionsummary.xml',
'views/report_userlabel.xml',
'views/point_of_sale.xml',
- 'point_of_sale_dashboard.xml',
+ 'views/point_of_sale_dashboard.xml',
],
'demo': [
- 'point_of_sale_demo.xml',
+ 'data/point_of_sale_demo.xml',
],
'test': [
'../account/test/account_minimal_test.xml',
diff --git a/addons/point_of_sale/models/__init__.py b/addons/point_of_sale/models/__init__.py
index d529cbf25e0..7170dbcf350 100644
--- a/addons/point_of_sale/models/__init__.py
+++ b/addons/point_of_sale/models/__init__.py
@@ -2,7 +2,13 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import account_bank_statement
-import point_of_sale
+import account_journal
+import barcode_rule
+import pos_category
+import pos_config
+import pos_order
+import pos_session
+import product
import res_config
import res_partner
import res_users
diff --git a/addons/point_of_sale/models/account_bank_statement.py b/addons/point_of_sale/models/account_bank_statement.py
index 5faa31b3007..098b750beba 100644
--- a/addons/point_of_sale/models/account_bank_statement.py
+++ b/addons/point_of_sale/models/account_bank_statement.py
@@ -5,14 +5,6 @@
from openerp.osv import fields, osv
-class account_journal(osv.osv):
- _inherit = 'account.journal'
- _columns = {
- 'journal_user': fields.boolean('Active in Point of Sale', help="Check this box if this journal define a payment method that can be used in a point of sale."),
-
- 'amount_authorized_diff' : fields.float('Amount Authorized Difference', help="This field depicts the maximum difference allowed between the ending balance and the theoretical cash when closing a session, for non-POS managers. If this maximum is reached, the user will have an error message at the closing of his session saying that he needs to contact his manager."),
- }
-
class account_bank_statement(osv.osv):
_inherit = 'account.bank.statement'
@@ -20,3 +12,10 @@ class account_bank_statement(osv.osv):
'pos_session_id' : fields.many2one('pos.session', string="Session", copy=False),
'account_id': fields.related('journal_id', 'default_debit_account_id', type='many2one', relation='account.account', readonly=True),
}
+
+
+class account_bank_statement_line(osv.osv):
+ _inherit = 'account.bank.statement.line'
+ _columns= {
+ 'pos_statement_id': fields.many2one('pos.order', string="POS statement", ondelete='cascade'),
+ }
diff --git a/addons/point_of_sale/models/account_journal.py b/addons/point_of_sale/models/account_journal.py
new file mode 100644
index 00000000000..f9310410467
--- /dev/null
+++ b/addons/point_of_sale/models/account_journal.py
@@ -0,0 +1,27 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+# Copyright (C) 2004-2008 PC Solutions (). All Rights Reserved
+from openerp.osv import fields, osv
+
+class account_journal(osv.osv):
+ _inherit = 'account.journal'
+ _columns = {
+ 'journal_user': fields.boolean('Active in Point of Sale', help="Check this box if this journal define a payment method that can be used in a point of sale."),
+
+ 'amount_authorized_diff' : fields.float('Amount Authorized Difference', help="This field depicts the maximum difference allowed between the ending balance and the theoretical cash when closing a session, for non-POS managers. If this maximum is reached, the user will have an error message at the closing of his session saying that he needs to contact his manager."),
+ }
+
+ def search(self, cr, uid, args, offset=0, limit=None, order=None, context=None, count=False):
+ if not context:
+ context = {}
+ session_id = context.get('pos_session_id', False) or False
+
+ if session_id:
+ session = self.pool.get('pos.session').browse(cr, uid, session_id, context=context)
+
+ if session:
+ journal_ids = [journal.id for journal in session.config_id.journal_ids]
+ args += [('id', 'in', journal_ids)]
+
+ return super(account_journal, self).search(cr, uid, args, offset=offset, limit=limit, order=order, context=context, count=count)
diff --git a/addons/point_of_sale/models/barcode_rule.py b/addons/point_of_sale/models/barcode_rule.py
new file mode 100644
index 00000000000..f1f5e2abbba
--- /dev/null
+++ b/addons/point_of_sale/models/barcode_rule.py
@@ -0,0 +1,20 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+import sets
+from openerp import models
+from openerp.tools.translate import _
+
+
+class barcode_rule(models.Model):
+ _inherit = 'barcode.rule'
+
+ def _get_type_selection(self):
+ types = sets.Set(super(barcode_rule,self)._get_type_selection())
+ types.update([
+ ('weight', _('Weighted Product')),
+ ('price', _('Priced Product')),
+ ('discount', _('Discounted Product')),
+ ('client', _('Client')),
+ ('cashier', _('Cashier'))
+ ])
+ return list(types)
diff --git a/addons/point_of_sale/models/pos_category.py b/addons/point_of_sale/models/pos_category.py
new file mode 100644
index 00000000000..8c9d2ba5253
--- /dev/null
+++ b/addons/point_of_sale/models/pos_category.py
@@ -0,0 +1,71 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from openerp import tools
+from openerp.osv import fields, osv
+
+
+class pos_category(osv.osv):
+ _name = "pos.category"
+ _description = "Public Category"
+ _order = "sequence, name"
+
+ _constraints = [
+ (osv.osv._check_recursion, 'Error ! You cannot create recursive categories.', ['parent_id'])
+ ]
+
+ def name_get(self, cr, uid, ids, context=None):
+ res = []
+ for cat in self.browse(cr, uid, ids, context=context):
+ names = [cat.name]
+ pcat = cat.parent_id
+ while pcat:
+ names.append(pcat.name)
+ pcat = pcat.parent_id
+ res.append((cat.id, ' / '.join(reversed(names))))
+ return res
+
+ def _name_get_fnc(self, cr, uid, ids, prop, unknow_none, context=None):
+ res = self.name_get(cr, uid, ids, context=context)
+ return dict(res)
+
+ _columns = {
+ 'name': fields.char('Name', required=True, translate=True),
+ 'complete_name': fields.function(_name_get_fnc, type="char", string='Name'),
+ 'parent_id': fields.many2one('pos.category','Parent Category', select=True),
+ 'child_id': fields.one2many('pos.category', 'parent_id', string='Children Categories'),
+ 'sequence': fields.integer('Sequence', help="Gives the sequence order when displaying a list of product categories."),
+
+ }
+
+ # NOTE: there is no 'default image', because by default we don't show
+ # thumbnails for categories. However if we have a thumbnail for at least one
+ # category, then we display a default image on the other, so that the
+ # buttons have consistent styling.
+ # In this case, the default image is set by the js code.
+ image = openerp.fields.Binary("Image", attachment=True,
+ help="This field holds the image used as image for the cateogry, limited to 1024x1024px.")
+ image_medium = openerp.fields.Binary("Medium-sized image",
+ compute='_compute_images', inverse='_inverse_image_medium', store=True, attachment=True,
+ help="Medium-sized image of the category. It is automatically "\
+ "resized as a 128x128px image, with aspect ratio preserved. "\
+ "Use this field in form views or some kanban views.")
+ image_small = openerp.fields.Binary("Small-sized image",
+ compute='_compute_images', inverse='_inverse_image_small', store=True, attachment=True,
+ help="Small-sized image of the category. It is automatically "\
+ "resized as a 64x64px image, with aspect ratio preserved. "\
+ "Use this field anywhere a small image is required.")
+
+ @openerp.api.depends('image')
+ def _compute_images(self):
+ for rec in self:
+ rec.image_medium = tools.image_resize_image_medium(rec.image)
+ rec.image_small = tools.image_resize_image_small(rec.image)
+
+ def _inverse_image_medium(self):
+ for rec in self:
+ rec.image = tools.image_resize_image_big(rec.image_medium)
+
+ def _inverse_image_small(self):
+ for rec in self:
+ rec.image = tools.image_resize_image_big(rec.image_small)
diff --git a/addons/point_of_sale/models/pos_config.py b/addons/point_of_sale/models/pos_config.py
new file mode 100644
index 00000000000..dfa363d1745
--- /dev/null
+++ b/addons/point_of_sale/models/pos_config.py
@@ -0,0 +1,324 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+import uuid
+
+from openerp import SUPERUSER_ID
+from openerp.osv import fields, osv
+from openerp.tools.translate import _
+
+class pos_config(osv.osv):
+ _name = 'pos.config'
+
+ POS_CONFIG_STATE = [
+ ('active', 'Active'),
+ ('inactive', 'Inactive'),
+ ('deprecated', 'Deprecated')
+ ]
+
+ def _get_currency(self, cr, uid, ids, fieldnames, args, context=None):
+ result = dict.fromkeys(ids, False)
+ for pos_config in self.browse(cr, uid, ids, context=context):
+ if pos_config.journal_id:
+ currency_id = pos_config.journal_id.currency_id.id or pos_config.journal_id.company_id.currency_id.id
+ else:
+ currency_id = self.pool['res.users'].browse(cr, uid, uid, context=context).company_id.currency_id.id
+ 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"),
+ 'journal_ids' : fields.many2many('account.journal', 'pos_config_journal_rel',
+ 'pos_config_id', 'journal_id', 'Available Payment Methods',
+ domain="[('journal_user', '=', True ), ('type', 'in', ['bank', 'cash'])]",),
+ 'picking_type_id': fields.many2one('stock.picking.type', 'Picking Type'),
+ 'stock_location_id': fields.many2one('stock.location', 'Stock Location', domain=[('usage', '=', 'internal')], required=True),
+ 'journal_id' : fields.many2one('account.journal', 'Sale Journal',
+ domain=[('type', '=', 'sale')],
+ help="Accounting journal used to post sales entries."),
+ 'currency_id' : fields.function(_get_currency, type="many2one", string="Currency", relation="res.currency"),
+ 'iface_cashdrawer' : fields.boolean('Cashdrawer', help="Automatically open the cashdrawer"),
+ 'iface_payment_terminal' : fields.boolean('Payment Terminal', help="Enables Payment Terminal integration"),
+ 'iface_electronic_scale' : fields.boolean('Electronic Scale', help="Enables Electronic Scale integration"),
+ 'iface_vkeyboard' : fields.boolean('Virtual KeyBoard', help="Enables an integrated Virtual Keyboard"),
+ 'iface_print_via_proxy' : fields.boolean('Print via Proxy', help="Bypass browser printing and prints via the hardware proxy"),
+ 'iface_scan_via_proxy' : fields.boolean('Scan via Proxy', help="Enable barcode scanning with a remotely connected barcode scanner"),
+ 'iface_invoicing': fields.boolean('Invoicing',help='Enables invoice generation from the Point of Sale'),
+ 'iface_big_scrollbars': fields.boolean('Large Scrollbars',help='For imprecise industrial touchscreens'),
+ # TODO master: remove the `iface_fullscreen` field. This is no longer used.
+ 'iface_fullscreen': fields.boolean('Fullscreen', help='Display the Point of Sale in full screen mode'),
+ 'iface_print_auto': fields.boolean('Automatic Receipt Printing', help='The receipt will automatically be printed at the end of each order'),
+ 'iface_print_skip_screen': fields.boolean('Skip Receipt Screen', help='The receipt screen will be skipped if the receipt can be printed automatically.'),
+ 'iface_precompute_cash': fields.boolean('Prefill Cash Payment', help='The payment input will behave similarily to bank payment input, and will be prefilled with the exact due amount'),
+ 'iface_tax_included': fields.boolean('Include Taxes in Prices', help='The displayed prices will always include all taxes, even if the taxes have been setup differently'),
+ 'iface_start_categ_id': fields.many2one('pos.category','Start Category', help='The point of sale will display this product category by default. If no category is specified, all available products will be shown'),
+ 'iface_display_categ_images': fields.boolean('Display Category Pictures', help="The product categories will be displayed with pictures."),
+ 'cash_control': fields.boolean('Cash Control', help="Check the amount of the cashbox at opening and closing."),
+ 'receipt_header': fields.text('Receipt Header',help="A short text that will be inserted as a header in the printed receipt"),
+ 'receipt_footer': fields.text('Receipt Footer',help="A short text that will be inserted as a footer in the printed receipt"),
+ 'proxy_ip': fields.char('IP Address', help='The hostname or ip address of the hardware proxy, Will be autodetected if left empty', size=45),
+
+ 'state' : fields.selection(POS_CONFIG_STATE, 'Status', required=True, readonly=True, copy=False),
+ 'uuid' : fields.char('uuid', readonly=True, help='A globally unique identifier for this pos configuration, used to prevent conflicts in client-generated data'),
+ 'sequence_id' : fields.many2one('ir.sequence', 'Order IDs Sequence', readonly=True,
+ 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),
+ 'barcode_nomenclature_id': fields.many2one('barcode.nomenclature','Barcodes', help='Defines what kind of barcodes are available and how they are assigned to products, customers and cashiers', required=True),
+ 'group_pos_manager_id': fields.many2one('res.groups','Point of Sale Manager Group', help='This field is there to pass the id of the pos manager group to the point of sale client'),
+ 'group_pos_user_id': fields.many2one('res.groups','Point of Sale User Group', help='This field is there to pass the id of the pos user group to the point of sale client'),
+ 'tip_product_id': fields.many2one('product.product','Tip Product', help="The product used to encode the customer tip. Leave empty if you do not accept tips."),
+ 'fiscal_position_ids': fields.many2many('account.fiscal.position', string='Fiscal Positions')
+ }
+
+ def _check_company_location(self, cr, uid, ids, context=None):
+ for config in self.browse(cr, uid, ids, context=context):
+ if config.stock_location_id.company_id and config.stock_location_id.company_id.id != config.company_id.id:
+ return False
+ return True
+
+ def _check_company_journal(self, cr, uid, ids, context=None):
+ for config in self.browse(cr, uid, ids, context=context):
+ if config.journal_id and config.journal_id.company_id.id != config.company_id.id:
+ return False
+ return True
+
+ def _check_company_payment(self, cr, uid, ids, context=None):
+ for config in self.browse(cr, uid, ids, context=context):
+ journal_ids = [j.id for j in config.journal_ids]
+ if self.pool['account.journal'].search(cr, uid, [
+ ('id', 'in', journal_ids),
+ ('company_id', '!=', config.company_id.id)
+ ], count=True, context=context):
+ return False
+ return True
+
+ _constraints = [
+ (_check_company_location, "The company of the stock location is different than the one of point of sale", ['company_id', 'stock_location_id']),
+ (_check_company_journal, "The company of the sale journal is different than the one of point of sale", ['company_id', 'journal_id']),
+ (_check_company_payment, "The company of a payment method is different than the one of point of sale", ['company_id', 'journal_ids']),
+ ]
+
+ def name_get(self, cr, uid, ids, context=None):
+ result = []
+ states = {
+ 'opening_control': _('Opening Control'),
+ 'opened': _('In Progress'),
+ 'closing_control': _('Closing Control'),
+ 'closed': _('Closed & Posted'),
+ }
+ for record in self.browse(cr, uid, ids, context=context):
+ if (not record.session_ids) or (record.session_ids[0].state=='closed'):
+ result.append((record.id, record.name+' ('+_('not used')+')'))
+ continue
+ session = record.session_ids[0]
+ result.append((record.id, record.name + ' ('+session.user_id.name+')')) #, '+states[session.state]+')'))
+ return result
+
+ def _default_sale_journal(self, cr, uid, context=None):
+ company_id = self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id.id
+ res = self.pool.get('account.journal').search(cr, uid, [('type', '=', 'sale'), ('company_id', '=', company_id)], limit=1, context=context)
+ return res and res[0] or False
+
+ def _default_pricelist(self, cr, uid, context=None):
+ res = self.pool.get('product.pricelist').search(cr, uid, [], limit=1, context=context)
+ return res and res[0] or False
+
+ def _get_default_location(self, cr, uid, context=None):
+ wh_obj = self.pool.get('stock.warehouse')
+ user = self.pool.get('res.users').browse(cr, uid, uid, context)
+ res = wh_obj.search(cr, uid, [('company_id', '=', user.company_id.id)], limit=1, context=context)
+ if res and res[0]:
+ return wh_obj.browse(cr, uid, res[0], context=context).lot_stock_id.id
+ return False
+
+ def _get_default_company(self, cr, uid, context=None):
+ company_id = self.pool.get('res.users')._get_company(cr, uid, context=context)
+ return company_id
+
+ def _get_default_nomenclature(self, cr, uid, context=None):
+ nom_obj = self.pool.get('barcode.nomenclature')
+ res = nom_obj.search(cr, uid, [], limit=1, context=context)
+ return res and res[0] or False
+
+ def _get_group_pos_manager(self, cr, uid, context=None):
+ group = self.pool.get('ir.model.data').get_object_reference(cr,uid,'point_of_sale','group_pos_manager')
+ if group:
+ return group[1]
+ else:
+ return False
+
+ def _get_group_pos_user(self, cr, uid, context=None):
+ group = self.pool.get('ir.model.data').get_object_reference(cr,uid,'point_of_sale','group_pos_user')
+ if group:
+ return group[1]
+ else:
+ return False
+
+ _defaults = {
+ 'uuid' : lambda self, cr, uid, context={}: str(uuid.uuid4()),
+ 'state' : POS_CONFIG_STATE[0][0],
+ 'journal_id': _default_sale_journal,
+ 'group_by' : True,
+ 'pricelist_id': _default_pricelist,
+ 'iface_invoicing': True,
+ 'iface_print_auto': False,
+ 'iface_print_skip_screen': True,
+ 'stock_location_id': _get_default_location,
+ 'company_id': _get_default_company,
+ 'barcode_nomenclature_id': _get_default_nomenclature,
+ 'group_pos_manager_id': _get_group_pos_manager,
+ 'group_pos_user_id': _get_group_pos_user,
+ }
+
+ def onchange_picking_type_id(self, cr, uid, ids, picking_type_id, context=None):
+ p_type_obj = self.pool.get("stock.picking.type")
+ p_type = p_type_obj.browse(cr, uid, picking_type_id, context=context)
+ if p_type.default_location_src_id and p_type.default_location_src_id.usage == 'internal' and p_type.default_location_dest_id and p_type.default_location_dest_id.usage == 'customer':
+ return {'value': {'stock_location_id': p_type.default_location_src_id.id}}
+ return False
+
+ def onchange_iface_print_via_proxy(self, cr, uid, ids, print_via_proxy, context=None):
+ return {'value': {'iface_print_auto': print_via_proxy}}
+
+ def set_active(self, cr, uid, ids, context=None):
+ return self.write(cr, uid, ids, {'state' : 'active'}, context=context)
+
+ def set_inactive(self, cr, uid, ids, context=None):
+ return self.write(cr, uid, ids, {'state' : 'inactive'}, context=context)
+
+ def set_deprecate(self, cr, uid, ids, context=None):
+ return self.write(cr, uid, ids, {'state' : 'deprecated'}, context=context)
+
+ def create(self, cr, uid, values, context=None):
+ ir_sequence = self.pool.get('ir.sequence')
+ # force sequence_id field to new pos.order sequence
+ values['sequence_id'] = ir_sequence.create(cr, SUPERUSER_ID, {
+ 'name': 'POS Order %s' % values['name'],
+ 'padding': 4,
+ 'prefix': "%s/" % values['name'],
+ 'code': "pos.order",
+ 'company_id': values.get('company_id', False),
+ }, context=context)
+
+ # TODO master: add field sequence_line_id on model
+ # this make sure we always have one available per company
+ ir_sequence.create(cr, SUPERUSER_ID, {
+ 'name': 'POS order line %s' % values['name'],
+ 'padding': 4,
+ 'prefix': "%s/" % values['name'],
+ 'code': "pos.order.line",
+ 'company_id': values.get('company_id', False),
+ }, context=context)
+
+ return super(pos_config, self).create(cr, uid, values, context=context)
+
+ def unlink(self, cr, uid, ids, context=None):
+ for obj in self.browse(cr, uid, ids, context=context):
+ if obj.sequence_id:
+ 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)
+ self.write(cr, SUPERUSER_ID, record.id, {'current_session_id': 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',
+ }
diff --git a/addons/point_of_sale/models/point_of_sale.py b/addons/point_of_sale/models/pos_order.py
similarity index 53%
rename from addons/point_of_sale/models/point_of_sale.py
rename to addons/point_of_sale/models/pos_order.py
index b09fcc8a722..65e4754908e 100644
--- a/addons/point_of_sale/models/point_of_sale.py
+++ b/addons/point_of_sale/models/pos_order.py
@@ -2,16 +2,15 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import logging
+
import time
from datetime import datetime
-import uuid
-import sets
from functools import partial
import openerp
import openerp.addons.decimal_precision as dp
-from openerp import tools, models, SUPERUSER_ID
+from openerp import tools, SUPERUSER_ID
from openerp.osv import fields, osv
from openerp.tools import float_is_zero
from openerp.tools.translate import _
@@ -20,623 +19,6 @@ from openerp.exceptions import UserError
_logger = logging.getLogger(__name__)
-class pos_config(osv.osv):
- _name = 'pos.config'
-
- POS_CONFIG_STATE = [
- ('active', 'Active'),
- ('inactive', 'Inactive'),
- ('deprecated', 'Deprecated')
- ]
-
- def _get_currency(self, cr, uid, ids, fieldnames, args, context=None):
- result = dict.fromkeys(ids, False)
- for pos_config in self.browse(cr, uid, ids, context=context):
- if pos_config.journal_id:
- currency_id = pos_config.journal_id.currency_id.id or pos_config.journal_id.company_id.currency_id.id
- else:
- currency_id = self.pool['res.users'].browse(cr, uid, uid, context=context).company_id.currency_id.id
- 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"),
- 'journal_ids' : fields.many2many('account.journal', 'pos_config_journal_rel',
- 'pos_config_id', 'journal_id', 'Available Payment Methods',
- domain="[('journal_user', '=', True ), ('type', 'in', ['bank', 'cash'])]",),
- 'picking_type_id': fields.many2one('stock.picking.type', 'Picking Type'),
- 'stock_location_id': fields.many2one('stock.location', 'Stock Location', domain=[('usage', '=', 'internal')], required=True),
- 'journal_id' : fields.many2one('account.journal', 'Sale Journal',
- domain=[('type', '=', 'sale')],
- help="Accounting journal used to post sales entries."),
- 'currency_id' : fields.function(_get_currency, type="many2one", string="Currency", relation="res.currency"),
- 'iface_cashdrawer' : fields.boolean('Cashdrawer', help="Automatically open the cashdrawer"),
- 'iface_payment_terminal' : fields.boolean('Payment Terminal', help="Enables Payment Terminal integration"),
- 'iface_electronic_scale' : fields.boolean('Electronic Scale', help="Enables Electronic Scale integration"),
- 'iface_vkeyboard' : fields.boolean('Virtual KeyBoard', help="Enables an integrated Virtual Keyboard"),
- 'iface_print_via_proxy' : fields.boolean('Print via Proxy', help="Bypass browser printing and prints via the hardware proxy"),
- 'iface_scan_via_proxy' : fields.boolean('Scan via Proxy', help="Enable barcode scanning with a remotely connected barcode scanner"),
- 'iface_invoicing': fields.boolean('Invoicing',help='Enables invoice generation from the Point of Sale'),
- 'iface_big_scrollbars': fields.boolean('Large Scrollbars',help='For imprecise industrial touchscreens'),
- # TODO master: remove the `iface_fullscreen` field. This is no longer used.
- 'iface_fullscreen': fields.boolean('Fullscreen', help='Display the Point of Sale in full screen mode'),
- 'iface_print_auto': fields.boolean('Automatic Receipt Printing', help='The receipt will automatically be printed at the end of each order'),
- 'iface_print_skip_screen': fields.boolean('Skip Receipt Screen', help='The receipt screen will be skipped if the receipt can be printed automatically.'),
- 'iface_precompute_cash': fields.boolean('Prefill Cash Payment', help='The payment input will behave similarily to bank payment input, and will be prefilled with the exact due amount'),
- 'iface_tax_included': fields.boolean('Include Taxes in Prices', help='The displayed prices will always include all taxes, even if the taxes have been setup differently'),
- 'iface_start_categ_id': fields.many2one('pos.category','Start Category', help='The point of sale will display this product category by default. If no category is specified, all available products will be shown'),
- 'iface_display_categ_images': fields.boolean('Display Category Pictures', help="The product categories will be displayed with pictures."),
- 'cash_control': fields.boolean('Cash Control', help="Check the amount of the cashbox at opening and closing."),
- 'receipt_header': fields.text('Receipt Header',help="A short text that will be inserted as a header in the printed receipt"),
- 'receipt_footer': fields.text('Receipt Footer',help="A short text that will be inserted as a footer in the printed receipt"),
- 'proxy_ip': fields.char('IP Address', help='The hostname or ip address of the hardware proxy, Will be autodetected if left empty', size=45),
-
- 'state' : fields.selection(POS_CONFIG_STATE, 'Status', required=True, readonly=True, copy=False),
- 'uuid' : fields.char('uuid', readonly=True, help='A globally unique identifier for this pos configuration, used to prevent conflicts in client-generated data'),
- 'sequence_id' : fields.many2one('ir.sequence', 'Order IDs Sequence', readonly=True,
- 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),
- 'barcode_nomenclature_id': fields.many2one('barcode.nomenclature','Barcodes', help='Defines what kind of barcodes are available and how they are assigned to products, customers and cashiers', required=True),
- 'group_pos_manager_id': fields.many2one('res.groups','Point of Sale Manager Group', help='This field is there to pass the id of the pos manager group to the point of sale client'),
- 'group_pos_user_id': fields.many2one('res.groups','Point of Sale User Group', help='This field is there to pass the id of the pos user group to the point of sale client'),
- 'tip_product_id': fields.many2one('product.product','Tip Product', help="The product used to encode the customer tip. Leave empty if you do not accept tips."),
- 'fiscal_position_ids': fields.many2many('account.fiscal.position', string='Fiscal Positions')
- }
-
- def _check_company_location(self, cr, uid, ids, context=None):
- for config in self.browse(cr, uid, ids, context=context):
- if config.stock_location_id.company_id and config.stock_location_id.company_id.id != config.company_id.id:
- return False
- return True
-
- def _check_company_journal(self, cr, uid, ids, context=None):
- for config in self.browse(cr, uid, ids, context=context):
- if config.journal_id and config.journal_id.company_id.id != config.company_id.id:
- return False
- return True
-
- def _check_company_payment(self, cr, uid, ids, context=None):
- for config in self.browse(cr, uid, ids, context=context):
- journal_ids = [j.id for j in config.journal_ids]
- if self.pool['account.journal'].search(cr, uid, [
- ('id', 'in', journal_ids),
- ('company_id', '!=', config.company_id.id)
- ], count=True, context=context):
- return False
- return True
-
- _constraints = [
- (_check_company_location, "The company of the stock location is different than the one of point of sale", ['company_id', 'stock_location_id']),
- (_check_company_journal, "The company of the sale journal is different than the one of point of sale", ['company_id', 'journal_id']),
- (_check_company_payment, "The company of a payment method is different than the one of point of sale", ['company_id', 'journal_ids']),
- ]
-
- def name_get(self, cr, uid, ids, context=None):
- result = []
- states = {
- 'opening_control': _('Opening Control'),
- 'opened': _('In Progress'),
- 'closing_control': _('Closing Control'),
- 'closed': _('Closed & Posted'),
- }
- for record in self.browse(cr, uid, ids, context=context):
- if (not record.session_ids) or (record.session_ids[0].state=='closed'):
- result.append((record.id, record.name+' ('+_('not used')+')'))
- continue
- session = record.session_ids[0]
- result.append((record.id, record.name + ' ('+session.user_id.name+')')) #, '+states[session.state]+')'))
- return result
-
- def _default_sale_journal(self, cr, uid, context=None):
- company_id = self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id.id
- res = self.pool.get('account.journal').search(cr, uid, [('type', '=', 'sale'), ('company_id', '=', company_id)], limit=1, context=context)
- return res and res[0] or False
-
- def _default_pricelist(self, cr, uid, context=None):
- res = self.pool.get('product.pricelist').search(cr, uid, [], limit=1, context=context)
- return res and res[0] or False
-
- def _get_default_location(self, cr, uid, context=None):
- wh_obj = self.pool.get('stock.warehouse')
- user = self.pool.get('res.users').browse(cr, uid, uid, context)
- res = wh_obj.search(cr, uid, [('company_id', '=', user.company_id.id)], limit=1, context=context)
- if res and res[0]:
- return wh_obj.browse(cr, uid, res[0], context=context).lot_stock_id.id
- return False
-
- def _get_default_company(self, cr, uid, context=None):
- company_id = self.pool.get('res.users')._get_company(cr, uid, context=context)
- return company_id
-
- def _get_default_nomenclature(self, cr, uid, context=None):
- nom_obj = self.pool.get('barcode.nomenclature')
- res = nom_obj.search(cr, uid, [], limit=1, context=context)
- return res and res[0] or False
-
- def _get_group_pos_manager(self, cr, uid, context=None):
- group = self.pool.get('ir.model.data').get_object_reference(cr,uid,'point_of_sale','group_pos_manager')
- if group:
- return group[1]
- else:
- return False
-
- def _get_group_pos_user(self, cr, uid, context=None):
- group = self.pool.get('ir.model.data').get_object_reference(cr,uid,'point_of_sale','group_pos_user')
- if group:
- return group[1]
- else:
- return False
-
- _defaults = {
- 'uuid' : lambda self, cr, uid, context={}: str(uuid.uuid4()),
- 'state' : POS_CONFIG_STATE[0][0],
- 'journal_id': _default_sale_journal,
- 'group_by' : True,
- 'pricelist_id': _default_pricelist,
- 'iface_invoicing': True,
- 'iface_print_auto': False,
- 'iface_print_skip_screen': True,
- 'stock_location_id': _get_default_location,
- 'company_id': _get_default_company,
- 'barcode_nomenclature_id': _get_default_nomenclature,
- 'group_pos_manager_id': _get_group_pos_manager,
- 'group_pos_user_id': _get_group_pos_user,
- }
-
- def onchange_picking_type_id(self, cr, uid, ids, picking_type_id, context=None):
- p_type_obj = self.pool.get("stock.picking.type")
- p_type = p_type_obj.browse(cr, uid, picking_type_id, context=context)
- if p_type.default_location_src_id and p_type.default_location_src_id.usage == 'internal' and p_type.default_location_dest_id and p_type.default_location_dest_id.usage == 'customer':
- return {'value': {'stock_location_id': p_type.default_location_src_id.id}}
- return False
-
- def onchange_iface_print_via_proxy(self, cr, uid, ids, print_via_proxy, context=None):
- return {'value': {'iface_print_auto': print_via_proxy}}
-
- def set_active(self, cr, uid, ids, context=None):
- return self.write(cr, uid, ids, {'state' : 'active'}, context=context)
-
- def set_inactive(self, cr, uid, ids, context=None):
- return self.write(cr, uid, ids, {'state' : 'inactive'}, context=context)
-
- def set_deprecate(self, cr, uid, ids, context=None):
- return self.write(cr, uid, ids, {'state' : 'deprecated'}, context=context)
-
- def create(self, cr, uid, values, context=None):
- ir_sequence = self.pool.get('ir.sequence')
- # force sequence_id field to new pos.order sequence
- values['sequence_id'] = ir_sequence.create(cr, SUPERUSER_ID, {
- 'name': 'POS Order %s' % values['name'],
- 'padding': 4,
- 'prefix': "%s/" % values['name'],
- 'code': "pos.order",
- 'company_id': values.get('company_id', False),
- }, context=context)
-
- # TODO master: add field sequence_line_id on model
- # this make sure we always have one available per company
- ir_sequence.create(cr, SUPERUSER_ID, {
- 'name': 'POS order line %s' % values['name'],
- 'padding': 4,
- 'prefix': "%s/" % values['name'],
- 'code': "pos.order.line",
- 'company_id': values.get('company_id', False),
- }, context=context)
-
- return super(pos_config, self).create(cr, uid, values, context=context)
-
- def unlink(self, cr, uid, ids, context=None):
- for obj in self.browse(cr, uid, ids, context=context):
- if obj.sequence_id:
- 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)
- self.write(cr, SUPERUSER_ID, record.id, {'current_session_id': 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'
-
- POS_SESSION_STATE = [
- ('opening_control', 'Opening Control'), # Signal open
- ('opened', 'In Progress'), # Signal closing
- ('closing_control', 'Closing Control'), # Signal close
- ('closed', 'Closed & Posted'),
- ]
-
- def _compute_cash_all(self, cr, uid, ids, fieldnames, args, context=None):
- result = dict()
-
- for record in self.browse(cr, uid, ids, context=context):
- result[record.id] = {
- 'cash_journal_id' : False,
- 'cash_register_id' : False,
- 'cash_control' : False,
- }
-
- if record.config_id.cash_control:
- for st in record.statement_ids:
- if st.journal_id.type == 'cash':
- result[record.id]['cash_control'] = True
- result[record.id]['cash_journal_id'] = st.journal_id.id
- result[record.id]['cash_register_id'] = st.id
-
- if not result[record.id]['cash_control']:
- raise UserError(_("Cash control can only be applied to cash journals."))
-
- return result
-
- _columns = {
- 'config_id' : fields.many2one('pos.config', 'Point of Sale',
- help="The physical point of sale you will use.",
- required=True,
- select=1,
- domain="[('state', '=', 'active')]",
- ),
-
- 'name' : fields.char('Session ID', required=True, readonly=True),
- 'user_id' : fields.many2one('res.users', 'Responsible',
- required=True,
- select=1,
- readonly=True,
- states={'opening_control' : [('readonly', False)]}
- ),
- 'currency_id' : fields.related('config_id', 'currency_id', type="many2one", relation='res.currency', string="Currency"),
- 'start_at' : fields.datetime('Opening Date', readonly=True),
- 'stop_at' : fields.datetime('Closing Date', readonly=True, copy=False),
-
- 'state' : fields.selection(POS_SESSION_STATE, 'Status',
- required=True, readonly=True,
- select=1, copy=False),
-
- 'sequence_number': fields.integer('Order Sequence Number', help='A sequence number that is incremented with each order'),
- 'login_number': fields.integer('Login Sequence Number', help='A sequence number that is incremented each time a user resumes the pos session'),
-
- 'cash_control' : fields.function(_compute_cash_all,
- multi='cash',
- type='boolean', string='Has Cash Control'),
- 'cash_journal_id' : fields.function(_compute_cash_all,
- multi='cash',
- type='many2one', relation='account.journal',
- string='Cash Journal', store=True),
- 'cash_register_id' : fields.function(_compute_cash_all,
- multi='cash',
- type='many2one', relation='account.bank.statement',
- string='Cash Register', store=True),
-
- 'cash_register_balance_end_real' : fields.related('cash_register_id', 'balance_end_real',
- type='float',
- digits=0,
- string="Ending Balance",
- help="Total of closing cash control lines.",
- readonly=True),
- 'cash_register_balance_start' : fields.related('cash_register_id', 'balance_start',
- type='float',
- digits=0,
- string="Starting Balance",
- help="Total of opening cash control lines.",
- readonly=True),
- 'cash_register_total_entry_encoding' : fields.related('cash_register_id', 'total_entry_encoding',
- string='Total Cash Transaction',
- readonly=True,
- help="Total of all paid sale orders"),
- 'cash_register_balance_end' : fields.related('cash_register_id', 'balance_end',
- type='float',
- digits=0,
- string="Theoretical Closing Balance",
- help="Sum of opening balance and transactions.",
- readonly=True),
- 'cash_register_difference' : fields.related('cash_register_id', 'difference',
- type='float',
- string='Difference',
- help="Difference between the theoretical closing balance and the real closing balance.",
- readonly=True),
-
- 'journal_ids' : fields.related('config_id', 'journal_ids',
- type='many2many',
- readonly=True,
- relation='account.journal',
- string='Available Payment Methods'),
- 'order_ids' : fields.one2many('pos.order', 'session_id', 'Orders'),
-
- 'statement_ids' : fields.one2many('account.bank.statement', 'pos_session_id', 'Bank Statement', readonly=True),
- }
-
- _defaults = {
- 'name' : '/',
- 'user_id' : lambda obj, cr, uid, context: uid,
- 'state' : 'opening_control',
- 'sequence_number': 1,
- 'login_number': 0,
- }
-
- _sql_constraints = [
- ('uniq_name', 'unique(name)', "The name of this POS Session must be unique !"),
- ]
-
- def _check_unicity(self, cr, uid, ids, context=None):
- for session in self.browse(cr, uid, ids, context=None):
- # open if there is no session in 'opening_control', 'opened', 'closing_control' for one user
- domain = [
- ('state', 'not in', ('closed','closing_control')),
- ('user_id', '=', session.user_id.id)
- ]
- count = self.search_count(cr, uid, domain, context=context)
- if count>1:
- return False
- return True
-
- def _check_pos_config(self, cr, uid, ids, context=None):
- for session in self.browse(cr, uid, ids, context=None):
- domain = [
- ('state', '!=', 'closed'),
- ('config_id', '=', session.config_id.id)
- ]
- count = self.search_count(cr, uid, domain, context=context)
- if count>1:
- return False
- return True
-
- _constraints = [
- (_check_unicity, "You cannot create two active sessions with the same responsible!", ['user_id', 'state']),
- (_check_pos_config, "You cannot create two active sessions related to the same point of sale!", ['config_id']),
- ]
-
- def create(self, cr, uid, values, context=None):
- context = dict(context or {})
- config_id = values.get('config_id', False) or context.get('default_config_id', False)
- if not config_id:
- raise UserError(_("You should assign a Point of Sale to your session."))
-
- # journal_id is not required on the pos_config because it does not
- # exists at the installation. If nothing is configured at the
- # installation we do the minimal configuration. Impossible to do in
- # the .xml files as the CoA is not yet installed.
- jobj = self.pool.get('pos.config')
- pos_config = jobj.browse(cr, uid, config_id, context=context)
- context.update({'company_id': pos_config.company_id.id})
- if not pos_config.journal_id:
- jid = jobj.default_get(cr, uid, ['journal_id'], context=context)['journal_id']
- if jid:
- jobj.write(cr, SUPERUSER_ID, [pos_config.id], {'journal_id': jid}, context=context)
- else:
- raise UserError(_("Unable to open the session. You have to assign a sale journal to your point of sale."))
-
- # define some cash journal if no payment method exists
- if not pos_config.journal_ids:
- journal_proxy = self.pool.get('account.journal')
- cashids = journal_proxy.search(cr, uid, [('journal_user', '=', True), ('type','=','cash')], context=context)
- if not cashids:
- cashids = journal_proxy.search(cr, uid, [('type', '=', 'cash')], context=context)
- if not cashids:
- cashids = journal_proxy.search(cr, uid, [('journal_user','=',True)], context=context)
-
- journal_proxy.write(cr, SUPERUSER_ID, cashids, {'journal_user': True})
- jobj.write(cr, SUPERUSER_ID, [pos_config.id], {'journal_ids': [(6,0, cashids)]})
-
-
- pos_config = jobj.browse(cr, uid, config_id, context=context)
-
- statements = [(0, 0, {
- 'journal_id': journal.id,
- 'user_id': uid,
- 'company_id': pos_config.company_id.id
- }) for journal in pos_config.journal_ids]
-
- values.update({
- 'name': self.pool['ir.sequence'].next_by_code(cr, uid, 'pos.session', context=context),
- 'statement_ids': statements,
- 'config_id': config_id
- })
-
- # set the journal_id which should be used by
- # account.bank.statement to set the opening balance of the
- # newly created bank statement
- if pos_config.cash_control:
- for journal in pos_config.journal_ids:
- if journal.type == 'cash':
- context.update({'journal_id': journal.id})
-
- return super(pos_session, self).create(cr, uid, values, context=context)
-
- def unlink(self, cr, uid, ids, context=None):
- for obj in self.browse(cr, uid, ids, context=context):
- self.pool.get('account.bank.statement').unlink(cr, uid, obj.statement_ids.ids, context=context)
- return super(pos_session, self).unlink(cr, uid, ids, context=context)
-
- def login(self, cr, uid, ids, context=None):
- this_record = self.browse(cr, uid, ids[0], context=context)
- this_record.write({
- 'login_number': this_record.login_number+1,
- })
-
- def wkf_action_open(self, cr, uid, ids, context=None):
- # second browse because we need to refetch the data from the DB for cash_register_id
- for record in self.browse(cr, uid, ids, context=context):
- values = {}
- if not record.start_at:
- values['start_at'] = time.strftime('%Y-%m-%d %H:%M:%S')
- values['state'] = 'opened'
- record.write(values)
- for st in record.statement_ids:
- st.button_open()
-
- return True
-
- def wkf_action_opening_control(self, cr, uid, ids, context=None):
- return self.write(cr, uid, ids, {'state' : 'opening_control'}, context=context)
-
- def wkf_action_closing_control(self, cr, uid, ids, context=None):
- for session in self.browse(cr, uid, ids, context=context):
- for statement in session.statement_ids:
- if (statement != session.cash_register_id) and (statement.balance_end != statement.balance_end_real):
- self.pool.get('account.bank.statement').write(cr, uid, [statement.id], {'balance_end_real': statement.balance_end})
- return self.write(cr, uid, ids, {'state' : 'closing_control', 'stop_at' : time.strftime('%Y-%m-%d %H:%M:%S')}, context=context)
-
- def wkf_action_close(self, cr, uid, ids, context=None):
- # Close CashBox
- local_context = dict(context)
- for record in self.browse(cr, uid, ids, context=context):
- company_id = record.config_id.company_id.id
- local_context.update({'force_company': company_id, 'company_id': company_id})
- for st in record.statement_ids:
- if abs(st.difference) > st.journal_id.amount_authorized_diff:
- # The pos manager can close statements with maximums.
- if not self.pool.get('ir.model.access').check_groups(cr, uid, "point_of_sale.group_pos_manager"):
- raise UserError(_("Your ending balance is too different from the theoretical cash closing (%.2f), the maximum allowed is: %.2f. You can contact your manager to force it.") % (st.difference, st.journal_id.amount_authorized_diff))
- if (st.journal_id.type not in ['bank', 'cash']):
- raise UserError(_("The type of the journal for your payment method should be bank or cash "))
- self.pool['account.bank.statement'].button_confirm_bank(cr, SUPERUSER_ID, [st.id], context=local_context)
- self._confirm_orders(cr, uid, ids, context=local_context)
- self.write(cr, uid, ids, {'state' : 'closed'}, context=local_context)
-
- obj = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'point_of_sale', 'menu_point_root')[1]
- return {
- 'type' : 'ir.actions.client',
- 'name' : 'Point of Sale Menu',
- 'tag' : 'reload',
- 'params' : {'menu_id': obj},
- }
-
- def _confirm_orders(self, cr, uid, ids, context=None):
- pos_order_obj = self.pool.get('pos.order')
- for session in self.browse(cr, uid, ids, context=context):
- company_id = session.config_id.journal_id.company_id.id
- local_context = dict(context or {}, force_company=company_id)
- order_ids = [order.id for order in session.order_ids if order.state == 'paid']
-
- move_id = pos_order_obj._create_account_move(cr, uid, session.start_at, session.name, session.config_id.journal_id.id, company_id, context=context)
-
- pos_order_obj._create_account_move_line(cr, uid, order_ids, session, move_id, context=local_context)
-
- for order in session.order_ids:
- if order.state == 'done':
- continue
- if order.state not in ('paid', 'invoiced'):
- raise UserError(_("You cannot confirm all orders of this session, because they have not the 'paid' status"))
- else:
- pos_order_obj.signal_workflow(cr, uid, [order.id], 'done')
-
- return True
-
- def open_frontend_cb(self, cr, uid, ids, context=None):
- if not context:
- context = {}
- if not ids:
- return {}
- for session in self.browse(cr, uid, ids, context=context):
- if session.user_id.id != uid:
- raise UserError(_("You cannot use the session of another users. This session is owned by %s. "
- "Please first close this one to use this point of sale.") % session.user_id.name)
- context.update({'active_id': ids[0]})
- return {
- 'type' : 'ir.actions.act_url',
- 'target': 'self',
- 'url': '/pos/web/',
- }
class pos_order(osv.osv):
_name = "pos.order"
@@ -1377,12 +759,6 @@ class pos_order(osv.osv):
self.create_account_move(cr, uid, ids, context=context)
return True
-class account_bank_statement_line(osv.osv):
- _inherit = 'account.bank.statement.line'
- _columns= {
- 'pos_statement_id': fields.many2one('pos.order', string="POS statement", ondelete='cascade'),
- }
-
class pos_order_line(osv.osv):
_name = "pos.order.line"
_description = "Lines of Point of Sale"
@@ -1480,125 +856,3 @@ class pos_order_line(osv.osv):
'discount': lambda *a: 0.0,
'company_id': lambda self,cr,uid,c: self.pool.get('res.users').browse(cr, uid, uid, c).company_id.id,
}
-
-class pos_category(osv.osv):
- _name = "pos.category"
- _description = "Public Category"
- _order = "sequence, name"
-
- _constraints = [
- (osv.osv._check_recursion, 'Error ! You cannot create recursive categories.', ['parent_id'])
- ]
-
- def name_get(self, cr, uid, ids, context=None):
- res = []
- for cat in self.browse(cr, uid, ids, context=context):
- names = [cat.name]
- pcat = cat.parent_id
- while pcat:
- names.append(pcat.name)
- pcat = pcat.parent_id
- res.append((cat.id, ' / '.join(reversed(names))))
- return res
-
- def _name_get_fnc(self, cr, uid, ids, prop, unknow_none, context=None):
- res = self.name_get(cr, uid, ids, context=context)
- return dict(res)
-
- _columns = {
- 'name': fields.char('Name', required=True, translate=True),
- 'complete_name': fields.function(_name_get_fnc, type="char", string='Name'),
- 'parent_id': fields.many2one('pos.category','Parent Category', select=True),
- 'child_id': fields.one2many('pos.category', 'parent_id', string='Children Categories'),
- 'sequence': fields.integer('Sequence', help="Gives the sequence order when displaying a list of product categories."),
-
- }
-
- # NOTE: there is no 'default image', because by default we don't show
- # thumbnails for categories. However if we have a thumbnail for at least one
- # category, then we display a default image on the other, so that the
- # buttons have consistent styling.
- # In this case, the default image is set by the js code.
- image = openerp.fields.Binary("Image", attachment=True,
- help="This field holds the image used as image for the cateogry, limited to 1024x1024px.")
- image_medium = openerp.fields.Binary("Medium-sized image",
- compute='_compute_images', inverse='_inverse_image_medium', store=True, attachment=True,
- help="Medium-sized image of the category. It is automatically "\
- "resized as a 128x128px image, with aspect ratio preserved. "\
- "Use this field in form views or some kanban views.")
- image_small = openerp.fields.Binary("Small-sized image",
- compute='_compute_images', inverse='_inverse_image_small', store=True, attachment=True,
- help="Small-sized image of the category. It is automatically "\
- "resized as a 64x64px image, with aspect ratio preserved. "\
- "Use this field anywhere a small image is required.")
-
- @openerp.api.depends('image')
- def _compute_images(self):
- for rec in self:
- rec.image_medium = tools.image_resize_image_medium(rec.image)
- rec.image_small = tools.image_resize_image_small(rec.image)
-
- def _inverse_image_medium(self):
- for rec in self:
- rec.image = tools.image_resize_image_big(rec.image_medium)
-
- def _inverse_image_small(self):
- for rec in self:
- rec.image = tools.image_resize_image_big(rec.image_small)
-
-class product_template(osv.osv):
- _inherit = 'product.template'
-
- _columns = {
- 'available_in_pos': fields.boolean('Available in the Point of Sale', help='Check if you want this product to appear in the Point of Sale'),
- 'to_weight' : fields.boolean('To Weigh With Scale', help="Check if the product should be weighted using the hardware scale integration"),
- 'pos_categ_id': fields.many2one('pos.category','Point of Sale Category', help="Those categories are used to group similar products for point of sale."),
- }
-
- _defaults = {
- 'to_weight' : False,
- 'available_in_pos': True,
- }
-
- def unlink(self, cr, uid, ids, context=None):
- product_ctx = dict(context or {}, active_test=False)
- if self.search_count(cr, uid, [('id', 'in', ids), ('available_in_pos', '=', True)], context=product_ctx):
- if self.pool['pos.session'].search_count(cr, uid, [('state', '!=', 'closed')], context=context):
- raise osv.except_osv(_('Error!'),
- _('You cannot delete a product saleable in point of sale while a session is still opened.'))
- return super(product_template, self).unlink(cr, uid, ids, context=context)
-
-class res_partner(osv.osv):
- _inherit = 'res.partner'
-
- def create_from_ui(self, cr, uid, partner, context=None):
- """ create or modify a partner from the point of sale ui.
- partner contains the partner's fields. """
-
- #image is a dataurl, get the data after the comma
- if partner.get('image',False):
- img = partner['image'].split(',')[1]
- partner['image'] = img
-
- if partner.get('id',False): # Modifying existing partner
- partner_id = partner['id']
- del partner['id']
- self.write(cr, uid, [partner_id], partner, context=context)
- else:
- partner_id = self.create(cr, uid, partner, context=context)
-
- return partner_id
-
-class barcode_rule(models.Model):
- _inherit = 'barcode.rule'
-
- def _get_type_selection(self):
- types = sets.Set(super(barcode_rule,self)._get_type_selection())
- types.update([
- ('weight', _('Weighted Product')),
- ('price', _('Priced Product')),
- ('discount', _('Discounted Product')),
- ('client', _('Client')),
- ('cashier', _('Cashier'))
- ])
- return list(types)
diff --git a/addons/point_of_sale/models/pos_session.py b/addons/point_of_sale/models/pos_session.py
new file mode 100644
index 00000000000..b1f408afb68
--- /dev/null
+++ b/addons/point_of_sale/models/pos_session.py
@@ -0,0 +1,307 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+import time
+from openerp.osv import fields, osv
+from openerp.tools.translate import _
+
+class pos_session(osv.osv):
+ _name = 'pos.session'
+ _order = 'id desc'
+
+ POS_SESSION_STATE = [
+ ('opening_control', 'Opening Control'), # Signal open
+ ('opened', 'In Progress'), # Signal closing
+ ('closing_control', 'Closing Control'), # Signal close
+ ('closed', 'Closed & Posted'),
+ ]
+
+ def _compute_cash_all(self, cr, uid, ids, fieldnames, args, context=None):
+ result = dict()
+
+ for record in self.browse(cr, uid, ids, context=context):
+ result[record.id] = {
+ 'cash_journal_id' : False,
+ 'cash_register_id' : False,
+ 'cash_control' : False,
+ }
+
+ if record.config_id.cash_control:
+ for st in record.statement_ids:
+ if st.journal_id.type == 'cash':
+ result[record.id]['cash_control'] = True
+ result[record.id]['cash_journal_id'] = st.journal_id.id
+ result[record.id]['cash_register_id'] = st.id
+
+ if not result[record.id]['cash_control']:
+ raise UserError(_("Cash control can only be applied to cash journals."))
+
+ return result
+
+ _columns = {
+ 'config_id' : fields.many2one('pos.config', 'Point of Sale',
+ help="The physical point of sale you will use.",
+ required=True,
+ select=1,
+ domain="[('state', '=', 'active')]",
+ ),
+
+ 'name' : fields.char('Session ID', required=True, readonly=True),
+ 'user_id' : fields.many2one('res.users', 'Responsible',
+ required=True,
+ select=1,
+ readonly=True,
+ states={'opening_control' : [('readonly', False)]}
+ ),
+ 'currency_id' : fields.related('config_id', 'currency_id', type="many2one", relation='res.currency', string="Currency"),
+ 'start_at' : fields.datetime('Opening Date', readonly=True),
+ 'stop_at' : fields.datetime('Closing Date', readonly=True, copy=False),
+
+ 'state' : fields.selection(POS_SESSION_STATE, 'Status',
+ required=True, readonly=True,
+ select=1, copy=False),
+
+ 'sequence_number': fields.integer('Order Sequence Number', help='A sequence number that is incremented with each order'),
+ 'login_number': fields.integer('Login Sequence Number', help='A sequence number that is incremented each time a user resumes the pos session'),
+
+ 'cash_control' : fields.function(_compute_cash_all,
+ multi='cash',
+ type='boolean', string='Has Cash Control'),
+ 'cash_journal_id' : fields.function(_compute_cash_all,
+ multi='cash',
+ type='many2one', relation='account.journal',
+ string='Cash Journal', store=True),
+ 'cash_register_id' : fields.function(_compute_cash_all,
+ multi='cash',
+ type='many2one', relation='account.bank.statement',
+ string='Cash Register', store=True),
+
+ 'cash_register_balance_end_real' : fields.related('cash_register_id', 'balance_end_real',
+ type='float',
+ digits=0,
+ string="Ending Balance",
+ help="Total of closing cash control lines.",
+ readonly=True),
+ 'cash_register_balance_start' : fields.related('cash_register_id', 'balance_start',
+ type='float',
+ digits=0,
+ string="Starting Balance",
+ help="Total of opening cash control lines.",
+ readonly=True),
+ 'cash_register_total_entry_encoding' : fields.related('cash_register_id', 'total_entry_encoding',
+ string='Total Cash Transaction',
+ readonly=True,
+ help="Total of all paid sale orders"),
+ 'cash_register_balance_end' : fields.related('cash_register_id', 'balance_end',
+ type='float',
+ digits=0,
+ string="Theoretical Closing Balance",
+ help="Sum of opening balance and transactions.",
+ readonly=True),
+ 'cash_register_difference' : fields.related('cash_register_id', 'difference',
+ type='float',
+ string='Difference',
+ help="Difference between the theoretical closing balance and the real closing balance.",
+ readonly=True),
+
+ 'journal_ids' : fields.related('config_id', 'journal_ids',
+ type='many2many',
+ readonly=True,
+ relation='account.journal',
+ string='Available Payment Methods'),
+ 'order_ids' : fields.one2many('pos.order', 'session_id', 'Orders'),
+
+ 'statement_ids' : fields.one2many('account.bank.statement', 'pos_session_id', 'Bank Statement', readonly=True),
+ }
+
+ _defaults = {
+ 'name' : '/',
+ 'user_id' : lambda obj, cr, uid, context: uid,
+ 'state' : 'opening_control',
+ 'sequence_number': 1,
+ 'login_number': 0,
+ }
+
+ _sql_constraints = [
+ ('uniq_name', 'unique(name)', "The name of this POS Session must be unique !"),
+ ]
+
+ def _check_unicity(self, cr, uid, ids, context=None):
+ for session in self.browse(cr, uid, ids, context=None):
+ # open if there is no session in 'opening_control', 'opened', 'closing_control' for one user
+ domain = [
+ ('state', 'not in', ('closed','closing_control')),
+ ('user_id', '=', session.user_id.id)
+ ]
+ count = self.search_count(cr, uid, domain, context=context)
+ if count>1:
+ return False
+ return True
+
+ def _check_pos_config(self, cr, uid, ids, context=None):
+ for session in self.browse(cr, uid, ids, context=None):
+ domain = [
+ ('state', '!=', 'closed'),
+ ('config_id', '=', session.config_id.id)
+ ]
+ count = self.search_count(cr, uid, domain, context=context)
+ if count>1:
+ return False
+ return True
+
+ _constraints = [
+ (_check_unicity, "You cannot create two active sessions with the same responsible!", ['user_id', 'state']),
+ (_check_pos_config, "You cannot create two active sessions related to the same point of sale!", ['config_id']),
+ ]
+
+ def create(self, cr, uid, values, context=None):
+ context = dict(context or {})
+ config_id = values.get('config_id', False) or context.get('default_config_id', False)
+ if not config_id:
+ raise UserError(_("You should assign a Point of Sale to your session."))
+
+ # journal_id is not required on the pos_config because it does not
+ # exists at the installation. If nothing is configured at the
+ # installation we do the minimal configuration. Impossible to do in
+ # the .xml files as the CoA is not yet installed.
+ jobj = self.pool.get('pos.config')
+ pos_config = jobj.browse(cr, uid, config_id, context=context)
+ context.update({'company_id': pos_config.company_id.id})
+ if not pos_config.journal_id:
+ jid = jobj.default_get(cr, uid, ['journal_id'], context=context)['journal_id']
+ if jid:
+ jobj.write(cr, SUPERUSER_ID, [pos_config.id], {'journal_id': jid}, context=context)
+ else:
+ raise UserError(_("Unable to open the session. You have to assign a sale journal to your point of sale."))
+
+ # define some cash journal if no payment method exists
+ if not pos_config.journal_ids:
+ journal_proxy = self.pool.get('account.journal')
+ cashids = journal_proxy.search(cr, uid, [('journal_user', '=', True), ('type','=','cash')], context=context)
+ if not cashids:
+ cashids = journal_proxy.search(cr, uid, [('type', '=', 'cash')], context=context)
+ if not cashids:
+ cashids = journal_proxy.search(cr, uid, [('journal_user','=',True)], context=context)
+
+ journal_proxy.write(cr, SUPERUSER_ID, cashids, {'journal_user': True})
+ jobj.write(cr, SUPERUSER_ID, [pos_config.id], {'journal_ids': [(6,0, cashids)]})
+
+
+ pos_config = jobj.browse(cr, uid, config_id, context=context)
+
+ statements = [(0, 0, {
+ 'journal_id': journal.id,
+ 'user_id': uid,
+ 'company_id': pos_config.company_id.id
+ }) for journal in pos_config.journal_ids]
+
+ values.update({
+ 'name': self.pool['ir.sequence'].next_by_code(cr, uid, 'pos.session', context=context),
+ 'statement_ids': statements,
+ 'config_id': config_id
+ })
+
+ # set the journal_id which should be used by
+ # account.bank.statement to set the opening balance of the
+ # newly created bank statement
+ if pos_config.cash_control:
+ for journal in pos_config.journal_ids:
+ if journal.type == 'cash':
+ context.update({'journal_id': journal.id})
+
+ return super(pos_session, self).create(cr, uid, values, context=context)
+
+ def unlink(self, cr, uid, ids, context=None):
+ for obj in self.browse(cr, uid, ids, context=context):
+ self.pool.get('account.bank.statement').unlink(cr, uid, obj.statement_ids.ids, context=context)
+ return super(pos_session, self).unlink(cr, uid, ids, context=context)
+
+ def login(self, cr, uid, ids, context=None):
+ this_record = self.browse(cr, uid, ids[0], context=context)
+ this_record.write({
+ 'login_number': this_record.login_number+1,
+ })
+
+ def wkf_action_open(self, cr, uid, ids, context=None):
+ # second browse because we need to refetch the data from the DB for cash_register_id
+ for record in self.browse(cr, uid, ids, context=context):
+ values = {}
+ if not record.start_at:
+ values['start_at'] = time.strftime('%Y-%m-%d %H:%M:%S')
+ values['state'] = 'opened'
+ record.write(values)
+ for st in record.statement_ids:
+ st.button_open()
+
+ return True
+
+ def wkf_action_opening_control(self, cr, uid, ids, context=None):
+ return self.write(cr, uid, ids, {'state' : 'opening_control'}, context=context)
+
+ def wkf_action_closing_control(self, cr, uid, ids, context=None):
+ for session in self.browse(cr, uid, ids, context=context):
+ for statement in session.statement_ids:
+ if (statement != session.cash_register_id) and (statement.balance_end != statement.balance_end_real):
+ self.pool.get('account.bank.statement').write(cr, uid, [statement.id], {'balance_end_real': statement.balance_end})
+ return self.write(cr, uid, ids, {'state' : 'closing_control', 'stop_at' : time.strftime('%Y-%m-%d %H:%M:%S')}, context=context)
+
+ def wkf_action_close(self, cr, uid, ids, context=None):
+ # Close CashBox
+ local_context = dict(context)
+ for record in self.browse(cr, uid, ids, context=context):
+ company_id = record.config_id.company_id.id
+ local_context.update({'force_company': company_id, 'company_id': company_id})
+ for st in record.statement_ids:
+ if abs(st.difference) > st.journal_id.amount_authorized_diff:
+ # The pos manager can close statements with maximums.
+ if not self.pool.get('ir.model.access').check_groups(cr, uid, "point_of_sale.group_pos_manager"):
+ raise UserError(_("Your ending balance is too different from the theoretical cash closing (%.2f), the maximum allowed is: %.2f. You can contact your manager to force it.") % (st.difference, st.journal_id.amount_authorized_diff))
+ if (st.journal_id.type not in ['bank', 'cash']):
+ raise UserError(_("The type of the journal for your payment method should be bank or cash "))
+ self.pool['account.bank.statement'].button_confirm_bank(cr, SUPERUSER_ID, [st.id], context=local_context)
+ self._confirm_orders(cr, uid, ids, context=local_context)
+ self.write(cr, uid, ids, {'state' : 'closed'}, context=local_context)
+
+ obj = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'point_of_sale', 'menu_point_root')[1]
+ return {
+ 'type' : 'ir.actions.client',
+ 'name' : 'Point of Sale Menu',
+ 'tag' : 'reload',
+ 'params' : {'menu_id': obj},
+ }
+
+ def _confirm_orders(self, cr, uid, ids, context=None):
+ pos_order_obj = self.pool.get('pos.order')
+ for session in self.browse(cr, uid, ids, context=context):
+ company_id = session.config_id.journal_id.company_id.id
+ local_context = dict(context or {}, force_company=company_id)
+ order_ids = [order.id for order in session.order_ids if order.state == 'paid']
+
+ move_id = pos_order_obj._create_account_move(cr, uid, session.start_at, session.name, session.config_id.journal_id.id, company_id, context=context)
+
+ pos_order_obj._create_account_move_line(cr, uid, order_ids, session, move_id, context=local_context)
+
+ for order in session.order_ids:
+ if order.state == 'done':
+ continue
+ if order.state not in ('paid', 'invoiced'):
+ raise UserError(_("You cannot confirm all orders of this session, because they have not the 'paid' status"))
+ else:
+ pos_order_obj.signal_workflow(cr, uid, [order.id], 'done')
+
+ return True
+
+ def open_frontend_cb(self, cr, uid, ids, context=None):
+ if not context:
+ context = {}
+ if not ids:
+ return {}
+ for session in self.browse(cr, uid, ids, context=context):
+ if session.user_id.id != uid:
+ raise UserError(_("You cannot use the session of another users. This session is owned by %s. "
+ "Please first close this one to use this point of sale.") % session.user_id.name)
+ context.update({'active_id': ids[0]})
+ return {
+ 'type' : 'ir.actions.act_url',
+ 'target': 'self',
+ 'url': '/pos/web/',
+ }
diff --git a/addons/point_of_sale/models/product.py b/addons/point_of_sale/models/product.py
new file mode 100644
index 00000000000..dd2607c4d60
--- /dev/null
+++ b/addons/point_of_sale/models/product.py
@@ -0,0 +1,27 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+from openerp.osv import fields, osv
+from openerp.tools.translate import _
+
+
+class product_template(osv.osv):
+ _inherit = 'product.template'
+
+ _columns = {
+ 'available_in_pos': fields.boolean('Available in the Point of Sale', help='Check if you want this product to appear in the Point of Sale'),
+ 'to_weight' : fields.boolean('To Weigh With Scale', help="Check if the product should be weighted using the hardware scale integration"),
+ 'pos_categ_id': fields.many2one('pos.category','Point of Sale Category', help="Those categories are used to group similar products for point of sale."),
+ }
+
+ _defaults = {
+ 'to_weight' : False,
+ 'available_in_pos': True,
+ }
+
+ def unlink(self, cr, uid, ids, context=None):
+ product_ctx = dict(context or {}, active_test=False)
+ if self.search_count(cr, uid, [('id', 'in', ids), ('available_in_pos', '=', True)], context=product_ctx):
+ if self.pool['pos.session'].search_count(cr, uid, [('state', '!=', 'closed')], context=context):
+ raise osv.except_osv(_('Error!'),
+ _('You cannot delete a product saleable in point of sale while a session is still opened.'))
+ return super(product_template, self).unlink(cr, uid, ids, context=context)
diff --git a/addons/point_of_sale/models/res_partner.py b/addons/point_of_sale/models/res_partner.py
index 53c27c9fd96..b5167b9bea7 100644
--- a/addons/point_of_sale/models/res_partner.py
+++ b/addons/point_of_sale/models/res_partner.py
@@ -6,4 +6,22 @@ class res_users(osv.osv):
_inherit = 'res.partner'
_columns = {
'barcode' : fields.char('Barcode', help="BarCode", oldname='ean13'),
- }
\ No newline at end of file
+ }
+
+ def create_from_ui(self, cr, uid, partner, context=None):
+ """ create or modify a partner from the point of sale ui.
+ partner contains the partner's fields. """
+
+ #image is a dataurl, get the data after the comma
+ if partner.get('image',False):
+ img = partner['image'].split(',')[1]
+ partner['image'] = img
+
+ if partner.get('id',False): # Modifying existing partner
+ partner_id = partner['id']
+ del partner['id']
+ self.write(cr, uid, [partner_id], partner, context=context)
+ else:
+ partner_id = self.create(cr, uid, partner, context=context)
+
+ return partner_id
diff --git a/addons/point_of_sale/views/account_invoice_view.xml b/addons/point_of_sale/views/account_invoice_view.xml
new file mode 100644
index 00000000000..58bfe004dc0
--- /dev/null
+++ b/addons/point_of_sale/views/account_invoice_view.xml
@@ -0,0 +1,13 @@
+
+
+
+
+ Invoices
+ ir.actions.act_window
+ account.invoice
+ form
+ tree,form
+ [('origin','like','POS')]
+
+
+
\ No newline at end of file
diff --git a/addons/point_of_sale/views/account_journal_view.xml b/addons/point_of_sale/views/account_journal_view.xml
new file mode 100644
index 00000000000..dbd7f41299e
--- /dev/null
+++ b/addons/point_of_sale/views/account_journal_view.xml
@@ -0,0 +1,29 @@
+
+
+
+
+ Payment Methods
+ account.journal
+ form
+ tree,form
+
+ [('journal_user','=', 1)]
+
+
+ Click to add a payment method.
+
+ Payment methods are defined by accounting journals having the
+ field PoS Payment Method checked. In order to be useable
+ from the touchscreen interface, you must set the payment method
+ on the Point of Sale configuration.
+
- You must define a product for everything you sell through
- the point of sale interface.
-
- Do not forget to set the price and the point of sale category
- in which it should appear. If a product has no point of sale
- category, you can not sell it through the point of sale
- interface.
-
- Categories are used to browse your products through the
- touchscreen interface.
-
- If you put a photo on the category and enable the option in the
- point of sale configuration, the layout of the touchscreen interface
- will automatically be displayed. We suggest not to put a photo
- on categories for small (1024x768) screens.
-
- Payment methods are defined by accounting journals having the
- field PoS Payment Method checked. In order to be useable
- from the touchscreen interface, you must set the payment method
- on the Point of Sale configuration.
-
- A session is a period of time, usually one day, during which
- you sell through the point of sale. The user has to check the
- currencies in your cash registers at the beginning and the end
- of each session.
-
- Note that you may use the menu Your Session
- to quickly open a new session.
-
+ Categories are used to browse your products through the
+ touchscreen interface.
+
+ If you put a photo on the category and enable the option in the
+ point of sale configuration, the layout of the touchscreen interface
+ will automatically be displayed. We suggest not to put a photo
+ on categories for small (1024x768) screens.
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/addons/point_of_sale/views/pos_config_view.xml b/addons/point_of_sale/views/pos_config_view.xml
new file mode 100644
index 00000000000..18b8d0e5c6b
--- /dev/null
+++ b/addons/point_of_sale/views/pos_config_view.xml
@@ -0,0 +1,147 @@
+
+
+
+
+ pos.config.form.view
+ pos.config
+
+
+ A session is a period of time, usually one day, during which
+ you sell through the point of sale. The user has to check the
+ currencies in your cash registers at the beginning and the end
+ of each session.
+
+ Note that you may use the menu Your Session
+ to quickly open a new session.
+
+ You must define a product for everything you sell through
+ the point of sale interface.
+
+ Do not forget to set the price and the point of sale category
+ in which it should appear. If a product has no point of sale
+ category, you can not sell it through the point of sale
+ interface.
+
+
+
+
+
+
+ product.template.form.inherit
+ product.template
+
+
+
+
+
+
+
+
+
+
+
+
+ product.template.tree.inherit
+ product.template
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/addons/point_of_sale/wizard/pos_payment.py b/addons/point_of_sale/wizard/pos_payment.py
index 40d8ff18a55..f609a5ffee5 100644
--- a/addons/point_of_sale/wizard/pos_payment.py
+++ b/addons/point_of_sale/wizard/pos_payment.py
@@ -7,27 +7,10 @@ from openerp.osv import osv, fields
from openerp.tools.translate import _
-class account_journal(osv.osv):
- _inherit = 'account.journal'
-
- def search(self, cr, uid, args, offset=0, limit=None, order=None, context=None, count=False):
- if not context:
- context = {}
- session_id = context.get('pos_session_id', False) or False
-
- if session_id:
- session = self.pool.get('pos.session').browse(cr, uid, session_id, context=context)
-
- if session:
- journal_ids = [journal.id for journal in session.config_id.journal_ids]
- args += [('id', 'in', journal_ids)]
-
- return super(account_journal, self).search(cr, uid, args, offset=offset, limit=limit, order=order, context=context, count=count)
-
-
class pos_make_payment(osv.osv_memory):
_name = 'pos.make.payment'
_description = 'Point of Sale Payment'
+
def check(self, cr, uid, ids, context=None):
"""Check the order:
if the order is not paid: continue payment,
From 8ab5b8664a2a1791106fea0923b6d179de9c04b5 Mon Sep 17 00:00:00 2001
From: Martin Trigaux
Date: Mon, 1 Feb 2016 14:47:50 +0100
Subject: [PATCH 3/9] [IMP] point_of_sale: remove unused action
Never called
---
addons/point_of_sale/__openerp__.py | 1 -
addons/point_of_sale/views/account_invoice_view.xml | 13 -------------
2 files changed, 14 deletions(-)
delete mode 100644 addons/point_of_sale/views/account_invoice_view.xml
diff --git a/addons/point_of_sale/__openerp__.py b/addons/point_of_sale/__openerp__.py
index f3c455b9023..d49ddac2cb9 100644
--- a/addons/point_of_sale/__openerp__.py
+++ b/addons/point_of_sale/__openerp__.py
@@ -49,7 +49,6 @@ Main Features
'views/account_journal_view.xml',
'views/pos_config_view.xml',
'views/pos_session_view.xml',
- 'views/account_invoice_view.xml',
'views/point_of_sale_sequence.xml',
'data/point_of_sale_data.xml',
'data/point_of_sale_workflow.xml',
diff --git a/addons/point_of_sale/views/account_invoice_view.xml b/addons/point_of_sale/views/account_invoice_view.xml
deleted file mode 100644
index 58bfe004dc0..00000000000
--- a/addons/point_of_sale/views/account_invoice_view.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
-
-
- Invoices
- ir.actions.act_window
- account.invoice
- form
- tree,form
- [('origin','like','POS')]
-
-
-
\ No newline at end of file
From 7d93f9904e01bd8df187b41e3915e935db7d78ec Mon Sep 17 00:00:00 2001
From: Martin Trigaux
Date: Tue, 2 Feb 2016 12:26:52 +0100
Subject: [PATCH 4/9] [IMP] point_of_sale: remove complete_name and
iface_fullscreen
complete_name is actually the existing orm field display_name
field iface_fullscreen is never used, cf TODO
---
addons/point_of_sale/models/pos_category.py | 5 -----
addons/point_of_sale/models/pos_config.py | 3 +--
addons/point_of_sale/views/pos_category_view.xml | 2 +-
3 files changed, 2 insertions(+), 8 deletions(-)
diff --git a/addons/point_of_sale/models/pos_category.py b/addons/point_of_sale/models/pos_category.py
index 8c9d2ba5253..b9d8c1b02e4 100644
--- a/addons/point_of_sale/models/pos_category.py
+++ b/addons/point_of_sale/models/pos_category.py
@@ -25,13 +25,8 @@ class pos_category(osv.osv):
res.append((cat.id, ' / '.join(reversed(names))))
return res
- def _name_get_fnc(self, cr, uid, ids, prop, unknow_none, context=None):
- res = self.name_get(cr, uid, ids, context=context)
- return dict(res)
-
_columns = {
'name': fields.char('Name', required=True, translate=True),
- 'complete_name': fields.function(_name_get_fnc, type="char", string='Name'),
'parent_id': fields.many2one('pos.category','Parent Category', select=True),
'child_id': fields.one2many('pos.category', 'parent_id', string='Children Categories'),
'sequence': fields.integer('Sequence', help="Gives the sequence order when displaying a list of product categories."),
diff --git a/addons/point_of_sale/models/pos_config.py b/addons/point_of_sale/models/pos_config.py
index dfa363d1745..db57f6007de 100644
--- a/addons/point_of_sale/models/pos_config.py
+++ b/addons/point_of_sale/models/pos_config.py
@@ -85,8 +85,7 @@ class pos_config(osv.osv):
'iface_scan_via_proxy' : fields.boolean('Scan via Proxy', help="Enable barcode scanning with a remotely connected barcode scanner"),
'iface_invoicing': fields.boolean('Invoicing',help='Enables invoice generation from the Point of Sale'),
'iface_big_scrollbars': fields.boolean('Large Scrollbars',help='For imprecise industrial touchscreens'),
- # TODO master: remove the `iface_fullscreen` field. This is no longer used.
- 'iface_fullscreen': fields.boolean('Fullscreen', help='Display the Point of Sale in full screen mode'),
+
'iface_print_auto': fields.boolean('Automatic Receipt Printing', help='The receipt will automatically be printed at the end of each order'),
'iface_print_skip_screen': fields.boolean('Skip Receipt Screen', help='The receipt screen will be skipped if the receipt can be printed automatically.'),
'iface_precompute_cash': fields.boolean('Prefill Cash Payment', help='The payment input will behave similarily to bank payment input, and will be prefilled with the exact due amount'),
diff --git a/addons/point_of_sale/views/pos_category_view.xml b/addons/point_of_sale/views/pos_category_view.xml
index 9b9932df98a..3b5f1106ace 100644
--- a/addons/point_of_sale/views/pos_category_view.xml
+++ b/addons/point_of_sale/views/pos_category_view.xml
@@ -27,7 +27,7 @@
-
+
From 62406fa4bf9b3cd0edf4227c137ec6295b53669d Mon Sep 17 00:00:00 2001
From: Darshan Kalola
Date: Thu, 31 Dec 2015 16:07:54 +0530
Subject: [PATCH 5/9] [REM] point of sale: removed useless wizard and code
Never called or not used
---
addons/point_of_sale/__openerp__.py | 1 -
addons/point_of_sale/models/pos_order.py | 5 +--
.../test/01_order_to_payment.yml | 2 +-
addons/point_of_sale/wizard/__init__.py | 1 -
addons/point_of_sale/wizard/pos_confirm.py | 32 -------------------
addons/point_of_sale/wizard/pos_confirm.xml | 20 ------------
addons/point_of_sale/wizard/pos_payment.py | 10 +-----
7 files changed, 3 insertions(+), 68 deletions(-)
delete mode 100644 addons/point_of_sale/wizard/pos_confirm.py
delete mode 100644 addons/point_of_sale/wizard/pos_confirm.xml
diff --git a/addons/point_of_sale/__openerp__.py b/addons/point_of_sale/__openerp__.py
index d49ddac2cb9..13800334325 100644
--- a/addons/point_of_sale/__openerp__.py
+++ b/addons/point_of_sale/__openerp__.py
@@ -34,7 +34,6 @@ Main Features
'security/ir.model.access.csv',
'data/default_barcode_patterns.xml',
'wizard/pos_box.xml',
- 'wizard/pos_confirm.xml',
'wizard/pos_details.xml',
'wizard/pos_discount.xml',
'wizard/pos_open_statement.xml',
diff --git a/addons/point_of_sale/models/pos_order.py b/addons/point_of_sale/models/pos_order.py
index 65e4754908e..71fb19a47e0 100644
--- a/addons/point_of_sale/models/pos_order.py
+++ b/addons/point_of_sale/models/pos_order.py
@@ -566,9 +566,6 @@ class pos_order(osv.osv):
'res_id': inv_ids and inv_ids[0] or False,
}
- def create_account_move(self, cr, uid, ids, context=None):
- return self._create_account_move_line(cr, uid, ids, None, None, context=context)
-
def _prepare_analytic_account(self, cr, uid, line, context=None):
'''This method is designed to be inherited in a custom module'''
return False
@@ -756,7 +753,7 @@ class pos_order(osv.osv):
return True
def action_done(self, cr, uid, ids, context=None):
- self.create_account_move(cr, uid, ids, context=context)
+ self._create_account_move_line(cr, uid, ids, context=context)
return True
class pos_order_line(osv.osv):
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 46b7b2b1444..a5f41fec71b 100644
--- a/addons/point_of_sale/test/01_order_to_payment.yml
+++ b/addons/point_of_sale/test/01_order_to_payment.yml
@@ -124,7 +124,7 @@
I generate the journal entries
-
!python {model: pos.order}: |
- self.create_account_move(cr, uid, [ref('pos_order_pos0')], {})
+ self._create_account_move_lines(cr, uid, [ref('pos_order_pos0')], {})
-
I test that the generated journal entry is attached to the PoS order
-
diff --git a/addons/point_of_sale/wizard/__init__.py b/addons/point_of_sale/wizard/__init__.py
index 2cd077955ca..442fc2a4133 100644
--- a/addons/point_of_sale/wizard/__init__.py
+++ b/addons/point_of_sale/wizard/__init__.py
@@ -2,7 +2,6 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import pos_box
-import pos_confirm
import pos_details
import pos_discount
import pos_open_statement
diff --git a/addons/point_of_sale/wizard/pos_confirm.py b/addons/point_of_sale/wizard/pos_confirm.py
deleted file mode 100644
index 78604811dad..00000000000
--- a/addons/point_of_sale/wizard/pos_confirm.py
+++ /dev/null
@@ -1,32 +0,0 @@
-# -*- coding: utf-8 -*-
-# Part of Odoo. See LICENSE file for full copyright and licensing details.
-
-from openerp.osv import osv
-
-
-class pos_confirm(osv.osv_memory):
- _name = 'pos.confirm'
- _description = 'Post POS Journal Entries'
-
- def action_confirm(self, cr, uid, ids, context=None):
- order_obj = self.pool.get('pos.order')
- ids = order_obj.search(cr, uid, [('state','=','paid')], context=context)
- for order in order_obj.browse(cr, uid, ids, context=context):
- todo = True
- for line in order.statement_ids:
- if line.statement_id.state != 'confirm':
- todo = False
- break
- if todo:
- order.signal_workflow('done')
-
- # Check if there is orders to reconcile their invoices
- ids = order_obj.search(cr, uid, [('state','=','invoiced'),('invoice_id.state','=','open')], context=context)
- for order in order_obj.browse(cr, uid, ids, context=context):
- invoice = order.invoice_id
- data_lines = [x.id for x in invoice.move_id.line_id if x.account_id.id == invoice.account_id.id]
- for st in order.statement_ids:
- for move in st.move_ids:
- data_lines += [x.id for x in move.line_id if x.account_id.id == invoice.account_id.id]
- self.pool.get('account.move.line').reconcile(cr, uid, data_lines, context=context)
- return {}
diff --git a/addons/point_of_sale/wizard/pos_confirm.xml b/addons/point_of_sale/wizard/pos_confirm.xml
deleted file mode 100644
index d0a1653a348..00000000000
--- a/addons/point_of_sale/wizard/pos_confirm.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-
-
-
-
-
- Post Sale Journal Entries
- pos.confirm
-
-
-
-
-
-
-
-
-
-
diff --git a/addons/point_of_sale/wizard/pos_payment.py b/addons/point_of_sale/wizard/pos_payment.py
index f609a5ffee5..ed53de42cdd 100644
--- a/addons/point_of_sale/wizard/pos_payment.py
+++ b/addons/point_of_sale/wizard/pos_payment.py
@@ -48,14 +48,6 @@ class pos_make_payment(osv.osv_memory):
'context': context,
}
- def print_report(self, cr, uid, ids, context=None):
- active_id = context.get('active_id', [])
- datas = {'ids' : [active_id]}
- return {
- 'type': 'ir.actions.report.xml',
- 'report_name': 'pos.receipt',
- 'datas': datas,
- }
def _default_journal(self, cr, uid, context=None):
if not context:
@@ -89,4 +81,4 @@ class pos_make_payment(osv.osv_memory):
'journal_id' : _default_journal,
'payment_date': lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'),
'amount': _default_amount,
- }
+ }
\ No newline at end of file
From e741de8b0c152279eb2d20faa77d06df8e50b91a Mon Sep 17 00:00:00 2001
From: Martin Trigaux
Date: Wed, 17 Feb 2016 13:27:20 +0100
Subject: [PATCH 6/9] [IMP] point_of_sale: remove some reports
- report.transaction.pos
- report.sales.by.user.pos
- report.sales.by.user.pos.month
These reports where not called anywhere and have been replaced by pivot views
Remove order reports that can be easily replaced by use of the pivot view of
report.pos.order
Add a session_id field and a filter on report.pos.order to make it equivalent.
The invoice report is only useful when called from the pos UI so keeping the
report but not added in the print menu
---
addons/point_of_sale/__openerp__.py | 6 -
addons/point_of_sale/report/__init__.py | 8 +-
addons/point_of_sale/report/pos_details.py | 181 ------------
addons/point_of_sale/report/pos_lines.py | 43 ---
.../point_of_sale/report/pos_order_report.py | 16 +-
.../report/pos_payment_report.py | 52 ----
addons/point_of_sale/report/pos_report.py | 121 --------
.../security/ir.model.access.csv | 3 -
.../test/point_of_sale_report.yml | 43 ---
.../views/point_of_sale_report.xml | 266 +-----------------
.../views/pos_order_report_view.xml | 7 +
addons/point_of_sale/views/pos_order_view.xml | 3 -
.../views/report_detailsofsales.xml | 140 ---------
addons/point_of_sale/views/report_payment.xml | 91 ------
.../point_of_sale/views/report_saleslines.xml | 98 -------
.../views/report_sessionsummary.xml | 124 --------
addons/point_of_sale/wizard/__init__.py | 1 -
addons/point_of_sale/wizard/pos_details.py | 39 ---
addons/point_of_sale/wizard/pos_details.xml | 36 ---
19 files changed, 28 insertions(+), 1250 deletions(-)
delete mode 100644 addons/point_of_sale/report/pos_details.py
delete mode 100644 addons/point_of_sale/report/pos_lines.py
delete mode 100644 addons/point_of_sale/report/pos_payment_report.py
delete mode 100644 addons/point_of_sale/report/pos_report.py
delete mode 100644 addons/point_of_sale/test/point_of_sale_report.yml
delete mode 100644 addons/point_of_sale/views/report_detailsofsales.xml
delete mode 100644 addons/point_of_sale/views/report_payment.xml
delete mode 100644 addons/point_of_sale/views/report_saleslines.xml
delete mode 100644 addons/point_of_sale/views/report_sessionsummary.xml
delete mode 100644 addons/point_of_sale/wizard/pos_details.py
delete mode 100644 addons/point_of_sale/wizard/pos_details.xml
diff --git a/addons/point_of_sale/__openerp__.py b/addons/point_of_sale/__openerp__.py
index 13800334325..9a84760bb68 100644
--- a/addons/point_of_sale/__openerp__.py
+++ b/addons/point_of_sale/__openerp__.py
@@ -34,7 +34,6 @@ Main Features
'security/ir.model.access.csv',
'data/default_barcode_patterns.xml',
'wizard/pos_box.xml',
- 'wizard/pos_details.xml',
'wizard/pos_discount.xml',
'wizard/pos_open_statement.xml',
'wizard/pos_payment.xml',
@@ -59,10 +58,6 @@ Main Features
'views/res_config_view.xml',
'views/report_statement.xml',
'views/report_usersproduct.xml',
- 'views/report_saleslines.xml',
- 'views/report_detailsofsales.xml',
- 'views/report_payment.xml',
- 'views/report_sessionsummary.xml',
'views/report_userlabel.xml',
'views/point_of_sale.xml',
'views/point_of_sale_dashboard.xml',
@@ -76,7 +71,6 @@ Main Features
'test/00_register_open.yml',
'test/01_order_to_payment.yml',
'test/02_order_to_invoice.yml',
- 'test/point_of_sale_report.yml',
'test/account_statement_reports.yml',
],
'installable': True,
diff --git a/addons/point_of_sale/report/__init__.py b/addons/point_of_sale/report/__init__.py
index 093cf63f789..3de0210b9f0 100644
--- a/addons/point_of_sale/report/__init__.py
+++ b/addons/point_of_sale/report/__init__.py
@@ -1,12 +1,8 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
-import pos_users_product
import account_statement
-import pos_receipt
import pos_invoice
-import pos_lines
-import pos_details
-import pos_payment_report
-import pos_report
import pos_order_report
+import pos_receipt
+import pos_users_product
diff --git a/addons/point_of_sale/report/pos_details.py b/addons/point_of_sale/report/pos_details.py
deleted file mode 100644
index 4625ddbd766..00000000000
--- a/addons/point_of_sale/report/pos_details.py
+++ /dev/null
@@ -1,181 +0,0 @@
-# -*- coding: utf-8 -*-
-# Part of Odoo. See LICENSE file for full copyright and licensing details.
-
-import time
-from openerp.osv import osv
-from openerp.report import report_sxw
-
-
-class pos_details(report_sxw.rml_parse):
-
- def _get_invoice(self, inv_id):
- res={}
- if inv_id:
- self.cr.execute("select number from account_invoice as ac where id = %s", (inv_id,))
- res = self.cr.fetchone()
- return res[0] or 'Draft'
- else:
- return ''
-
- def _get_all_users(self):
- user_obj = self.pool.get('res.users')
- return user_obj.search(self.cr, self.uid, [])
-
- def _pos_sales_details(self, form):
- pos_obj = self.pool.get('pos.order')
- user_obj = self.pool.get('res.users')
- data = []
- result = {}
- user_ids = form['user_ids'] or self._get_all_users()
- company_id = user_obj.browse(self.cr, self.uid, self.uid).company_id.id
- pos_ids = pos_obj.search(self.cr, self.uid, [('date_order','>=',form['date_start'] + ' 00:00:00'),('date_order','<=',form['date_end'] + ' 23:59:59'),('user_id','in',user_ids),('state','in',['done','paid','invoiced']),('company_id','=',company_id)])
- for pos in pos_obj.browse(self.cr, self.uid, pos_ids, context=self.localcontext):
- for pol in pos.lines:
- result = {
- 'code': pol.product_id.default_code,
- 'name': pol.product_id.name,
- 'invoice_id': pos.invoice_id.id,
- 'price_unit': pol.price_unit,
- 'qty': pol.qty,
- 'discount': pol.discount,
- 'total': (pol.price_unit * pol.qty * (1 - (pol.discount) / 100.0)),
- 'date_order': pos.date_order,
- 'pos_name': pos.name,
- 'uom': pol.product_id.uom_id.name
- }
- data.append(result)
- self.total += result['total']
- self.qty += result['qty']
- self.discount += result['discount']
- if data:
- return data
- else:
- return {}
-
- def _get_qty_total_2(self):
- return self.qty
-
- def _get_sales_total_2(self):
- return self.total
-
- def _get_sum_invoice_2(self, form):
- pos_obj = self.pool.get('pos.order')
- user_obj = self.pool.get('res.users')
- user_ids = form['user_ids'] or self._get_all_users()
- company_id = user_obj.browse(self.cr, self.uid, self.uid).company_id.id
- pos_ids = pos_obj.search(self.cr, self.uid, [('date_order','>=',form['date_start'] + ' 00:00:00'),('date_order','<=',form['date_end'] + ' 23:59:59'),('user_id','in',user_ids),('company_id','=',company_id),('invoice_id','<>',False)])
- for pos in pos_obj.browse(self.cr, self.uid, pos_ids):
- for pol in pos.lines:
- self.total_invoiced += (pol.price_unit * pol.qty * (1 - (pol.discount) / 100.0))
- return self.total_invoiced or False
-
- def _paid_total_2(self):
- return self.total or 0.0
-
- def _get_sum_dis_2(self):
- return self.discount or 0.0
-
- def _get_sum_discount(self, form):
- #code for the sum of discount value
- pos_obj = self.pool.get('pos.order')
- user_obj = self.pool.get('res.users')
- user_ids = form['user_ids'] or self._get_all_users()
- company_id = user_obj.browse(self.cr, self.uid, self.uid).company_id.id
- pos_ids = pos_obj.search(self.cr, self.uid, [('date_order','>=',form['date_start'] + ' 00:00:00'),('date_order','<=',form['date_end'] + ' 23:59:59'),('user_id','in',user_ids),('company_id','=',company_id)])
- for pos in pos_obj.browse(self.cr, self.uid, pos_ids):
- for pol in pos.lines:
- self.total_discount += ((pol.price_unit * pol.qty) * (pol.discount / 100))
- return self.total_discount or False
-
- def _get_payments(self, form):
- statement_line_obj = self.pool.get("account.bank.statement.line")
- pos_order_obj = self.pool.get("pos.order")
- user_ids = form['user_ids'] or self._get_all_users()
- company_id = self.pool['res.users'].browse(self.cr, self.uid, self.uid).company_id.id
- pos_ids = pos_order_obj.search(self.cr, self.uid, [('date_order','>=',form['date_start'] + ' 00:00:00'),('date_order','<=',form['date_end'] + ' 23:59:59'),('state','in',['paid','invoiced','done']),('user_id','in',user_ids), ('company_id', '=', company_id)])
- data={}
- if pos_ids:
- st_line_ids = statement_line_obj.search(self.cr, self.uid, [('pos_statement_id', 'in', pos_ids)])
- if st_line_ids:
- st_id = statement_line_obj.browse(self.cr, self.uid, st_line_ids)
- a_l=[]
- for r in st_id:
- a_l.append(r['id'])
- self.cr.execute("select aj.name,sum(amount) from account_bank_statement_line as absl,account_bank_statement as abs,account_journal as aj " \
- "where absl.statement_id = abs.id and abs.journal_id = aj.id and absl.id IN %s " \
- "group by aj.name ",(tuple(a_l),))
-
- data = self.cr.dictfetchall()
- return data
- else:
- return {}
-
- def _total_of_the_day(self, objects):
- return self.total or 0.00
-
- def _sum_invoice(self, objects):
- return reduce(lambda acc, obj:
- acc + obj.invoice_id.amount_total,
- [o for o in objects if o.invoice_id and o.invoice_id.number],
- 0.0)
-
- def _ellipsis(self, orig_str, maxlen=100, ellipsis='...'):
- maxlen = maxlen - len(ellipsis)
- if maxlen <= 0:
- maxlen = 1
- new_str = orig_str[:maxlen]
- return new_str
-
- def _strip_name(self, name, maxlen=50):
- return self._ellipsis(name, maxlen, ' ...')
-
- def _get_tax_amount(self, form):
- taxes = {}
- account_tax_obj = self.pool.get('account.tax')
- user_ids = form['user_ids'] or self._get_all_users()
- pos_order_obj = self.pool.get('pos.order')
- company_id = self.pool['res.users'].browse(self.cr, self.uid, self.uid).company_id.id
- pos_ids = pos_order_obj.search(self.cr, self.uid, [('date_order','>=',form['date_start'] + ' 00:00:00'),('date_order','<=',form['date_end'] + ' 23:59:59'),('state','in',['paid','invoiced','done']),('user_id','in',user_ids), ('company_id', '=', company_id)])
- for order in pos_order_obj.browse(self.cr, self.uid, pos_ids):
- currency = order.session_id.currency_id
- for line in order.lines:
- if line.product_id.taxes_id:
- line_taxes = line.product_id.taxes_id.compute_all(line.price_unit * (1-(line.discount or 0.0)/100.0), currency, line.qty, product=line.product_id, partner=line.order_id.partner_id or False)
- for tax in line_taxes['taxes']:
- taxes.setdefault(tax['id'], {'name': tax['name'], 'amount':0.0})
- taxes[tax['id']]['amount'] += tax['amount']
- return taxes.values()
-
- def _get_user_names(self, user_ids):
- user_obj = self.pool.get('res.users')
- return ', '.join(map(lambda x: x.name, user_obj.browse(self.cr, self.uid, user_ids)))
-
- def __init__(self, cr, uid, name, context):
- super(pos_details, self).__init__(cr, uid, name, context=context)
- self.total = 0.0
- self.qty = 0.0
- self.total_invoiced = 0.0
- self.discount = 0.0
- self.total_discount = 0.0
- self.localcontext.update({
- 'time': time,
- 'strip_name': self._strip_name,
- 'getpayments': self._get_payments,
- 'getsumdisc': self._get_sum_discount,
- 'gettotaloftheday': self._total_of_the_day,
- 'gettaxamount': self._get_tax_amount,
- 'pos_sales_details':self._pos_sales_details,
- 'getqtytotal2': self._get_qty_total_2,
- 'getsalestotal2': self._get_sales_total_2,
- 'getsuminvoice2':self._get_sum_invoice_2,
- 'getpaidtotal2': self._paid_total_2,
- 'getinvoice':self._get_invoice,
- 'get_user_names': self._get_user_names,
- })
-
-
-class report_pos_details(osv.AbstractModel):
- _name = 'report.point_of_sale.report_detailsofsales'
- _inherit = 'report.abstract_report'
- _template = 'point_of_sale.report_detailsofsales'
- _wrapped_report_class = pos_details
diff --git a/addons/point_of_sale/report/pos_lines.py b/addons/point_of_sale/report/pos_lines.py
deleted file mode 100644
index 6f74cf40ab6..00000000000
--- a/addons/point_of_sale/report/pos_lines.py
+++ /dev/null
@@ -1,43 +0,0 @@
-# -*- coding: utf-8 -*-
-# Part of Odoo. See LICENSE file for full copyright and licensing details.
-
-import time
-from openerp.osv import osv
-from openerp.report import report_sxw
-
-
-class pos_lines(report_sxw.rml_parse):
-
- def __init__(self, cr, uid, name, context):
- super(pos_lines, self).__init__(cr, uid, name, context=context)
- self.total = 0.0
- self.localcontext.update({
- 'time': time,
- 'total_quantity': self.__total_quantity__,
- 'taxes':self.__taxes__,
-
- })
-
- def __total_quantity__(self, obj):
- tot = 0
- for line in obj.lines:
- tot += line.qty
- self.total = tot
- return self.total
-
- def __taxes__(self, obj):
- self.cr.execute ( " Select acct.name from pos_order as po " \
- " LEFT JOIN pos_order_line as pol ON po.id = pol.order_id " \
- " LEFT JOIN product_product as pp ON pol.product_id = pp.id"
- " LEFT JOIN product_taxes_rel as ptr ON pp.product_tmpl_id = ptr.prod_id " \
- " LEFT JOIN account_tax as acct ON acct.id = ptr.tax_id " \
- " WHERE pol.id = %s", (obj.id,))
- res=self.cr.fetchone()[0]
- return res
-
-
-class report_pos_lines(osv.AbstractModel):
- _name = 'report.point_of_sale.report_saleslines'
- _inherit = 'report.abstract_report'
- _template = 'point_of_sale.report_saleslines'
- _wrapped_report_class = pos_lines
diff --git a/addons/point_of_sale/report/pos_order_report.py b/addons/point_of_sale/report/pos_order_report.py
index 7adc4f8528b..7d528f8649d 100644
--- a/addons/point_of_sale/report/pos_order_report.py
+++ b/addons/point_of_sale/report/pos_order_report.py
@@ -33,6 +33,7 @@ class pos_order_report(osv.osv):
'pos_categ_id': fields.many2one('pos.category','Public Category', readonly=True),
'stock_location_id': fields.many2one('stock.location', 'Warehouse', readonly=True),
'pricelist_id': fields.many2one('product.pricelist', 'Pricelist', readonly=True),
+ 'session_id;: fields.many2one('pos.session', string='Session', readonly=True),
}
_order = 'date desc'
@@ -63,6 +64,7 @@ class pos_order_report(osv.osv):
pt.pos_categ_id,
pc.stock_location_id,
s.pricelist_id,
+ s.session_id,
s.invoice_id IS NOT NULL AS invoiced
from pos_order_line as l
left join pos_order s on (s.id=l.order_id)
@@ -73,6 +75,14 @@ class pos_order_report(osv.osv):
left join pos_config pc on (ps.config_id=pc.id)
group by
s.date_order, s.partner_id,s.state, pt.categ_id,
- s.user_id,s.location_id,s.company_id,s.sale_journal,s.pricelist_id,s.invoice_id,l.product_id,s.create_date,pt.categ_id,pt.pos_categ_id,p.product_tmpl_id,ps.config_id,pc.stock_location_id
- having
- sum(l.qty * u.factor) != 0)""")
+ s.user_id, s.location_id, s.company_id, s.sale_journal,
+ s.pricelist_id, s.invoice_id, s.create_date, s.session_id,
+ l.product_id,
+ pt.categ_id, pt.pos_categ_id,
+ p.product_tmpl_id,
+ ps.config_id,
+ pc.stock_location_id
+ HAVING
+ SUM(l.qty * u.factor) != 0
+ )
+ """)
diff --git a/addons/point_of_sale/report/pos_payment_report.py b/addons/point_of_sale/report/pos_payment_report.py
deleted file mode 100644
index e34e50e69bd..00000000000
--- a/addons/point_of_sale/report/pos_payment_report.py
+++ /dev/null
@@ -1,52 +0,0 @@
-# -*- coding: utf-8 -*-
-# Part of Odoo. See LICENSE file for full copyright and licensing details.
-
-import time
-from openerp.osv import osv
-from openerp.report import report_sxw
-
-
-class pos_payment_report(report_sxw.rml_parse):
-
- def __init__(self, cr, uid, name, context):
- super(pos_payment_report, self).__init__(cr, uid, name, context=context)
- self.total = 0.0
- self.localcontext.update({
- 'time': time,
- 'pos_payment': self._pos_payment,
- 'pos_payment_total':self._pos_payment_total,
- })
-
- def _pos_payment(self, obj):
- self.total = 0
- data={}
- sql = """ select id from pos_order where id = %d"""%(obj.id)
- self.cr.execute(sql)
- if self.cr.fetchone():
- self.cr.execute ("select pt.name,pp.default_code as code,pol.qty,pu.name as uom,pol.discount,pol.price_unit, " \
- "(pol.price_unit * pol.qty * (1 - (pol.discount) / 100.0)) as total " \
- "from pos_order as po,pos_order_line as pol,product_product as pp,product_template as pt, product_uom as pu " \
- "where pt.id=pp.product_tmpl_id and pp.id=pol.product_id and po.id = pol.order_id and pu.id=pt.uom_id " \
- "and po.state IN ('paid','invoiced') and to_char(date_trunc('day',po.date_order),'YYYY-MM-DD')::date = current_date and po.id=%d"%(obj.id))
- data=self.cr.dictfetchall()
- else:
- self.cr.execute ("select pt.name,pp.default_code as code,pol.qty,pu.name as uom,pol.discount,pol.price_unit, " \
- "(pol.price_unit * pol.qty * (1 - (pol.discount) / 100.0)) as total " \
- "from pos_order as po,pos_order_line as pol,product_product as pp,product_template as pt, product_uom as pu " \
- "where pt.id=pp.product_tmpl_id and pp.id=pol.product_id and po.id = pol.order_id and pu.id=pt.uom_id " \
- "and po.state IN ('paid','invoiced') and to_char(date_trunc('day',po.date_order),'YYYY-MM-DD')::date = current_date")
- data=self.cr.dictfetchall()
-
- for d in data:
- self.total += d['price_unit'] * d['qty']
- return data
-
- def _pos_payment_total(self, o):
- return self.total
-
-
-class report_pos_payment(osv.AbstractModel):
- _name = 'report.point_of_sale.report_payment'
- _inherit = 'report.abstract_report'
- _template = 'point_of_sale.report_payment'
- _wrapped_report_class = pos_payment_report
diff --git a/addons/point_of_sale/report/pos_report.py b/addons/point_of_sale/report/pos_report.py
deleted file mode 100644
index 826e6134429..00000000000
--- a/addons/point_of_sale/report/pos_report.py
+++ /dev/null
@@ -1,121 +0,0 @@
-# -*- coding: utf-8 -*-
-# Part of Odoo. See LICENSE file for full copyright and licensing details.
-
-from openerp.osv import fields, osv
-from openerp import tools
-
-class report_transaction_pos(osv.osv):
- _name = "report.transaction.pos"
- _description = "transaction for the pos"
- _auto = False
- _columns = {
- 'date_create': fields.char('Date', size=16, readonly=True),
- 'journal_id': fields.many2one('account.journal', 'Sales Journal', readonly=True),
- 'jl_id': fields.many2one('account.journal', 'Cash Journals', readonly=True),
- 'user_id': fields.many2one('res.users', 'User', readonly=True),
- 'no_trans': fields.float('Number of Transaction', readonly=True),
- 'amount': fields.float('Amount', readonly=True),
- 'invoice_id': fields.float('Nbr Invoice', readonly=True),
- 'invoice_am': fields.float('Invoice Amount', readonly=True),
- 'product_nb': fields.float('Product Nb.', readonly=True),
- 'disc': fields.float('Disc.', readonly=True),
- }
-
- def init(self, cr):
- tools.drop_view_if_exists(cr, 'report_transaction_pos')
- cr.execute("""
- create or replace view report_transaction_pos as (
- select
- min(absl.id) as id,
- count(absl.id) as no_trans,
- sum(absl.amount) as amount,
- sum((100.0-line.discount) * line.price_unit * line.qty / 100.0) as disc,
- to_char(date_trunc('day',absl.create_date),'YYYY-MM-DD')::text as date_create,
- po.user_id as user_id,
- po.sale_journal as journal_id,
- abs.journal_id as jl_id,
- count(po.invoice_id) as invoice_id,
- count(p.id) as product_nb
- from
- account_bank_statement_line as absl,
- account_bank_statement as abs,
- product_product as p,
- pos_order_line as line,
- pos_order as po
- where
- absl.pos_statement_id = po.id and
- line.order_id=po.id and
- line.product_id=p.id and
- absl.statement_id=abs.id
-
- group by
- po.user_id,po.sale_journal, abs.journal_id,
- to_char(date_trunc('day',absl.create_date),'YYYY-MM-DD')::text
- )
- """)
- #to_char(date_trunc('day',absl.create_date),'YYYY-MM-DD')
- #to_char(date_trunc('day',absl.create_date),'YYYY-MM-DD')::text as date_create,
-
-class report_sales_by_user_pos(osv.osv):
- _name = "report.sales.by.user.pos"
- _description = "Sales by user"
- _auto = False
- _columns = {
- 'date_order': fields.date('Order Date',required=True, select=True),
- 'amount': fields.float('Total', readonly=True, select=True),
- 'qty': fields.float('Quantity', readonly=True, select=True),
- 'user_id': fields.many2one('res.users', 'User', readonly=True, select=True),
- }
-
- def init(self, cr):
- tools.drop_view_if_exists(cr, 'report_sales_by_user_pos')
- cr.execute("""
- create or replace view report_sales_by_user_pos as (
- select
- min(po.id) as id,
- to_char(date_trunc('day',po.date_order),'YYYY-MM-DD')::text as date_order,
- po.user_id as user_id,
- sum(pol.qty)as qty,
- sum((pol.price_unit * pol.qty * (1 - (pol.discount) / 100.0))) as amount
- from
- pos_order as po,pos_order_line as pol,product_product as pp,product_template as pt
- where
- pt.id=pp.product_tmpl_id and pp.id=pol.product_id and po.id = pol.order_id
- group by
- to_char(date_trunc('day',po.date_order),'YYYY-MM-DD')::text,
- po.user_id
-
- )
- """)
-
-class report_sales_by_user_pos_month(osv.osv):
- _name = "report.sales.by.user.pos.month"
- _description = "Sales by user monthly"
- _auto = False
- _columns = {
- 'date_order': fields.date('Order Date',required=True, select=True),
- 'amount': fields.float('Total', readonly=True, select=True),
- 'qty': fields.float('Quantity', readonly=True, select=True),
- 'user_id': fields.many2one('res.users', 'User', readonly=True, select=True),
- }
-
- def init(self, cr):
- tools.drop_view_if_exists(cr, 'report_sales_by_user_pos_month')
- cr.execute("""
- create or replace view report_sales_by_user_pos_month as (
- select
- min(po.id) as id,
- to_char(date_trunc('month',po.date_order),'YYYY-MM-DD')::text as date_order,
- po.user_id as user_id,
- sum(pol.qty)as qty,
- sum((pol.price_unit * pol.qty * (1 - (pol.discount) / 100.0))) as amount
- from
- pos_order as po,pos_order_line as pol,product_product as pp,product_template as pt
- where
- pt.id=pp.product_tmpl_id and pp.id=pol.product_id and po.id = pol.order_id
- group by
- to_char(date_trunc('month',po.date_order),'YYYY-MM-DD')::text,
- po.user_id
-
- )
- """)
diff --git a/addons/point_of_sale/security/ir.model.access.csv b/addons/point_of_sale/security/ir.model.access.csv
index 2e167277dbc..17b4f584f45 100644
--- a/addons/point_of_sale/security/ir.model.access.csv
+++ b/addons/point_of_sale/security/ir.model.access.csv
@@ -1,13 +1,10 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_pos_order,pos.order,model_pos_order,group_pos_user,1,1,1,1
access_pos_order_line,pos.order.line,model_pos_order_line,group_pos_user,1,1,1,1
-access_report_transaction_pos,report.transaction.pos,model_report_transaction_pos,group_pos_manager,1,1,1,1
access_stock_picking_pos_user,stock.picking pos_user,stock.model_stock_picking,group_pos_user,1,1,1,1
access_stock_warehouse_pos_user,stock.warehouse pos_user,stock.model_stock_warehouse,group_pos_user,1,0,0,0
access_pos_order_stock_worker,pos.order stock_worker,model_pos_order,stock.group_stock_user,1,0,0,0
access_stock_move_pos_user,stock.move pos_user,stock.model_stock_move,group_pos_user,1,1,1,1
-access_report_sales_by_user_pos,report.sales.by.user.pos,model_report_sales_by_user_pos,group_pos_user,1,0,0,0
-access_report_sales_by_user_pos_month,report.sales.by.user.pos.month,model_report_sales_by_user_pos_month,group_pos_user,1,0,0,0
access_report_pos_order,report.pos.order,model_report_pos_order,group_pos_user,1,1,1,1
access_account_journal_pos_user,account.journal pos_user,account.model_account_journal,group_pos_user,1,0,0,0
access_account_payment_method_pos_user,account.payment.method pos_user,account.model_account_payment_method,group_pos_manager,1,0,0,0
diff --git a/addons/point_of_sale/test/point_of_sale_report.yml b/addons/point_of_sale/test/point_of_sale_report.yml
deleted file mode 100644
index c039eab4c3d..00000000000
--- a/addons/point_of_sale/test/point_of_sale_report.yml
+++ /dev/null
@@ -1,43 +0,0 @@
--
- In order to test the PDF reports defined on a Point Of Sale, we will print a POS Invoice Report
--
- !python {model: pos.order}: |
- import os
- import openerp.report
- from openerp import tools
- data, format = openerp.report.render_report(cr, uid, [ref('pos_order_pos1')], 'point_of_sale.report_invoice', {}, {})
- if tools.config['test_report_directory']:
- file(os.path.join(tools.config['test_report_directory'], 'point_of_sale-invoice_report.'+format), 'wb+').write(data)
-
--
- In order to test the PDF reports defined on a Point Of Sale, we will print a POS Lines Report
--
- !python {model: pos.order}: |
- import os
- import openerp.report
- from openerp import tools
- data, format = openerp.report.render_report(cr, uid, [ref('pos_order_pos1')], 'point_of_sale.report_saleslines', {}, {})
- if tools.config['test_report_directory']:
- file(os.path.join(tools.config['test_report_directory'], 'point_of_sale-lines_report.'+format), 'wb+').write(data)
-
--
- Print the POS Details Report through the wizard
--
- !python {model: ir.ui.menu}: |
- import time
- ctx={}
- ctx.update({'model': 'ir.ui.menu','active_ids': []})
- data_dict = {'date_start': time.strftime('%Y-%m-%d'), 'date_end': time.strftime('%Y-%m-%d'), 'user_ids': [(6,0,[ref('base.user_root')])]}
- from openerp.tools import test_reports
- test_reports.try_report_action(cr, uid, 'action_report_pos_details',wiz_data=data_dict, context=ctx, our_module='point_of_sale')
-
--
- In order to test the PDF reports defined on a Point of Sale, we will print a POS Payment Report
--
- !python {model: pos.order}: |
- import os, time
- import openerp.report
- from openerp import tools
- data, format = openerp.report.render_report(cr, uid, [ref('pos_order_pos0')], 'point_of_sale.report_payment', {}, {})
- if tools.config['test_report_directory']:
- file(os.path.join(tools.config['test_report_directory'], 'point_of_sale-payment_report.'+format), 'wb+').write(data)
diff --git a/addons/point_of_sale/views/point_of_sale_report.xml b/addons/point_of_sale/views/point_of_sale_report.xml
index 9eee8f2c065..7d68e896f41 100644
--- a/addons/point_of_sale/views/point_of_sale_report.xml
+++ b/addons/point_of_sale/views/point_of_sale_report.xml
@@ -10,269 +10,15 @@
file="point_of_sale.report_userlabel"
/>
-
-
-
-
-
-
-
-
-
-
-
-
-
- report.trans.pos.user.form
- report.transaction.pos
-
-
Payment methods are defined by accounting journals having the
field PoS Payment Method checked. In order to be useable
from the touchscreen interface, you must set the payment method
on the Point of Sale configuration.
-
Categories are used to browse your products through the
touchscreen interface.
-
- If you put a photo on the category and enable the option in the
- point of sale configuration, the layout of the touchscreen interface
- will automatically be displayed. We suggest not to put a photo
- on categories for small (1024x768) screens.
-
-
-
+
+ If you put a photo on the category, the layout of the
+ touchscreen interface will automatically. We suggest not to put
+ a photo on categories for small (1024x768) screens.
+
+
+
-
-
-
-
\ No newline at end of file
+
+
diff --git a/addons/point_of_sale/views/pos_config_view.xml b/addons/point_of_sale/views/pos_config_view.xml
index 18b8d0e5c6b..830ab75f697 100644
--- a/addons/point_of_sale/views/pos_config_view.xml
+++ b/addons/point_of_sale/views/pos_config_view.xml
@@ -1,147 +1,139 @@
-
-
-
-
- pos.config.form.view
- pos.config
-
-
A session is a period of time, usually one day, during which
you sell through the point of sale. The user has to check the
currencies in your cash registers at the beginning and the end
of each session.
-
+
Note that you may use the menu Your Session
to quickly open a new session.
-
You must define a product for everything you sell through
the point of sale interface.
-
+
Do not forget to set the price and the point of sale category
in which it should appear. If a product has no point of sale
category, you can not sell it through the point of sale
interface.
-
-
-
\ No newline at end of file
+
diff --git a/addons/point_of_sale/views/res_config_view.xml b/addons/point_of_sale/views/res_config_view.xml
index 610d96c4d46..767d83ee34a 100644
--- a/addons/point_of_sale/views/res_config_view.xml
+++ b/addons/point_of_sale/views/res_config_view.xml
@@ -1,6 +1,5 @@
-
-
+pos.config.settings.formpos.config.settings
@@ -33,7 +32,6 @@
inline
-
-
-
+
diff --git a/addons/point_of_sale/views/res_partner_view.xml b/addons/point_of_sale/views/res_partner_view.xml
index 3320bc3a3cc..746aab53613 100644
--- a/addons/point_of_sale/views/res_partner_view.xml
+++ b/addons/point_of_sale/views/res_partner_view.xml
@@ -1,7 +1,5 @@
-
-
-
+res.partner.pos.form.inheritres.partner
@@ -14,6 +12,4 @@
-
-
-
+
diff --git a/addons/point_of_sale/views/res_users_view.xml b/addons/point_of_sale/views/res_users_view.xml
index 47d8c7863de..84df0988d24 100644
--- a/addons/point_of_sale/views/res_users_view.xml
+++ b/addons/point_of_sale/views/res_users_view.xml
@@ -1,6 +1,5 @@
-
-
+res.users.form.viewres.users
@@ -16,5 +15,4 @@
-
-
+
diff --git a/addons/point_of_sale/wizard/pos_box.py b/addons/point_of_sale/wizard/pos_box.py
index e013aea3167..653a3f5b3c8 100644
--- a/addons/point_of_sale/wizard/pos_box.py
+++ b/addons/point_of_sale/wizard/pos_box.py
@@ -1,62 +1,48 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
-from openerp.osv import osv
-from openerp.tools.translate import _
-from openerp.exceptions import UserError
+from odoo import api, _
+from odoo.exceptions import UserError
+
+from odoo.addons.account.wizard.pos_box import CashBox
-from openerp.addons.account.wizard.pos_box import CashBox
class PosBox(CashBox):
_register = False
- def run(self, cr, uid, ids, context=None):
- if not context:
- context = dict()
-
- active_model = context.get('active_model', False) or False
- active_ids = context.get('active_ids', []) or []
+ @api.multi
+ def run(self):
+ active_model = self.env.context.get('active_model', False)
+ active_ids = self.env.context.get('active_ids', [])
if active_model == 'pos.session':
- records = self.pool[active_model].browse(cr, uid, active_ids, context=context)
- bank_statements = [record.cash_register_id for record in records if record.cash_register_id]
-
+ bank_statements = [session.cash_register_id for session in self.env[active_model].browse(active_ids) if session.cash_register_id]
if not bank_statements:
raise UserError(_("There is no cash register for this PoS Session"))
-
- return self._run(cr, uid, ids, bank_statements, context=context)
+ return self._run(bank_statements)
else:
- return super(PosBox, self).run(cr, uid, ids, context=context)
+ return super(PosBox, self).run()
+
class PosBoxIn(PosBox):
_inherit = 'cash.box.in'
- def _compute_values_for_statement_line(self, cr, uid, box, record, context=None):
-
- if context is None:
- context = {}
-
- values = super(PosBoxIn, self)._compute_values_for_statement_line(cr, uid, box, record, context=context)
-
- active_model = context.get('active_model', False) or False
- active_ids = context.get('active_ids', []) or []
-
- if active_model == 'pos.session':
- session = self.pool[active_model].browse(cr, uid, active_ids, context=context)[0]
- values['ref'] = session.name
-
+ def _compute_values_for_statement_line(self, box, record):
+ values = super(PosBoxIn, self)._compute_values_for_statement_line(box=box, record=record)
+ active_model = self.env.context.get('active_model', False)
+ active_ids = self.env.context.get('active_ids', [])
+ if active_model == 'pos.session' and active_ids:
+ values['ref'] = self.env[active_model].browse(active_ids)[0].name
return values
class PosBoxOut(PosBox):
_inherit = 'cash.box.out'
- def _compute_values_for_statement_line(self, cr, uid, box, record, context=None):
- values = super(PosBoxOut, self)._compute_values_for_statement_line(cr, uid, box, record, context=context)
-
- active_model = context.get('active_model', False) or False
- active_ids = context.get('active_ids', []) or []
-
- if active_model == 'pos.session':
- session = self.pool[active_model].browse(cr, uid, active_ids, context=context)[0]
- values['ref'] = session.name
-
+ def _compute_values_for_statement_line(self, box, record):
+ values = super(PosBoxOut, self)._compute_values_for_statement_line(box=box, record=record)
+ active_model = self.env.context.get('active_model', False)
+ active_ids = self.env.context.get('active_ids', [])
+ if active_model == 'pos.session' and active_ids:
+ values['ref'] = self.env[active_model].browse(active_ids)[0].name
return values
diff --git a/addons/point_of_sale/wizard/pos_box.xml b/addons/point_of_sale/wizard/pos_box.xml
index 1a4f7fb411e..9be27e1cde6 100644
--- a/addons/point_of_sale/wizard/pos_box.xml
+++ b/addons/point_of_sale/wizard/pos_box.xml
@@ -1,22 +1,20 @@
-
-
+
+ key2="client_action_multi"/>
-
-
+ key2="client_action_multi"/>
+
diff --git a/addons/point_of_sale/wizard/pos_discount.py b/addons/point_of_sale/wizard/pos_discount.py
index 7be3692b9bb..d45f81055b8 100644
--- a/addons/point_of_sale/wizard/pos_discount.py
+++ b/addons/point_of_sale/wizard/pos_discount.py
@@ -1,37 +1,17 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
-from openerp.osv import osv, fields
+from odoo import api, fields, models
-class pos_discount(osv.osv_memory):
+class PosDiscount(models.TransientModel):
_name = 'pos.discount'
_description = 'Add a Global Discount'
- _columns = {
- 'discount': fields.float('Discount (%)', required=True, digits=(16,2)),
- }
- _defaults = {
- 'discount': 5,
- }
- def apply_discount(self, cr, uid, ids, context=None):
- """
- To give the discount of product and check the.
+ discount = fields.Float(string='Discount (%)', required=True, digits=(16, 2), default=5)
- @param self: The object pointer.
- @param cr: A database cursor
- @param uid: ID of the user currently logged in
- @param context: A standard dictionary
- @return : nothing
- """
- order_ref = self.pool.get('pos.order')
- order_line_ref = self.pool.get('pos.order.line')
- if context is None:
- context = {}
- this = self.browse(cr, uid, ids[0], context=context)
- record_id = context and context.get('active_id', False)
- if isinstance(record_id, (int, long)):
- record_id = [record_id]
- for order in order_ref.browse(cr, uid, record_id, context=context):
- order_line_ref.write(cr, uid, [x.id for x in order.lines], {'discount':this.discount}, context=context)
- return {}
+ @api.multi
+ def apply_discount(self):
+ self.ensure_one()
+ for order in self.env['pos.order'].browse(self.env.context.get('active_id', False)):
+ order.lines.write({'discount': self.discount})
diff --git a/addons/point_of_sale/wizard/pos_discount.xml b/addons/point_of_sale/wizard/pos_discount.xml
index c01be27fdcf..e060a7900ab 100644
--- a/addons/point_of_sale/wizard/pos_discount.xml
+++ b/addons/point_of_sale/wizard/pos_discount.xml
@@ -1,10 +1,7 @@
-
-
-
-
+
- Apply Discount
+ pos.discount.formpos.discount
@@ -27,6 +24,4 @@
formnew
-
-
-
+
diff --git a/addons/point_of_sale/wizard/pos_open_statement.py b/addons/point_of_sale/wizard/pos_open_statement.py
index 7737957465e..befa94b0324 100644
--- a/addons/point_of_sale/wizard/pos_open_statement.py
+++ b/addons/point_of_sale/wizard/pos_open_statement.py
@@ -1,58 +1,32 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
-from openerp.osv import osv
-from openerp.tools.translate import _
-from openerp.exceptions import UserError
+from odoo import api, models, _
+from odoo.exceptions import UserError
-class pos_open_statement(osv.osv_memory):
+
+class PosOpenStatement(models.TransientModel):
_name = 'pos.open.statement'
_description = 'Open Statements'
- def open_statement(self, cr, uid, ids, context=None):
- """
- Open the statements
- @param self: The object pointer.
- @param cr: A database cursor
- @param uid: ID of the user currently logged in
- @param context: A standard dictionary
- @return : Blank Directory
- """
- data = {}
- mod_obj = self.pool.get('ir.model.data')
- statement_obj = self.pool.get('account.bank.statement')
- sequence_obj = self.pool.get('ir.sequence')
- journal_obj = self.pool.get('account.journal')
- if context is None:
- context = {}
-
- st_ids = []
- j_ids = journal_obj.search(cr, uid, [('journal_user','=',1)], context=context)
- if not j_ids:
+ @api.multi
+ def open_statement(self):
+ self.ensure_one()
+ BankStatement = self.env['account.bank.statement']
+ journals = self.env['account.journal'].search([('journal_user', '=', True)])
+ if not journals:
raise UserError(_('You have to define which payment method must be available in the point of sale by reusing existing bank and cash through "Accounting / Configuration / Journals / Journals". Select a journal and check the field "PoS Payment Method" from the "Point of Sale" tab. You can also create new payment methods directly from menu "PoS Backend / Configuration / Payment Methods".'))
- for journal in journal_obj.browse(cr, uid, j_ids, context=context):
- ids = statement_obj.search(cr, uid, [('state', '!=', 'confirm'), ('user_id', '=', uid), ('journal_id', '=', journal.id)], context=context)
-
+ for journal in journals:
if journal.sequence_id:
- number = sequence_obj.next_by_id(cr, uid, journal.sequence_id.id, context=context)
+ number = journal.sequence_id.next_by_id()
else:
raise UserError(_("No sequence defined on the journal"))
+ BankStatement += BankStatement.create({'journal_id': journal.id, 'user_id': self.env.uid, 'name': number})
- data.update({
- 'journal_id': journal.id,
- 'user_id': uid,
- 'name': number
- })
- statement_id = statement_obj.create(cr, uid, data, context=context)
- st_ids.append(int(statement_id))
-
- tree_res = mod_obj.get_object_reference(cr, uid, 'account', 'view_bank_statement_tree')
- tree_id = tree_res and tree_res[1] or False
- form_res = mod_obj.get_object_reference(cr, uid, 'account', 'view_bank_statement_form')
- form_id = form_res and form_res[1] or False
- search_res = mod_obj.get_object_reference(cr, uid, 'account', 'view_bank_statement_search')
- search_id = search_res and search_res[1] or False
+ tree_id = self.env.ref('account.view_bank_statement_tree').id
+ form_id = self.env.ref('account.view_bank_statement_form').id
+ search_id = self.env.ref('account.view_bank_statement_search').id
return {
'type': 'ir.actions.act_window',
@@ -60,7 +34,7 @@ class pos_open_statement(osv.osv_memory):
'view_type': 'form',
'view_mode': 'tree,form',
'res_model': 'account.bank.statement',
- 'domain': str([('id', 'in', st_ids)]),
+ 'domain': str([('id', 'in', BankStatement.ids)]),
'views': [(tree_id, 'tree'), (form_id, 'form')],
'search_view_id': search_id,
}
diff --git a/addons/point_of_sale/wizard/pos_open_statement.xml b/addons/point_of_sale/wizard/pos_open_statement.xml
index cc10f44dce1..9ae3d8450a4 100644
--- a/addons/point_of_sale/wizard/pos_open_statement.xml
+++ b/addons/point_of_sale/wizard/pos_open_statement.xml
@@ -1,10 +1,7 @@
-
-
-
-
+
- Open Statements
+ pos.open.statement.formpos.open.statement
@@ -17,13 +14,14 @@
-
+ key2="client_action_multi"/>
Open Cash Register
@@ -33,6 +31,4 @@
formnew
-
-
-
+
diff --git a/addons/point_of_sale/wizard/pos_payment.py b/addons/point_of_sale/wizard/pos_payment.py
index ed53de42cdd..87fffb3da0a 100644
--- a/addons/point_of_sale/wizard/pos_payment.py
+++ b/addons/point_of_sale/wizard/pos_payment.py
@@ -1,41 +1,52 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
-import time
-
-from openerp.osv import osv, fields
-from openerp.tools.translate import _
+from odoo import api, fields, models, _
-class pos_make_payment(osv.osv_memory):
+class PosMakePayment(models.TransientModel):
_name = 'pos.make.payment'
_description = 'Point of Sale Payment'
- def check(self, cr, uid, ids, context=None):
+ def _default_journal(self):
+ active_id = self.env.context.get('active_id')
+ if active_id:
+ session = self.env['pos.order'].browse(active_id).session_id
+ return session.config_id.journal_ids and session.config_id.journal_ids.ids[0] or False
+ return False
+
+ def _default_amount(self):
+ active_id = self.env.context.get('active_id')
+ if active_id:
+ order = self.env['pos.order'].browse(active_id)
+ return (order.amount_total - order.amount_paid)
+ return False
+
+ journal_id = fields.Many2one('account.journal', string='Payment Mode', required=True, default=_default_journal)
+ amount = fields.Float(digits=(16, 2), required=True, default=_default_amount)
+ payment_name = fields.Char(string='Payment Reference')
+ payment_date = fields.Date(string='Payment Date', required=True, default=lambda *a: fields.Datetime.now())
+
+ @api.multi
+ def check(self):
"""Check the order:
if the order is not paid: continue payment,
if the order is paid print ticket.
"""
- context = context or {}
- order_obj = self.pool.get('pos.order')
- active_id = context and context.get('active_id', False)
-
- order = order_obj.browse(cr, uid, active_id, context=context)
+ self.ensure_one()
+ order = self.env['pos.order'].browse(self.env.context.get('active_id', False))
amount = order.amount_total - order.amount_paid
- data = self.read(cr, uid, ids, context=context)[0]
+ data = self.read()[0]
# this is probably a problem of osv_memory as it's not compatible with normal OSV's
data['journal'] = data['journal_id'][0]
-
if amount != 0.0:
- order_obj.add_payment(cr, uid, active_id, data, context=context)
+ order.add_payment(data)
+ if order.test_paid():
+ order.signal_workflow('paid')
+ return {'type': 'ir.actions.act_window_close'}
+ return self.launch_payment()
- if order_obj.test_paid(cr, uid, [active_id]):
- order_obj.signal_workflow(cr, uid, [active_id], 'paid')
- return {'type' : 'ir.actions.act_window_close' }
-
- return self.launch_payment(cr, uid, ids, context=context)
-
- def launch_payment(self, cr, uid, ids, context=None):
+ def launch_payment(self):
return {
'name': _('Payment'),
'view_type': 'form',
@@ -45,40 +56,6 @@ class pos_make_payment(osv.osv_memory):
'target': 'new',
'views': False,
'type': 'ir.actions.act_window',
- 'context': context,
+ 'context': self.env.context,
}
-
- def _default_journal(self, cr, uid, context=None):
- if not context:
- context = {}
- session = False
- order_obj = self.pool.get('pos.order')
- active_id = context and context.get('active_id', False)
- if active_id:
- order = order_obj.browse(cr, uid, active_id, context=context)
- session = order.session_id
- if session:
- for journal in session.config_id.journal_ids:
- return journal.id
- return False
-
- def _default_amount(self, cr, uid, context=None):
- order_obj = self.pool.get('pos.order')
- active_id = context and context.get('active_id', False)
- if active_id:
- order = order_obj.browse(cr, uid, active_id, context=context)
- return order.amount_total - order.amount_paid
- return False
-
- _columns = {
- 'journal_id' : fields.many2one('account.journal', 'Payment Mode', required=True),
- 'amount': fields.float('Amount', digits=(16,2), required= True),
- 'payment_name': fields.char('Payment Reference'),
- 'payment_date': fields.date('Payment Date', required=True),
- }
- _defaults = {
- 'journal_id' : _default_journal,
- 'payment_date': lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'),
- 'amount': _default_amount,
- }
\ No newline at end of file
diff --git a/addons/point_of_sale/wizard/pos_payment.xml b/addons/point_of_sale/wizard/pos_payment.xml
index 3b0ec0a679f..5ab532b821f 100644
--- a/addons/point_of_sale/wizard/pos_payment.xml
+++ b/addons/point_of_sale/wizard/pos_payment.xml
@@ -1,9 +1,7 @@
-
-
-
+
- Pay Order
+ pos.make.payment.formpos.make.payment
@@ -27,6 +25,4 @@
formnew
-
-
-
+
From 62040dea69c0b519775c459cdbabf3f10434af52 Mon Sep 17 00:00:00 2001
From: Darshan Kalola
Date: Mon, 5 Oct 2015 18:17:51 +0530
Subject: [PATCH 8/9] [REF] point_of_sale: converted yml test cases to python
unit test
---
addons/point_of_sale/__openerp__.py | 8 -
.../point_of_sale/test/00_register_open.yml | 11 -
.../test/01_order_to_payment.yml | 136 -----------
.../test/02_order_to_invoice.yml | 53 -----
.../test/account_statement_reports.yml | 68 ------
addons/point_of_sale/test/tests_before.xml | 13 --
addons/point_of_sale/tests/__init__.py | 4 +
addons/point_of_sale/tests/common.py | 60 +++++
.../tests/test_point_of_sale_flow.py | 217 ++++++++++++++++++
9 files changed, 281 insertions(+), 289 deletions(-)
delete mode 100644 addons/point_of_sale/test/00_register_open.yml
delete mode 100644 addons/point_of_sale/test/01_order_to_payment.yml
delete mode 100644 addons/point_of_sale/test/02_order_to_invoice.yml
delete mode 100644 addons/point_of_sale/test/account_statement_reports.yml
delete mode 100644 addons/point_of_sale/test/tests_before.xml
create mode 100644 addons/point_of_sale/tests/__init__.py
create mode 100644 addons/point_of_sale/tests/common.py
create mode 100644 addons/point_of_sale/tests/test_point_of_sale_flow.py
diff --git a/addons/point_of_sale/__openerp__.py b/addons/point_of_sale/__openerp__.py
index 072db0b5c27..a42c2df9106 100644
--- a/addons/point_of_sale/__openerp__.py
+++ b/addons/point_of_sale/__openerp__.py
@@ -65,14 +65,6 @@ Main Features
'demo': [
'data/point_of_sale_demo.xml',
],
- 'test': [
- '../account/test/account_minimal_test.xml',
- 'test/tests_before.xml',
- 'test/00_register_open.yml',
- 'test/01_order_to_payment.yml',
- 'test/02_order_to_invoice.yml',
- 'test/account_statement_reports.yml',
- ],
'installable': True,
'application': True,
'qweb': ['static/src/xml/pos.xml'],
diff --git a/addons/point_of_sale/test/00_register_open.yml b/addons/point_of_sale/test/00_register_open.yml
deleted file mode 100644
index ce13f3c0f26..00000000000
--- a/addons/point_of_sale/test/00_register_open.yml
+++ /dev/null
@@ -1,11 +0,0 @@
--
- In order to test the Point of Sale module, I will open all cash registers through the wizard
--
- I go to the window to open the cash registers as admin
--
- !record {model: pos.open.statement, id: new_statement_open}:
--
- I click on the button to open all statements/cash registers
--
- !python {model: pos.open.statement}: |
- self.open_statement(cr, uid, [ref('new_statement_open')], context={})
diff --git a/addons/point_of_sale/test/01_order_to_payment.yml b/addons/point_of_sale/test/01_order_to_payment.yml
deleted file mode 100644
index a5f41fec71b..00000000000
--- a/addons/point_of_sale/test/01_order_to_payment.yml
+++ /dev/null
@@ -1,136 +0,0 @@
-- |
- In order to test the Point of Sale in module, I will do a full flow from the sale to the payment and invoicing.
- I will use two products, one with price including a 10% tax, the other one with 5% tax excluded from the price.
-- |
- I create a VAT tax of 10%, included in the public price
--
- !record {model: account.tax, id: account_tax_10_incl}:
- name: VAT 10 perc Incl
- amount_type: percent
- amount: 10.0
- price_include: 1
--
- I assign this 10 percent tax on the [PCSC234] PC Assemble SC234 product as a sale tax
--
- !record {model: product.product, id: product.product_product_3, view: False}:
- taxes_id: [account_tax_10_incl]
-- |
- I create a VAT tax of 5%, which is added to the public price
--
- !record {model: account.tax, id: account_tax_05_incl}:
- name: VAT 05 perc Excl
- amount_type: percent
- amount: 5.0
- price_include: 0
--
- I will create a second VAT tax of 5% but this time for a child company, to
- ensure that only product taxes of the current session's company are considered
- (this tax should be ignore when computing order's taxes in following tests)
--
- !record {model: account.tax, id: account_tax_05_incl_chicago}:
- name: VAT 05 perc Excl (US)
- amount_type: percent
- amount: 5.0
- price_include: 0
- company_id: stock.res_company_1
--
- I assign those 5 percent taxes on the PCSC349 product as a sale taxes
--
- !record {model: product.product, id: product.product_product_4, view: False}:
- taxes_id: [account_tax_05_incl, account_tax_05_incl_chicago]
--
- I click on create a new session button
--
- !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)
--
- !record {model: pos.order, id: pos_order_pos0}:
- company_id: base.main_company
- lines:
- - name: OL/0001
- product_id: product.product_product_3
- price_unit: 450
- discount: 0.0
- qty: 2.0
- - name: OL/0002
- product_id: product.product_product_4
- price_unit: 300
- discount: 0.0
- qty: 3.0
--
- I check that the total of the order is equal to 450*2 + 300*3*1.05 and the tax of the order is equal to 900 -(450 * 2 / 1.1) + 300*0.05*3
--
- !python {model: pos.order}: |
- order = self.browse(cr, uid, ref('pos_order_pos0'))
- assert(abs(order.amount_total - (450*2 + 300*3*1.05)) < 0.01), "The order has a wrong amount, tax included"
- assert(abs(order.amount_tax - (900-(450*2/1.1) + 300*0.05*3)) < 0.01), "The order has a wrong tax amount"
--
- I want to add a global discount of 5 percent using the wizard
--
- !record {model: pos.discount, id: pos_discount_0}:
- discount: 5.0
--
- I click the apply button to set the discount on all lines
--
- !python {model: pos.discount}: |
- self.apply_discount(cr, uid, [ref("pos_discount_0")], {"active_model": "pos.order",
- "active_ids": [ref("pos_order_pos0")], "active_id": ref("pos_order_pos0"), })
--
- I check that the total of the order is now equal to (450*2 + 300*3*1.05)*0.95
--
- !python {model: pos.order}: |
- order = self.browse(cr, uid, ref('pos_order_pos0'))
- assert(abs(order.amount_total - (450*2 + 300*3*1.05)*0.95) < 0.01), "The order has a wrong total including tax and discounts"
--
- I click on the "Make Payment" wizard to pay the PoS order with a partial amount of 100.0 EUR
--
- !record {model: pos.make.payment, id: pos_make_payment_0, context: '{"active_id": ref("pos_order_pos0"), "active_ids": [ref("pos_order_pos0")]}' }:
- amount: 100.0
--
- I click on the validate button to register the payment.
--
- !python {model: pos.make.payment}: |
- self.check(cr, uid, [ref('pos_make_payment_0')], context={'active_id': ref('pos_order_pos0'), 'lang':'en_US', 'active_model': 'pos.order', 'tz': False, 'active_ids': [ref('pos_order_pos0')]} )
-- |
- I check that the order is not marked as paid yet
--
- !assert {model: pos.order, id: pos_order_pos0}:
- - state == 'draft'
-- |
- On the second payment proposition, I check that it proposes me the remaining balance which is 1790.0 EUR
--
- !python {model: pos.make.payment}: |
- defs = self.default_get(cr, uid, ['amount'], {'active_id': ref('pos_order_pos0')})
- assert abs(defs['amount'] - ((450*2 + 300*3*1.05)*0.95-100.0)) < 0.01, "The remaining balance is incorrect"
--
- I pay the remaining balance.
--
- !record {model: pos.make.payment, id: pos_make_payment_1, context: '{"active_id": ref("pos_order_pos0"), "active_ids": [ref("pos_order_pos0")]}' }:
- amount: !eval >
- (450*2 + 300*3*1.05)*0.95-100.0
--
- I click on the validate button to register the payment.
--
- !python {model: pos.make.payment}: |
- self.check(cr, uid, [ref('pos_make_payment_1')], context={'active_id': ref('pos_order_pos0')} )
-- |
- I check that the order is marked as paid
--
- !assert {model: pos.order, id: pos_order_pos0}:
- - state == 'paid'
--
- I generate the journal entries
--
- !python {model: pos.order}: |
- self._create_account_move_lines(cr, uid, [ref('pos_order_pos0')], {})
--
- I test that the generated journal entry is attached to the PoS order
--
- !assert {model: pos.order, id: pos_order_pos0}:
- - bool(account_move)
-
-
-
-
diff --git a/addons/point_of_sale/test/02_order_to_invoice.yml b/addons/point_of_sale/test/02_order_to_invoice.yml
deleted file mode 100644
index 90442a9a2e1..00000000000
--- a/addons/point_of_sale/test/02_order_to_invoice.yml
+++ /dev/null
@@ -1,53 +0,0 @@
--
- I create a new PoS order with 2 units of PC1 at 450 EUR (Tax Incl) and 3 units of PCSC349 at 300 EUR. (Tax Excl)
--
- !record {model: pos.order, id: pos_order_pos1}:
- company_id: base.main_company
- partner_id: base.res_partner_1
- lines:
- - name: OL/0001
- product_id: product.product_product_3
- price_unit: 450
- discount: 5.0
- qty: 2.0
- - name: OL/0002
- product_id: product.product_product_4
- price_unit: 300
- discount: 5.0
- qty: 3.0
--
- I click on the "Make Payment" wizard to pay the PoS order
--
- !record {model: pos.make.payment, id: pos_make_payment_2, context: '{"active_id": ref("pos_order_pos1"), "active_ids": [ref("pos_order_pos1")]}' }:
- amount: !eval >
- (450*2 + 300*3*1.05)*0.95
--
- I click on the validate button to register the payment.
--
- !python {model: pos.make.payment}: |
- self.check(cr, uid, [ref('pos_make_payment_2')], context={'active_id': ref('pos_order_pos1')} )
-- |
- I check that the order is marked as paid and there is no invoice attached to it
--
- !python {model: pos.order}: |
- order = self.browse(cr,uid,ref('pos_order_pos1'))
--
- !assert {model: pos.order, id: pos_order_pos1, string: State not correct}:
- - state == 'paid'
- - not invoice_id
--
- I set the order as invoiced
--
- !workflow {model: pos.order, action: invoice, ref: pos_order_pos1}
--
- I generate an invoice from the order
--
- !python {model: pos.order}: |
- invoice = self.action_invoice(cr, uid, [ref('pos_order_pos1')])
--
- I test that the total of the attached invoice is correct
--
- !python {model: pos.order}: |
- from openerp.tools import float_compare
- amount_total = self.browse(cr, uid, ref('pos_order_pos1')).amount_total
- assert float_compare(amount_total, 1752.75, precision_digits=2) == 0, "Invoice not correct"
diff --git a/addons/point_of_sale/test/account_statement_reports.yml b/addons/point_of_sale/test/account_statement_reports.yml
deleted file mode 100644
index b03cf7e3f29..00000000000
--- a/addons/point_of_sale/test/account_statement_reports.yml
+++ /dev/null
@@ -1,68 +0,0 @@
--
- In order to test the reports on Bank Statement defined in point_of_sale module, I create a bank statement line, confirm it and print the reports
--
- I select the period and journal for the bank statement
--
- !python {model: account.bank.statement}: |
- import time
- journal = self._default_journal(cr, uid, {'journal_type': 'bank'})
- assert journal, 'Journal has not been selected'
--
- I create a bank statement with Opening and Closing balance 0.
--
- !record {model: account.bank.statement, id: account_bank_statement_0}:
- balance_end_real: 0.0
- balance_start: 0.0
- date: !eval time.strftime('%Y-%m-%d')
- journal_id: bank_journal
--
- I create bank statement line
--
- !record {model: account.bank.statement.line, id: account_bank_statement_line_0}:
- amount: 1000.0
- partner_id: base.res_partner_4
- statement_id: account_bank_statement_0
- name: 'EXT001'
--
- I modify the bank statement and set the Closing Balance.
--
- !record {model: account.bank.statement, id: account_bank_statement_0}:
- balance_end_real: 1000.0
--
- I reconcile the bank statement.
--
- !python {model: account.bank.statement.line}: |
- new_aml_dicts = [{
- 'account_id': self.pool.get('res.partner').browse(cr, uid, ref('base.res_partner_4')).property_account_receivable_id.id,
- 'name': "EXT001",
- 'credit': 1000.0,
- 'debit': 0.0,
- }]
- self.process_reconciliations(cr, uid, [ref("account_bank_statement_line_0")], [{'new_aml_dicts': new_aml_dicts}])
--
- I confirm the bank statement using Confirm button
--
- !python {model: account.bank.statement}: |
- self.button_confirm_bank(cr, uid, [ref("account_bank_statement_0")])
--
- Print the account statement report
--
- !python {model: account.bank.statement}: |
- import os
- import openerp.report
- from openerp import tools
- data, format = openerp.report.render_report(cr, uid, [ref('account_bank_statement_0')], 'point_of_sale.report_statement', {}, {})
- if tools.config['test_report_directory']:
- file(os.path.join(tools.config['test_report_directory'], 'point_of_sale-statement.'+format), 'wb+').write(data)
-
--
- Printing the user s product report
--
- !python {model: account.bank.statement}: |
- import os
- import openerp.report
- from openerp import tools
- data, format = openerp.report.render_report(cr, uid, [ref('account_bank_statement_0')], 'point_of_sale.report_usersproduct', {}, {})
- if tools.config['test_report_directory']:
- file(os.path.join(tools.config['test_report_directory'], 'point_of_sale-usersproduct.'+format), 'wb+').write(data)
-
diff --git a/addons/point_of_sale/test/tests_before.xml b/addons/point_of_sale/test/tests_before.xml
deleted file mode 100644
index 24276f9cd1b..00000000000
--- a/addons/point_of_sale/test/tests_before.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/addons/point_of_sale/tests/__init__.py b/addons/point_of_sale/tests/__init__.py
new file mode 100644
index 00000000000..676a99e4a6a
--- /dev/null
+++ b/addons/point_of_sale/tests/__init__.py
@@ -0,0 +1,4 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from . import test_point_of_sale_flow
diff --git a/addons/point_of_sale/tests/common.py b/addons/point_of_sale/tests/common.py
new file mode 100644
index 00000000000..ebf971d2ec3
--- /dev/null
+++ b/addons/point_of_sale/tests/common.py
@@ -0,0 +1,60 @@
+# -*- coding: utf-8 -*-
+from odoo.tests import common
+
+
+class TestPointOfSaleCommon(common.TransactionCase):
+
+ def setUp(self):
+ super(TestPointOfSaleCommon, self).setUp()
+ self.AccountBankStatement = self.env['account.bank.statement']
+ self.AccountBankStatementLine = self.env['account.bank.statement.line']
+ self.PosMakePayment = self.env['pos.make.payment']
+ self.PosOrder = self.env['pos.order']
+ self.company_id = self.ref('base.main_company')
+ self.product3 = self.env.ref('product.product_product_3')
+ self.product4 = self.env.ref('product.product_product_4')
+ self.partner1 = self.env.ref('base.res_partner_1')
+ self.partner4 = self.env.ref('base.res_partner_4')
+ self.pos_config = self.env.ref('point_of_sale.pos_config_main')
+
+ # create a new session
+ self.pos_order_session0 = self.env['pos.session'].create({
+ 'user_id': 1,
+ 'config_id': self.pos_config.id
+ })
+
+ # create a VAT tax of 10%, included in the public price
+ Tax = self.env['account.tax']
+ account_tax_10_incl = Tax.create({
+ 'name': 'VAT 10 perc Incl',
+ 'amount_type': 'percent',
+ 'amount': 10.0,
+ 'price_include': 1
+ })
+
+ # assign this 10 percent tax on the [PCSC234] PC Assemble SC234 product
+ # as a sale tax
+ self.product3.taxes_id = [(6, 0, [account_tax_10_incl.id])]
+
+ # create a VAT tax of 5%, which is added to the public price
+ account_tax_05_incl = Tax.create({
+ 'name': 'VAT 5 perc Incl',
+ 'amount_type': 'percent',
+ 'amount': 5.0,
+ 'price_include': 0
+ })
+
+ # create a second VAT tax of 5% but this time for a child company, to
+ # ensure that only product taxes of the current session's company are considered
+ #(this tax should be ignore when computing order's taxes in following tests)
+ account_tax_05_incl_chicago = Tax.create({
+ 'name': 'VAT 05 perc Excl (US)',
+ 'amount_type': 'percent',
+ 'amount': 5.0,
+ 'price_include': 0,
+ 'company_id': self.ref('stock.res_company_1')
+ })
+
+ # I assign those 5 percent taxes on the PCSC349 product as a sale taxes
+ self.product4.write(
+ {'taxes_id': [(6, 0, [account_tax_05_incl.id, account_tax_05_incl_chicago.id])]})
diff --git a/addons/point_of_sale/tests/test_point_of_sale_flow.py b/addons/point_of_sale/tests/test_point_of_sale_flow.py
new file mode 100644
index 00000000000..023eff033a0
--- /dev/null
+++ b/addons/point_of_sale/tests/test_point_of_sale_flow.py
@@ -0,0 +1,217 @@
+# -*- coding: utf-8 -*-
+import datetime
+import time
+import os
+
+import odoo
+from odoo import tools, report as odoo_report
+from odoo.tools import float_compare, test_reports
+
+from odoo.addons.point_of_sale.tests.common import TestPointOfSaleCommon
+
+
+@odoo.tests.common.at_install(False)
+@odoo.tests.common.post_install(True)
+class TestPointOfSaleFlow(TestPointOfSaleCommon):
+
+ def test_register_open(self):
+ """
+ In order to test the Point of Sale module, I will open all cash registers through the wizard
+ """
+ # open all statements/cash registers
+ self.env['pos.open.statement'].create({}).open_statement()
+
+ def test_order_to_payment(self):
+ """
+ In order to test the Point of Sale in module, I will do a full flow from the sale to the payment and invoicing.
+ I will use two products, one with price including a 10% tax, the other one with 5% tax excluded from the price.
+ """
+ # I click on create a new session button
+ self.pos_config.open_session_cb()
+
+ # 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)
+ self.pos_order_pos0 = self.PosOrder.create({
+ 'company_id': self.company_id,
+ 'pricelist_id': self.partner1.property_product_pricelist.id,
+ 'partner_id': self.partner1.id,
+ 'lines': [(0, 0, {
+ 'name': "OL/0001",
+ 'product_id': self.product3.id,
+ 'price_unit': 450,
+ 'discount': 0.0,
+ 'qty': 2.0,
+ 'tax_ids': [(6, 0, self.product3.taxes_id.ids)],
+ }), (0, 0, {
+ 'name': "OL/0002",
+ 'product_id': self.product4.id,
+ 'price_unit': 300,
+ 'discount': 0.0,
+ 'qty': 3.0,
+ 'tax_ids': [(6, 0, self.product4.taxes_id.ids)],
+ })]
+ })
+
+ # I check that the total of the order is equal to 450*2 + 300*3*1.05
+ # and the tax of the order is equal to 900 -(450 * 2 / 1.1) +
+ # 300*0.05*3
+ self.assertLess(
+ abs(self.pos_order_pos0.amount_total - (450 * 2 + 300 * 3 * 1.05)),
+ 0.01, 'The order has a wrong amount, tax included.')
+
+ self.assertLess(
+ abs(self.pos_order_pos0.amount_tax - (900 - (450 * 2 / 1.1) + 300 * 0.05 * 3)),
+ 0.01, 'The order has a wrong tax amount.')
+
+ # I want to add a global discount of 5 percent using the wizard
+
+ self.pos_discount_0 = self.env['pos.discount'].create({'discount': 5.0})
+
+ context = {"active_model": "pos.order", "active_ids": [self.pos_order_pos0.id], "active_id": self.pos_order_pos0.id}
+
+ # I click the apply button to set the discount on all lines
+ self.pos_discount_0.with_context(context).apply_discount()
+
+ # I check that the total of the order is now equal to (450*2 +
+ # 300*3*1.05)*0.95
+ self.assertLess(
+ abs(self.pos_order_pos0.amount_total - (450 * 2 + 300 * 3 * 1.05) * 0.95),
+ 0.01, 'The order has a wrong total including tax and discounts')
+
+ # I click on the "Make Payment" wizard to pay the PoS order with a
+ # partial amount of 100.0 EUR
+ context_make_payment = {"active_ids": [self.pos_order_pos0.id], "active_id": self.pos_order_pos0.id}
+ self.pos_make_payment_0 = self.PosMakePayment.with_context(context_make_payment).create({
+ 'amount': 100.0
+ })
+
+ # I click on the validate button to register the payment.
+ context_payment = {'active_id': self.pos_order_pos0.id}
+ self.pos_make_payment_0.with_context(context_payment).check()
+
+ # I check that the order is not marked as paid yet
+ self.assertEqual(self.pos_order_pos0.state, 'draft', 'Order should be in draft state.')
+
+ # On the second payment proposition, I check that it proposes me the
+ # remaining balance which is 1790.0 EUR
+ defs = self.pos_make_payment_0.with_context({'active_id': self.pos_order_pos0.id}).default_get(['amount'])
+
+ self.assertLess(
+ abs(defs['amount'] - ((450 * 2 + 300 * 3 * 1.05) * 0.95 - 100.0)), 0.01, "The remaining balance is incorrect.")
+
+ #'I pay the remaining balance.
+ context_make_payment = {
+ "active_ids": [self.pos_order_pos0.id], "active_id": self.pos_order_pos0.id}
+
+ self.pos_make_payment_1 = self.PosMakePayment.with_context(context_make_payment).create({
+ 'amount': (450 * 2 + 300 * 3 * 1.05) * 0.95 - 100.0
+ })
+
+ # I click on the validate button to register the payment.
+ self.pos_make_payment_1.with_context(context_make_payment).check()
+
+ # I check that the order is marked as paid
+ self.assertEqual(self.pos_order_pos0.state, 'paid', 'Order should be in paid state.')
+
+ # I generate the journal entries
+ self.pos_order_pos0._create_account_move_line()
+
+ # I test that the generated journal entry is attached to the PoS order
+ self.assertTrue(self.pos_order_pos0.account_move, "Journal entry has not been attached to Pos order.")
+
+ def test_order_to_invoice(self):
+
+ #I create a new PoS order with 2 units of PC1 at 450 EUR (Tax Incl) and 3 units of PCSC349 at 300 EUR. (Tax Excl)
+ self.pos_order_pos1 = self.PosOrder.create({
+ 'company_id': self.company_id,
+ 'partner_id': self.partner1.id,
+ 'pricelist_id': self.partner1.property_product_pricelist.id,
+ 'lines': [(0, 0, {
+ 'name': "OL/0001",
+ 'product_id': self.product3.id,
+ 'price_unit': 450,
+ 'discount': 5.0,
+ 'qty': 2.0,
+ 'tax_ids': [(6, 0, self.product3.taxes_id.ids)],
+ }), (0, 0, {
+ 'name': "OL/0002",
+ 'product_id': self.product4.id,
+ 'price_unit': 300,
+ 'discount': 5.0,
+ 'qty': 3.0,
+ 'tax_ids': [(6, 0, self.product4.taxes_id.ids)],
+ })]
+ })
+
+ # I click on the "Make Payment" wizard to pay the PoS order
+ context_make_payment = {"active_ids": [self.pos_order_pos1.id], "active_id": self.pos_order_pos1.id}
+ self.pos_make_payment = self.PosMakePayment.with_context(context_make_payment).create({
+ 'amount': (450 * 2 + 300 * 3 * 1.05) * 0.95,
+ })
+ # I click on the validate button to register the payment.
+ context_payment = {'active_id': self.pos_order_pos1.id}
+ self.pos_make_payment.with_context(context_payment).check()
+
+ # I check that the order is marked as paid and there is no invoice
+ # attached to it
+ self.assertEqual(self.pos_order_pos1.state, 'paid', "Order should be in paid state.")
+ self.assertFalse(self.pos_order_pos1.invoice_id, 'Invoice should not be attached to order.')
+
+ # I set the order as invoiced
+ self.pos_order_pos1.signal_workflow('invoice')
+
+ # I generate an invoice from the order
+ self.invoice = self.pos_order_pos1.action_invoice()
+
+ # I test that the total of the attached invoice is correct
+ self.amount_total = self.pos_order_pos1.amount_total
+ self.assertEqual(
+ float_compare(self.amount_total, 1752.75, precision_digits=2), 0, "Invoice not correct")
+
+ """In order to test the reports on Bank Statement defined in point_of_sale module, I create a bank statement line, confirm it and print the reports"""
+
+ # I select the period and journal for the bank statement
+
+ context_journal = {'journal_type': 'bank'}
+ self.assertTrue(self.AccountBankStatement.with_context(
+ context_journal)._default_journal(), 'Journal has not been selected')
+ journal = self.env['account.journal'].create({
+ 'name': 'Bank Test',
+ 'code': 'BNKT',
+ 'type': 'bank',
+ 'company_id': self.company_id,
+ })
+ # I create a bank statement with Opening and Closing balance 0.
+ account_statement = self.AccountBankStatement.create({
+ 'balance_start': 0.0,
+ 'balance_end_real': 0.0,
+ 'date': time.strftime('%Y-%m-%d'),
+ 'journal_id': journal.id,
+ 'company_id': self.company_id,
+ })
+ # I create bank statement line
+ account_statement_line = self.AccountBankStatementLine.create({
+ 'amount': 1000,
+ 'partner_id': self.partner4.id,
+ 'statement_id': account_statement.id,
+ 'name': 'EXT001'
+ })
+ # I modify the bank statement and set the Closing Balance.
+ account_statement.write({
+ 'balance_end_real': 1000.0,
+ })
+
+ # I reconcile the bank statement.
+ new_aml_dicts = [{
+ 'account_id': self.partner4.property_account_receivable_id.id,
+ 'name': "EXT001",
+ 'credit': 1000.0,
+ 'debit': 0.0,
+ }]
+
+ self.AccountBankStatementLine._model.process_reconciliations(
+ self.env.cr, self.env.uid, [account_statement_line.id], [{'new_aml_dicts': new_aml_dicts}])
+
+ # I confirm the bank statement using Confirm button
+
+ self.AccountBankStatement.button_confirm_bank()
From 90d406b567fd1319ec0a5d7285aa52521b3c90ab Mon Sep 17 00:00:00 2001
From: Darshan Kalola
Date: Wed, 14 Oct 2015 17:04:16 +0530
Subject: [PATCH 9/9] [FIX] pos_mercury: adapt to new signature method
add_payment is no longer an api.model so self represent the current order
---
addons/pos_mercury/models/pos_mercury.py | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)
diff --git a/addons/pos_mercury/models/pos_mercury.py b/addons/pos_mercury/models/pos_mercury.py
index 0abf956d726..1a9e36e3cfc 100644
--- a/addons/pos_mercury/models/pos_mercury.py
+++ b/addons/pos_mercury/models/pos_mercury.py
@@ -68,11 +68,10 @@ class pos_order_card(models.Model):
return fields
- @api.model
- def add_payment(self, order_id, data):
- statement_id = super(pos_order_card, self).add_payment(order_id, data)
+ def add_payment(self, data):
+ statement_id = super(pos_order_card, self).add_payment(data)
statement_lines = self.env['account.bank.statement.line'].search([('statement_id', '=', statement_id),
- ('pos_statement_id', '=', order_id),
+ ('pos_statement_id', '=', self.id),
('journal_id', '=', data['journal']),
('amount', '=', data['amount'])])