[MERGE] forward port branch 9.0 up to 1246a470df

This commit is contained in:
Christophe Simonis
2016-12-16 15:55:13 +01:00
17 changed files with 124 additions and 58 deletions
+1 -1
View File
@@ -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
+9 -1
View File
@@ -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
@@ -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)
+1 -1
View File
@@ -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/>
+6 -7
View File
@@ -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
@@ -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"
@@ -15,6 +15,18 @@ msgstr ""
"Content-Transfer-Encoding: \n"
"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
@@ -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,
}
+14 -21
View File
@@ -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')
+34 -1
View File
@@ -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
+1 -1
View File
@@ -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);
}
},
+2 -1
View File
@@ -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);
@@ -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(){
@@ -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>
+7 -6
View File
@@ -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:
@@ -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);
+11
View File
@@ -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
+4 -3
View File
@@ -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