diff options
| author | Christophe Simonis <chs@odoo.com> | 2016-12-16 15:55:13 +0100 |
|---|---|---|
| committer | Christophe Simonis <chs@odoo.com> | 2016-12-16 15:55:13 +0100 |
| commit | 2a6e077f4a9ab2b361ba81ea0bc9d7a5badd2e3d (patch) | |
| tree | 54383a1726119d64a7a2097ca1d007b14f910237 /addons | |
| parent | 68171347967dcaf2fa299bcd7f27915e03b80cc8 (diff) | |
| parent | 1246a470df5b8dee2845ebe8639076d2a715c341 (diff) | |
[MERGE] forward port branch 9.0 up to 1246a470df5
Diffstat (limited to 'addons')
| -rw-r--r-- | addons/account/models/account.py | 2 | ||||
| -rw-r--r-- | addons/account/models/account_payment.py | 10 | ||||
| -rw-r--r-- | addons/account_analytic_default/models/account_analytic_default.py | 9 | ||||
| -rw-r--r-- | addons/event/views/event_views.xml | 2 | ||||
| -rw-r--r-- | addons/mail/wizard/mail_compose_message.py | 13 | ||||
| -rw-r--r-- | addons/payment_transfer/i18n/payment_transfer.pot | 16 | ||||
| -rw-r--r-- | addons/payment_transfer/models/payment_acquirer.py | 4 | ||||
| -rw-r--r-- | addons/point_of_sale/models/pos_order.py | 35 | ||||
| -rw-r--r-- | addons/point_of_sale/models/pos_session.py | 35 | ||||
| -rw-r--r-- | addons/point_of_sale/static/src/js/gui.js | 2 | ||||
| -rw-r--r-- | addons/point_of_sale/static/src/js/models.js | 3 | ||||
| -rw-r--r-- | addons/point_of_sale/static/src/js/screens.js | 11 | ||||
| -rw-r--r-- | addons/point_of_sale/views/pos_session_view.xml | 8 | ||||
| -rw-r--r-- | addons/report/models/report.py | 13 | ||||
| -rw-r--r-- | addons/web/static/src/js/views/form_widgets.js | 1 |
15 files changed, 109 insertions, 55 deletions
diff --git a/addons/account/models/account.py b/addons/account/models/account.py index 09923efec07..48ef95bc276 100644 --- a/addons/account/models/account.py +++ b/addons/account/models/account.py @@ -149,7 +149,7 @@ class AccountAccount(models.Model): @api.one def copy(self, default=None): default = dict(default or {}) - default.update(code=_("%s (copy)") % (self.code or '')) + default.setdefault('code', _("%s (copy)") % (self.code or '')) return super(AccountAccount, self).copy(default) @api.multi diff --git a/addons/account/models/account_payment.py b/addons/account/models/account_payment.py index 1bf47b0f41d..4c7ba8eaeea 100644 --- a/addons/account/models/account_payment.py +++ b/addons/account/models/account_payment.py @@ -384,7 +384,15 @@ class account_payment(models.Model): #Reconcile with the invoices if self.payment_difference_handling == 'reconcile' and self.payment_difference: writeoff_line = self._get_shared_move_line_vals(0, 0, 0, move.id, False) - debit_wo, credit_wo, amount_currency_wo, currency_id = aml_obj.with_context(date=self.payment_date).compute_amount_fields(self.payment_difference, self.currency_id, self.company_id.currency_id, invoice_currency) + amount_currency_wo, currency_id = aml_obj.with_context(date=self.payment_date).compute_amount_fields(self.payment_difference, self.currency_id, self.company_id.currency_id, invoice_currency)[2:] + # the writeoff debit and credit must be computed from the invoice residual in company currency + # minus the payment amount in company currency, and not from the payment difference in the payment currency + # to avoid loss of precision during the currency rate computations. See revision 20935462a0cabeb45480ce70114ff2f4e91eaf79 for a detailed example. + total_residual_company_signed = sum(invoice.residual_company_signed for invoice in self.invoice_ids) + total_payment_company_signed = self.currency_id.with_context(date=self.payment_date).compute(self.amount, self.company_id.currency_id) + amount_wo = total_residual_company_signed - total_payment_company_signed + debit_wo = amount_wo > 0 and amount_wo or 0.0 + credit_wo = amount_wo < 0 and -amount_wo or 0.0 writeoff_line['name'] = _('Counterpart') writeoff_line['account_id'] = self.writeoff_account_id.id writeoff_line['debit'] = debit_wo diff --git a/addons/account_analytic_default/models/account_analytic_default.py b/addons/account_analytic_default/models/account_analytic_default.py index 85ec2199f0f..9ef2add29bc 100644 --- a/addons/account_analytic_default/models/account_analytic_default.py +++ b/addons/account_analytic_default/models/account_analytic_default.py @@ -65,7 +65,10 @@ class AccountInvoiceLine(models.Model): return res def _set_additional_fields(self, invoice): - rec = self.env['account.analytic.default'].account_get(self.product_id.id, self.invoice_id.partner_id.id, self.env.uid, - fields.Date.today(), company_id=self.company_id.id) - self.account_analytic_id = rec.analytic_id.id + if not self.account_analytic_id: + rec = self.env['account.analytic.default'].account_get( + self.product_id.id, self.invoice_id.partner_id.id, self.env.uid, + fields.Date.today(), company_id=self.company_id.id) + if rec: + self.account_analytic_id = rec.analytic_id.id super(AccountInvoiceLine, self)._set_additional_fields(invoice) diff --git a/addons/event/views/event_views.xml b/addons/event/views/event_views.xml index 886bec31019..12bd833ec65 100644 --- a/addons/event/views/event_views.xml +++ b/addons/event/views/event_views.xml @@ -244,7 +244,7 @@ <filter string="Unread Messages" name="message_needaction" domain="[('message_needaction','=',True)]"/> <separator/> <filter string="Unconfirmed" name="draft" domain="[('state','=','draft')]" help="Events in New state"/> - <filter string="Confirmed" domain="[('state','=','confirm')]" help="Confirmed events"/> + <filter name="confirm" string="Confirmed" domain="[('state','=','confirm')]" help="Confirmed events"/> <separator/> <filter string="My Events" help="My Events" domain="[('user_id','=',uid)]"/> <separator/> diff --git a/addons/mail/wizard/mail_compose_message.py b/addons/mail/wizard/mail_compose_message.py index 176ea88aec1..f55854a704e 100644 --- a/addons/mail/wizard/mail_compose_message.py +++ b/addons/mail/wizard/mail_compose_message.py @@ -12,18 +12,17 @@ from openerp.tools.safe_eval import safe_eval as eval EXPRESSION_PATTERN = re.compile('(\$\{.+?\})') -def _reopen(self, res_id, model): +def _reopen(self, res_id, model, context=None): + # save original model in context, because selecting the list of available + # templates requires a model in context + context = dict(context or {}, default_model=model) return {'type': 'ir.actions.act_window', 'view_mode': 'form', 'view_type': 'form', 'res_id': res_id, 'res_model': self._name, 'target': 'new', - # save original model in context, because selecting the list of available - # templates requires a model in context - 'context': { - 'default_model': model, - }, + 'context': context, } @@ -409,7 +408,7 @@ class MailComposer(models.TransientModel): # generate the saved template record.write({'template_id': template.id}) record.onchange_template_id_wrapper() - return _reopen(self, record.id, record.model) + return _reopen(self, record.id, record.model, context=self._context) #------------------------------------------------------ # Template rendering diff --git a/addons/payment_transfer/i18n/payment_transfer.pot b/addons/payment_transfer/i18n/payment_transfer.pot index 8d148467a56..13e0d0166f8 100644 --- a/addons/payment_transfer/i18n/payment_transfer.pot +++ b/addons/payment_transfer/i18n/payment_transfer.pot @@ -6,8 +6,8 @@ msgid "" msgstr "" "Project-Id-Version: Odoo Server 9.saas~10c\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2016-08-18 14:07+0000\n" -"PO-Revision-Date: 2016-08-18 14:07+0000\n" +"POT-Creation-Date: 2016-12-14 14:52+0000\n" +"PO-Revision-Date: 2016-12-14 14:52+0000\n" "Last-Translator: <>\n" "Language-Team: \n" "MIME-Version: 1.0\n" @@ -16,6 +16,18 @@ msgstr "" "Plural-Forms: \n" #. module: payment_transfer +#: code:addons/payment_transfer/models/payment_acquirer.py:32 +#, python-format +msgid "<div>\n" +"<h3>Please use the following transfer details</h3>\n" +"<h4>%(bank_title)s</h4>\n" +"%(bank_accounts)s\n" +"<h4>Communication</h4>\n" +"<p>Please use the order name as communication reference.</p>\n" +"</div>" +msgstr "" + +#. module: payment_transfer #: code:addons/payment_transfer/models/payment_acquirer.py:68 #, python-format msgid "; multiple order found" diff --git a/addons/payment_transfer/models/payment_acquirer.py b/addons/payment_transfer/models/payment_acquirer.py index a8d47098c6c..8b6ba44f202 100644 --- a/addons/payment_transfer/models/payment_acquirer.py +++ b/addons/payment_transfer/models/payment_acquirer.py @@ -29,13 +29,13 @@ class TransferPaymentAcquirer(osv.Model): accounts = self.pool['account.journal'].browse(cr, uid, journal_ids, context=context).mapped('bank_account_id').name_get() bank_title = _('Bank Accounts') if len(accounts) > 1 else _('Bank Account') bank_accounts = ''.join(['<ul>'] + ['<li>%s</li>' % name for id, name in accounts] + ['</ul>']) - post_msg = '''<div> + post_msg = _('''<div> <h3>Please use the following transfer details</h3> <h4>%(bank_title)s</h4> %(bank_accounts)s <h4>Communication</h4> <p>Please use the order name as communication reference.</p> -</div>''' % { +</div>''') % { '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 @@ <span class="o_stat_text">Money Out</span> </div> </button> - <button name="%(account.action_view_account_bnk_stmt_cashbox)d" + <button name="open_cashbox" class="oe_stat_button" attrs="{'invisible':['|', ('cash_control', '=', False), ('state', '!=', 'opening_control')]}" icon="fa-money" - type="action" context="{'balance': 'start', 'bank_statement_id': cash_register_id}"> + type="object" context="{'balance': 'start'}"> <span class="o_stat_text">Set Opening Balance</span> </button> - <button name="%(account.action_view_account_bnk_stmt_cashbox)d" + <button name="open_cashbox" class="oe_stat_button" attrs="{'invisible':['|', '|', ('cash_control', '=', False), ('state', '=', 'opening_control'), ('state', '=', 'closed')]}" icon="fa-money" - type="action" context="{'balance': 'end', 'bank_statement_id': cash_register_id}"> + type="object" context="{'balance': 'end'}"> <span class="o_stat_text">Set Closing Balance</span> </button> </div> 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); |
