Please use the order name as communication reference.
-
''' % {
+
''') % {
'bank_title': bank_title,
'bank_accounts': bank_accounts,
}
diff --git a/addons/point_of_sale/models/pos_order.py b/addons/point_of_sale/models/pos_order.py
index d9891283fc9..77a3c91ec7c 100644
--- a/addons/point_of_sale/models/pos_order.py
+++ b/addons/point_of_sale/models/pos_order.py
@@ -52,36 +52,29 @@ class PosOrder(models.Model):
}
# This deals with orders that belong to a closed session. In order
- # to recover from this we:
- # - assign the order to another compatible open session
- # - if that doesn't exist, create a new one
+ # to recover from this situation we create a new rescue session,
+ # making it obvious that something went wrong.
+ # A new, separate, rescue session is preferred for every such recovery,
+ # to avoid adding unrelated orders to live sessions.
def _get_valid_session(self, order):
PosSession = self.env['pos.session']
closed_session = PosSession.browse(order['pos_session_id'])
- open_session = PosSession.search(
- [('state', '=', 'opened'),
- ('config_id', '=', closed_session.config_id.id),
- ('user_id', '=', closed_session.user_id.id)],
- limit=1, order="start_at DESC")
_logger.warning('session %s (ID: %s) was closed but received order %s (total: %s) belonging to it',
closed_session.name,
closed_session.id,
order['name'],
order['amount_total'])
+ _logger.warning('attempting to create recovery session for saving order %s', order['name'])
+ new_session = PosSession.create({
+ 'config_id': closed_session.config_id.id,
+ 'name': _('(RESCUE FOR %(session)s)') % {'session': closed_session.name},
+ 'rescue': True, # avoid conflict with live sessions
+ })
+ # bypass opening_control (necessary when using cash control)
+ new_session.signal_workflow('open')
- if open_session:
- _logger.warning('using session %s (ID: %s) for order %s instead',
- open_session.name,
- open_session.id,
- order['name'])
- return open_session
- else:
- _logger.warning('attempting to create new session for order %s', order['name'])
- new_session = PosSession.create({'config_id': closed_session.config_id.id})
- # bypass opening_control (necessary when using cash control)
- new_session.signal_workflow('open')
- return new_session
+ return new_session
def _match_payment_to_invoice(self, order):
account_precision = self.env['decimal.precision'].precision_get('Account')
@@ -415,7 +408,7 @@ class PosOrder(models.Model):
# set name based on the sequence specified on the config
session = self.env['pos.session'].browse(values['session_id'])
values['name'] = session.config_id.sequence_id._next()
- values.setdefault('session_id', session.config_id.pricelist_id.id)
+ values.setdefault('pricelist_id', session.config_id.pricelist_id.id)
else:
# fallback on any pos.order sequence
values['name'] = self.env['ir.sequence'].next_by_code('pos.order')
diff --git a/addons/point_of_sale/models/pos_session.py b/addons/point_of_sale/models/pos_session.py
index 01576578fed..966743da560 100644
--- a/addons/point_of_sale/models/pos_session.py
+++ b/addons/point_of_sale/models/pos_session.py
@@ -160,8 +160,12 @@ class PosSession(models.Model):
statements.append(ABS.with_context(ctx).sudo(uid).create(st_values).id)
+ unique_name = self.env['ir.sequence'].with_context(ctx).next_by_code('pos.session')
+ if values.get('name'):
+ unique_name += ' ' + values['name']
+
values.update({
- 'name': self.env['ir.sequence'].with_context(ctx).next_by_code('pos.session'),
+ 'name': unique_name,
'statement_ids': [(6, 0, statements)],
'config_id': config_id
})
@@ -236,3 +240,32 @@ class PosSession(models.Model):
'target': 'self',
'url': '/pos/web/',
}
+
+ @api.multi
+ def open_cashbox(self):
+ self.ensure_one()
+ context = dict(self._context)
+ balance_type = context.get('balance') or 'start'
+ context['bank_statement_id'] = self.cash_register_id.id
+ context['balance'] = balance_type
+
+ action = {
+ 'name': _('Cash Control'),
+ 'view_type': 'form',
+ 'view_mode': 'form',
+ 'res_model': 'account.bank.statement.cashbox',
+ 'view_id': self.env.ref('account.view_account_bnk_stmt_cashbox').id,
+ 'type': 'ir.actions.act_window',
+ 'context': context,
+ 'target': 'new'
+ }
+
+ cashbox_id = None
+ if balance_type == 'start':
+ cashbox_id = self.cash_register_id.cashbox_start_id.id
+ else:
+ cashbox_id = self.cash_register_id.cashbox_end_id.id
+ if cashbox_id:
+ action['res_id'] = cashbox_id
+
+ return action
diff --git a/addons/point_of_sale/static/src/js/gui.js b/addons/point_of_sale/static/src/js/gui.js
index 365a27b1cc6..0fca3607f81 100644
--- a/addons/point_of_sale/static/src/js/gui.js
+++ b/addons/point_of_sale/static/src/js/gui.js
@@ -114,7 +114,7 @@ var Gui = core.Class.extend({
this.current_screen.hide();
}
this.current_screen = screen;
- this.current_screen.show();
+ this.current_screen.show(refresh);
}
},
diff --git a/addons/point_of_sale/static/src/js/models.js b/addons/point_of_sale/static/src/js/models.js
index 904cfcb9c42..d0cb2adba9f 100644
--- a/addons/point_of_sale/static/src/js/models.js
+++ b/addons/point_of_sale/static/src/js/models.js
@@ -1074,7 +1074,8 @@ exports.Orderline = Backbone.Model.extend({
init_from_JSON: function(json) {
this.product = this.pos.db.get_product_by_id(json.product_id);
if (!this.product) {
- console.error('ERROR: attempting to recover product not available in the point of sale');
+ console.error('ERROR: attempting to recover product ID', json.product_id,
+ 'not available in the point of sale. Correct the product or clean the browser cache.');
}
this.price = json.price_unit;
this.set_discount(json.discount);
diff --git a/addons/point_of_sale/static/src/js/screens.js b/addons/point_of_sale/static/src/js/screens.js
index 35d57a020d9..5bfa8f1e309 100644
--- a/addons/point_of_sale/static/src/js/screens.js
+++ b/addons/point_of_sale/static/src/js/screens.js
@@ -272,8 +272,9 @@ var ScaleScreenWidget = ScreenWidget.extend({
});
this.$('.next,.buy-product').click(function(){
- self.order_product();
self.gui.show_screen(self.next_screen);
+ // add product *after* switching screen to scroll properly
+ self.order_product();
});
queue.schedule(function(){
@@ -937,10 +938,12 @@ var ProductScreenWidget = ScreenWidget.extend({
}
},
- show: function(){
+ show: function(reset){
this._super();
- this.product_categories_widget.reset_category();
- this.numpad.state.reset();
+ if (reset) {
+ this.product_categories_widget.reset_category();
+ this.numpad.state.reset();
+ }
},
close: function(){
diff --git a/addons/point_of_sale/views/pos_session_view.xml b/addons/point_of_sale/views/pos_session_view.xml
index 4cf44bbf1f3..d6fb4cc6953 100644
--- a/addons/point_of_sale/views/pos_session_view.xml
+++ b/addons/point_of_sale/views/pos_session_view.xml
@@ -48,18 +48,18 @@
Money Out
-
-
diff --git a/addons/report/models/report.py b/addons/report/models/report.py
index 52a1a1f4a38..f3508872202 100644
--- a/addons/report/models/report.py
+++ b/addons/report/models/report.py
@@ -200,7 +200,7 @@ class Report(osv.Model):
# Get the ir.actions.report.xml record we are working on.
report = self._get_report_from_name(cr, uid, report_name)
# Check if we have to save the report or if we have to get one from the db.
- save_in_attachment = self._check_attachment_use(cr, uid, ids, report)
+ save_in_attachment = self._check_attachment_use(cr, uid, ids, report, context=context)
# Get the paperformat associated to the report, otherwise fallback on the company one.
if not report.paperformat_id:
user = self.pool['res.users'].browse(cr, uid, uid)
@@ -273,7 +273,8 @@ class Report(osv.Model):
return self._run_wkhtmltopdf(
cr, uid, headerhtml, footerhtml, contenthtml, context.get('landscape'),
paperformat, specific_paperformat_args, save_in_attachment,
- context.get('set_viewport_size')
+ context.get('set_viewport_size'),
+ context
)
@api.v8
@@ -319,7 +320,7 @@ class Report(osv.Model):
# Report generation helpers
#--------------------------------------------------------------------------
@api.v7
- def _check_attachment_use(self, cr, uid, ids, report):
+ def _check_attachment_use(self, cr, uid, ids, report, context=None):
""" Check attachment_use field. If set to true and an existing pdf is already saved, load
this one now. Else, mark save it.
"""
@@ -328,7 +329,7 @@ class Report(osv.Model):
save_in_attachment['loaded_documents'] = {}
if report.attachment:
- records = self.pool[report.model].browse(cr, uid, ids)
+ records = self.pool[report.model].browse(cr, uid, ids, context=context)
filenames = self._attachment_filename(cr, uid, records, report)
attachments = None
if report.attachment_use:
@@ -380,7 +381,7 @@ class Report(osv.Model):
def _check_wkhtmltopdf(self):
return wkhtmltopdf_state
- def _run_wkhtmltopdf(self, cr, uid, headers, footers, bodies, landscape, paperformat, spec_paperformat_args=None, save_in_attachment=None, set_viewport_size=False):
+ def _run_wkhtmltopdf(self, cr, uid, headers, footers, bodies, landscape, paperformat, spec_paperformat_args=None, save_in_attachment=None, set_viewport_size=False, context=None):
"""Execute wkhtmltopdf as a subprocess in order to convert html given in input into a pdf
document.
@@ -483,7 +484,7 @@ class Report(osv.Model):
'res_id': reporthtml[0],
}
try:
- self.pool['ir.attachment'].create(cr, uid, attachment)
+ self.pool['ir.attachment'].create(cr, uid, attachment, context)
except AccessError:
_logger.info("Cannot save PDF report %r as attachment", attachment['name'])
else:
diff --git a/addons/web/static/src/js/views/form_widgets.js b/addons/web/static/src/js/views/form_widgets.js
index 3b4628322f7..616e3e1aa07 100644
--- a/addons/web/static/src/js/views/form_widgets.js
+++ b/addons/web/static/src/js/views/form_widgets.js
@@ -1014,6 +1014,7 @@ var FieldReference = common.AbstractField.extend(common.ReinitializeFieldMixin,
this.m2o = new FieldMany2One(this.fm, { attrs: {
name: 'Referenced Document',
modifiers: JSON.stringify({readonly: this.get('effective_readonly')}),
+ context: this.build_context().eval(),
}});
this.m2o.on("change:value", this, this.data_changed);
this.m2o.appendTo(this.$el);
diff --git a/doc/cla/individual/hhgabelgaard.md b/doc/cla/individual/hhgabelgaard.md
new file mode 100644
index 00000000000..e873ef3f5e4
--- /dev/null
+++ b/doc/cla/individual/hhgabelgaard.md
@@ -0,0 +1,11 @@
+Denmark, 2016-05-06
+
+I hereby agree to the terms of the Odoo Individual Contributor License
+Agreement v1.0.
+
+I declare that I am authorized and able to make this agreement and sign this
+declaration.
+
+Signed,
+
+Hans Henrik Gabelgaard hhg@gabelgaard.org https://github.com/hhgabelgaard
\ No newline at end of file
diff --git a/openerp/models.py b/openerp/models.py
index e73e1827217..2aac55edefe 100644
--- a/openerp/models.py
+++ b/openerp/models.py
@@ -845,10 +845,11 @@ class BaseModel(object):
methods = []
for attr, func in getmembers(cls, is_constraint):
for name in func._constrains:
- if name not in cls._fields:
+ field = cls._fields.get(name)
+ if not field:
_logger.warning("method %s.%s: @constrains parameter %r is not a field name", cls._name, attr, name)
- if not cls._fields[name].store:
- _logger.warning("method %s.%s: @constrains parameter %r is not stored", cls._name, attr, name)
+ if not (field.store or field.column and field.column._fnct_inv):
+ _logger.warning("method %s.%s: @constrains parameter %r is not writeable", cls._name, attr, name)
methods.append(func)
# optimization: memoize result on cls, it will not be recomputed