[MERGE] forward port branch 11.0 up to ee0ff262f0
This commit is contained in:
@@ -107,7 +107,7 @@ class AccountInvoice(models.Model):
|
||||
def _compute_residual(self):
|
||||
residual = 0.0
|
||||
residual_company_signed = 0.0
|
||||
sign = self.type in ['in_refund', 'out_refund'] and -1 or 1
|
||||
sign = self.type in ['in_invoice', 'out_refund'] and -1 or 1
|
||||
for line in self.sudo().move_id.line_ids:
|
||||
if line.account_id == self.account_id:
|
||||
residual_company_signed += line.amount_residual
|
||||
|
||||
@@ -198,7 +198,7 @@ class account_abstract_payment(models.AbstractModel):
|
||||
total = 0.0
|
||||
groups = groupby(invoices, lambda i: i.currency_id)
|
||||
for payment_currency, payment_invoices in groups:
|
||||
amount_total = sum([MAP_INVOICE_TYPE_PAYMENT_SIGN[i.type] * i.residual_signed for i in payment_invoices])
|
||||
amount_total = sum(i.residual_signed for i in payment_invoices)
|
||||
if payment_currency == currency:
|
||||
total += amount_total
|
||||
else:
|
||||
|
||||
@@ -464,6 +464,32 @@ class TestPayment(AccountingTestCase):
|
||||
#check the invoice status
|
||||
self.assertEqual(invoice.state, 'paid')
|
||||
|
||||
def test_payment_and_writeoff_out_refund(self):
|
||||
# Use case:
|
||||
# Company is in EUR, create a credit note for 100 EUR and register payment of 90.
|
||||
# Mark invoice as fully paid with a write_off
|
||||
# Check that all the aml are correctly created.
|
||||
invoice = self.create_invoice(amount=100, type='out_refund', currency_id=self.currency_eur_id, partner=self.partner_agrolait.id)
|
||||
# register payment on invoice
|
||||
payment = self.payment_model.create({'payment_type': 'outbound',
|
||||
'payment_method_id': self.env.ref('account.account_payment_method_manual_in').id,
|
||||
'partner_type': 'customer',
|
||||
'partner_id': self.partner_agrolait.id,
|
||||
'amount': 90,
|
||||
'payment_date': time.strftime('%Y') + '-07-15',
|
||||
'payment_difference_handling': 'reconcile',
|
||||
'writeoff_account_id': self.account_payable.id,
|
||||
'journal_id': self.bank_journal_euro.id,
|
||||
'invoice_ids': [(4, invoice.id, None)]
|
||||
})
|
||||
payment.post()
|
||||
self.check_journal_items(payment.move_line_ids, [
|
||||
{'account_id': self.account_eur.id, 'debit': 0.0, 'credit': 90.0, 'amount_currency': 0.0, 'currency_id': False},
|
||||
{'account_id': self.account_payable.id, 'debit': 0.0, 'credit': 10.0, 'amount_currency': 0.0, 'currency_id': False},
|
||||
{'account_id': self.account_receivable.id, 'debit': 100.0, 'credit': 0.0, 'amount_currency': 0.0, 'currency_id': False},
|
||||
])
|
||||
self.assertEqual(invoice.state, 'paid')
|
||||
|
||||
def test_payment_and_writeoff_in_other_currency_3(self):
|
||||
# Use case related in revision 20935462a0cabeb45480ce70114ff2f4e91eaf79
|
||||
# Invoice made in secondary currency for which the rate to the company currency
|
||||
|
||||
@@ -81,7 +81,7 @@ class HrAttendance(models.Model):
|
||||
('employee_id', '=', attendance.employee_id.id),
|
||||
('check_out', '=', False),
|
||||
('id', '!=', attendance.id),
|
||||
])
|
||||
], order='check_in desc', limit=1)
|
||||
if no_check_out_attendances:
|
||||
raise exceptions.ValidationError(_("Cannot create new attendance record for %(empl_name)s, the employee hasn't checked out since %(datetime)s") % {
|
||||
'empl_name': attendance.employee_id.name,
|
||||
|
||||
@@ -486,10 +486,13 @@ var BasicComposer = Widget.extend({
|
||||
},
|
||||
|
||||
send_message: function () {
|
||||
var $button = this.$('.o_composer_button_send');
|
||||
|
||||
if (this.is_empty() || !this.do_check_attachment_upload()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$button.prop('disabled', true);
|
||||
clearTimeout(this.canned_timeout);
|
||||
var self = this;
|
||||
this.preprocess_message().then(function (message) {
|
||||
|
||||
@@ -197,6 +197,7 @@
|
||||
</span>
|
||||
</p>
|
||||
<div class="o_thread_message_content">
|
||||
<t t-raw="message.body"/>
|
||||
<t t-if="message.tracking_value_ids and message.tracking_value_ids.length > 0">
|
||||
<t t-if="message.subtype_description">
|
||||
<p><t t-esc="message.subtype_description"/></p>
|
||||
@@ -204,9 +205,6 @@
|
||||
<t t-call="mail.ChatThread.MessageTracking"/>
|
||||
</t>
|
||||
<p t-if="options.display_subject and message.display_subject" class="o_mail_subject">Subject: <t t-esc="message.subject"/></p>
|
||||
<t t-if="!(message.tracking_value_ids and message.tracking_value_ids.length > 0)">
|
||||
<t t-raw="message.body"/>
|
||||
</t>
|
||||
<t t-if="message.attachment_ids.length > 0">
|
||||
<div t-if="message.images.length > 0" class="o_attachments_previews">
|
||||
<t t-foreach="message.images" t-as="attachment">
|
||||
|
||||
@@ -613,6 +613,62 @@ QUnit.test('chatter: post, receive and star messages', function (assert) {
|
||||
});
|
||||
});
|
||||
|
||||
QUnit.test('chatter: post a message disable the send button', function(assert) {
|
||||
assert.expect(3);
|
||||
var bus = new Bus();
|
||||
var form = createView({
|
||||
View: FormView,
|
||||
model: 'partner',
|
||||
data: this.data,
|
||||
arch: '<form string="Partners">' +
|
||||
'<sheet>' +
|
||||
'<field name="foo"/>' +
|
||||
'</sheet>' +
|
||||
'<div class="oe_chatter">' +
|
||||
'<field name="message_ids" widget="mail_thread"/>' +
|
||||
'</div>' +
|
||||
'</form>',
|
||||
res_id: 2,
|
||||
session: {},
|
||||
mockRPC: function (route, args) {
|
||||
if (args.method === 'message_get_suggested_recipients') {
|
||||
return $.when({2: []});
|
||||
}
|
||||
return this._super(route, args);
|
||||
},
|
||||
intercepts: {
|
||||
get_messages: function (ev) {
|
||||
ev.stopPropagation();
|
||||
ev.data.callback($.when([]));
|
||||
},
|
||||
post_message: function (ev) {
|
||||
ev.stopPropagation();
|
||||
assert.ok(form.$('.o_composer_button_send').prop("disabled"),
|
||||
"Send button should be disabled when a message is being sent");
|
||||
bus.trigger('new_message', {
|
||||
id: 0,
|
||||
model: ev.data.options.model,
|
||||
res_id: ev.data.options.res_id,
|
||||
});
|
||||
},
|
||||
get_bus: function (ev) {
|
||||
ev.stopPropagation();
|
||||
ev.data.callback(bus);
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
form.$('.o_chatter_button_new_message').click();
|
||||
assert.notOk(form.$('.o_composer_button_send').prop('disabled'),
|
||||
"Send button should be enabled when posting a message");
|
||||
form.$('.oe_chatter .o_composer_text_field:first()').val("My first message");
|
||||
form.$('.oe_chatter .o_composer_button_send').click();
|
||||
form.$('.o_chatter_button_new_message').click();
|
||||
assert.notOk(form.$('.o_composer_button_send').prop('disabled'),
|
||||
"Send button should be enabled when posting another message");
|
||||
form.destroy();
|
||||
});
|
||||
|
||||
QUnit.test('chatter: post a message and switch in edit mode', function (assert) {
|
||||
assert.expect(5);
|
||||
|
||||
|
||||
@@ -2152,6 +2152,19 @@ td {
|
||||
overflow: auto;
|
||||
height: 120px;
|
||||
}
|
||||
.pos .modal-dialog .popup-textinput{
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.pos .modal-dialog .popup-textinput .title,
|
||||
.pos .modal-dialog .popup-textinput .footer{
|
||||
flex: none;
|
||||
}
|
||||
.pos .modal-dialog .popup-textinput textarea{
|
||||
resize: none;
|
||||
flex: auto;
|
||||
margin: 0 20px 80px;
|
||||
}
|
||||
.pos .popup .title {
|
||||
background: rgba(255,255,255,0.5);
|
||||
margin: 0;
|
||||
|
||||
@@ -742,6 +742,9 @@ var Chrome = PosBaseWidget.extend(AbstractAction.prototype, {
|
||||
if(err.message === 'XmlHttpRequestError '){
|
||||
title = 'Network Failure (XmlHttpRequestError)';
|
||||
body = 'The Point of Sale could not be loaded due to a network problem.\n Please check your internet connection.';
|
||||
}else if(err.message === 'TLSError'){
|
||||
title = 'Https connection to IoT Box failed';
|
||||
body = 'Make sure you are using IoT Box v18.10 or higher.\n\n Navigate to ' + err.url + ' to accept the certificate of your IoT Box.';
|
||||
}else if(err.code === 200){
|
||||
title = err.data.message;
|
||||
body = err.data.debug;
|
||||
|
||||
@@ -192,11 +192,15 @@ var ProxyDevice = core.Class.extend(mixins.PropertiesMixin,{
|
||||
// try harder when we remember a good proxy url
|
||||
found_url = this.try_hard_to_connect(localStorage.hw_proxy_url, options)
|
||||
.then(null,function(){
|
||||
return self.find_proxy(options);
|
||||
if (window.location.protocol != 'https:'){
|
||||
return self.find_proxy(options);
|
||||
}
|
||||
});
|
||||
}else{
|
||||
// just find something quick
|
||||
found_url = this.find_proxy(options);
|
||||
if (window.location.protocol != 'https:'){
|
||||
found_url = this.find_proxy(options);
|
||||
}
|
||||
}
|
||||
|
||||
success = found_url.then(function(url){
|
||||
@@ -248,16 +252,17 @@ var ProxyDevice = core.Class.extend(mixins.PropertiesMixin,{
|
||||
// try several time to connect to a known proxy url
|
||||
try_hard_to_connect: function(url,options){
|
||||
options = options || {};
|
||||
var port = ':' + (options.port || '8069');
|
||||
var protocol = window.location.protocol;
|
||||
var port = ( !options.port && protocol == "https:") ? ':443' : ':' + (options.port || '8069');
|
||||
|
||||
this.set_connection_status('connecting');
|
||||
|
||||
if(url.indexOf('//') < 0){
|
||||
url = 'http://'+url;
|
||||
url = protocol + '//' + url;
|
||||
}
|
||||
|
||||
if(url.indexOf(':',5) < 0){
|
||||
url = url+port;
|
||||
url = url + port;
|
||||
}
|
||||
|
||||
// try real hard to connect to url, with a 1sec timeout and up to 'retries' retries
|
||||
@@ -273,11 +278,11 @@ var ProxyDevice = core.Class.extend(mixins.PropertiesMixin,{
|
||||
.done(function(){
|
||||
done.resolve(url);
|
||||
})
|
||||
.fail(function(){
|
||||
.fail(function(resp){
|
||||
if(retries > 0){
|
||||
try_real_hard_to_connect(url,retries-1,done);
|
||||
}else{
|
||||
done.reject();
|
||||
done.reject(resp.statusText, url);
|
||||
}
|
||||
});
|
||||
return done;
|
||||
|
||||
@@ -127,13 +127,21 @@ exports.PosModel = Backbone.Model.extend({
|
||||
progress: function(prog){
|
||||
self.chrome.loading_progress(prog);
|
||||
},
|
||||
}).then(function(){
|
||||
if(self.config.iface_scan_via_proxy){
|
||||
self.barcode_reader.connect_to_proxy();
|
||||
}
|
||||
}).always(function(){
|
||||
done.resolve();
|
||||
});
|
||||
}).then(
|
||||
function(){
|
||||
if(self.config.iface_scan_via_proxy){
|
||||
self.barcode_reader.connect_to_proxy();
|
||||
}
|
||||
done.resolve();
|
||||
},
|
||||
function(statusText, url){
|
||||
if (statusText == 'error' && window.location.protocol == 'https:') {
|
||||
var error = {message: 'TLSError', url: url};
|
||||
self.chrome.loading_error(error);
|
||||
} else {
|
||||
done.resolve();
|
||||
}
|
||||
});
|
||||
return done;
|
||||
},
|
||||
|
||||
|
||||
@@ -1117,7 +1117,7 @@
|
||||
<div class="modal-dialog">
|
||||
<div class="popup popup-textinput">
|
||||
<p class="title"><t t-esc=" widget.options.title || '' " /></p>
|
||||
<textarea rows="10" cols="40"><t t-esc="widget.options.value" /></textarea>
|
||||
<textarea><t t-esc="widget.options.value" /></textarea>
|
||||
<div class="footer">
|
||||
<div class="button confirm">
|
||||
Ok
|
||||
|
||||
@@ -208,7 +208,7 @@ class BootstrapTranslator(nodes.NodeVisitor, object):
|
||||
else:
|
||||
opts = {}
|
||||
|
||||
def warner(msg):
|
||||
def warner(msg, **kw):
|
||||
self.builder.warn(msg, (self.builder.current_docname, node.line))
|
||||
highlighted = self.builder.highlighter.highlight_block(
|
||||
node.rawsource, lang, opts=opts, warn=warner, linenos=linenos,
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
India, 17, Sept - 2018
|
||||
|
||||
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,
|
||||
|
||||
Anil Kesariya anil.r.kesariya@gmail.com https://github.com/AnilKesariya
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
Mexico, 2018-10-24
|
||||
|
||||
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,
|
||||
|
||||
Luis Guzman ark@switnet.org https://github.com/ark74
|
||||
@@ -693,11 +693,10 @@ class Module(models.Model):
|
||||
mod.write(updated_values)
|
||||
else:
|
||||
mod_path = modules.get_module_path(mod_name)
|
||||
if not mod_path:
|
||||
if not mod_path or not terp:
|
||||
continue
|
||||
if not terp or not terp.get('installable', True):
|
||||
continue
|
||||
mod = self.create(dict(name=mod_name, state='uninstalled', **values))
|
||||
state = "uninstalled" if terp.get('installable', True) else "uninstallable"
|
||||
mod = self.create(dict(name=mod_name, state=state, **values))
|
||||
res[1] += 1
|
||||
|
||||
mod._update_dependencies(terp.get('depends', []))
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
'website': "http://www.yourcompany.com",
|
||||
|
||||
# Categories can be used to filter modules in modules listing
|
||||
# Check https://github.com/odoo/odoo/blob/master/odoo/addons/base/module/module_data.xml
|
||||
# Check https://github.com/odoo/odoo/blob/saas-11.3/odoo/addons/base/data/ir_module_category_data.xml
|
||||
# for the full list
|
||||
'category': 'Uncategorized',
|
||||
'version': '0.1',
|
||||
|
||||
@@ -56,8 +56,8 @@ class MigrationManager(object):
|
||||
function. Theses files must respect a directory tree structure: A 'migrations' folder
|
||||
which containt a folder by version. Version can be 'module' version or 'server.module'
|
||||
version (in this case, the files will only be processed by this version of the server).
|
||||
Python file names must start by `pre` or `post` and will be executed, respectively,
|
||||
before and after the module initialisation. `end` scripts are run after all modules have
|
||||
Python file names must start by `pre-` or `post-` and will be executed, respectively,
|
||||
before and after the module initialisation. `end-` scripts are run after all modules have
|
||||
been updated.
|
||||
Example:
|
||||
<moduledir>
|
||||
|
||||
Reference in New Issue
Block a user