[MERGE] forward port branch saas-15 up to 788684a5dd
This commit is contained in:
@@ -1266,6 +1266,20 @@ class AccountInvoice(models.Model):
|
||||
return result
|
||||
|
||||
@api.model
|
||||
def _refund_tax_lines_account_change(self, lines, taxes_to_change):
|
||||
# Let's change the account on tax lines when
|
||||
# @param {list} lines: a list of orm commands
|
||||
# @param {dict} taxes_to_change
|
||||
# key: tax ID, value: refund account
|
||||
|
||||
if not taxes_to_change:
|
||||
return lines
|
||||
|
||||
for line in lines:
|
||||
if isinstance(line[2], dict) and line[2]['tax_id'] in taxes_to_change:
|
||||
line[2]['account_id'] = taxes_to_change[line[2]['tax_id']]
|
||||
return lines
|
||||
|
||||
def _get_refund_common_fields(self):
|
||||
return ['partner_id', 'payment_term_id', 'account_id', 'currency_id', 'journal_id']
|
||||
|
||||
@@ -1311,7 +1325,12 @@ class AccountInvoice(models.Model):
|
||||
values['invoice_line_ids'] = self._refund_cleanup_lines(invoice.invoice_line_ids)
|
||||
|
||||
tax_lines = invoice.tax_line_ids
|
||||
values['tax_line_ids'] = self._refund_cleanup_lines(tax_lines)
|
||||
taxes_to_change = {
|
||||
line.tax_id.id: line.tax_id.refund_account_id.id
|
||||
for line in tax_lines.filtered(lambda l: l.tax_id.refund_account_id != l.tax_id.account_id)
|
||||
}
|
||||
cleaned_tax_lines = self._refund_cleanup_lines(tax_lines)
|
||||
values['tax_line_ids'] = self._refund_tax_lines_account_change(cleaned_tax_lines, taxes_to_change)
|
||||
|
||||
if journal_id:
|
||||
journal = self.env['account.journal'].browse(journal_id)
|
||||
|
||||
@@ -164,3 +164,59 @@ class TestAccountCustomerInvoice(AccountTestUsers):
|
||||
))
|
||||
|
||||
self.assertEquals(invoice.amount_untaxed, sum([x.base for x in invoice.tax_line_ids]))
|
||||
|
||||
def test_customer_invoice_tax_refund(self):
|
||||
company = self.env.user.company_id
|
||||
tax_account = self.env['account.account'].create({
|
||||
'name': 'TAX',
|
||||
'code': 'TAX',
|
||||
'user_type_id': self.env.ref('account.data_account_type_current_assets').id,
|
||||
'company_id': company.id,
|
||||
})
|
||||
|
||||
tax_refund_account = self.env['account.account'].create({
|
||||
'name': 'TAX_REFUND',
|
||||
'code': 'TAX_R',
|
||||
'user_type_id': self.env.ref('account.data_account_type_current_assets').id,
|
||||
'company_id': company.id,
|
||||
})
|
||||
|
||||
journalrec = self.env['account.journal'].search([('type', '=', 'sale')])[0]
|
||||
partner3 = self.env.ref('base.res_partner_3')
|
||||
account_id = self.env['account.account'].search([('user_type_id', '=', self.env.ref('account.data_account_type_revenue').id)], limit=1).id
|
||||
|
||||
tax = self.env['account.tax'].create({
|
||||
'name': 'Tax 15.0',
|
||||
'amount': 15.0,
|
||||
'amount_type': 'percent',
|
||||
'type_tax_use': 'sale',
|
||||
'account_id': tax_account.id,
|
||||
'refund_account_id': tax_refund_account.id
|
||||
})
|
||||
|
||||
invoice_line_data = [
|
||||
(0, 0,
|
||||
{
|
||||
'product_id': self.env.ref('product.product_product_1').id,
|
||||
'quantity': 40.0,
|
||||
'account_id': account_id,
|
||||
'name': 'product test 1',
|
||||
'discount': 10.00,
|
||||
'price_unit': 2.27,
|
||||
'invoice_line_tax_ids': [(6, 0, [tax.id])],
|
||||
}
|
||||
)]
|
||||
|
||||
invoice = self.env['account.invoice'].create(dict(
|
||||
name="Test Customer Invoice",
|
||||
reference_type="none",
|
||||
journal_id=journalrec.id,
|
||||
partner_id=partner3.id,
|
||||
invoice_line_ids=invoice_line_data
|
||||
))
|
||||
|
||||
invoice.action_invoice_open()
|
||||
|
||||
refund = invoice.refund()
|
||||
self.assertEqual(invoice.tax_line_ids.mapped('account_id'), tax_account)
|
||||
self.assertEqual(refund.tax_line_ids.mapped('account_id'), tax_refund_account)
|
||||
|
||||
@@ -892,12 +892,20 @@ exports.PosModel = Backbone.Model.extend({
|
||||
transfer.pipe(function(order_server_id){
|
||||
|
||||
// generate the pdf and download it
|
||||
self.chrome.do_action('point_of_sale.pos_invoice_report',{additional_context:{
|
||||
active_ids:order_server_id,
|
||||
}}).done(function () {
|
||||
invoiced.resolve();
|
||||
done.resolve();
|
||||
});
|
||||
if (order_server_id.length) {
|
||||
self.chrome.do_action('point_of_sale.pos_invoice_report',{additional_context:{
|
||||
active_ids:order_server_id,
|
||||
}}).done(function () {
|
||||
invoiced.resolve();
|
||||
done.resolve();
|
||||
});
|
||||
} else {
|
||||
// The order has been pushed separately in batch when
|
||||
// the connection came back.
|
||||
// The user has to go to the backend to print the invoice
|
||||
invoiced.reject({code:401, message:'Backend Invoice', data:{order: order}});
|
||||
done.reject();
|
||||
}
|
||||
});
|
||||
|
||||
return done;
|
||||
|
||||
@@ -2078,6 +2078,17 @@ var PaymentScreenWidget = ScreenWidget.extend({
|
||||
self.gui.show_screen('clientlist');
|
||||
},
|
||||
});
|
||||
} else if (error.message === 'Backend Invoice') {
|
||||
self.gui.show_popup('confirm',{
|
||||
'title': _t('Please print the invoice from the backend'),
|
||||
'body': _t('The order has been synchronized earlier. Please make the invoice from the backend for the order: ') + error.data.order.name,
|
||||
confirm: function () {
|
||||
this.gui.show_screen('receipt');
|
||||
},
|
||||
cancel: function () {
|
||||
this.gui.show_screen('receipt');
|
||||
},
|
||||
});
|
||||
} else if (error.code < 0) { // XmlHttpRequest Errors
|
||||
self.gui.show_popup('error',{
|
||||
'title': _t('The order could not be sent'),
|
||||
|
||||
@@ -22,6 +22,7 @@ var map_title ={
|
||||
var CrashManager = core.Class.extend({
|
||||
init: function() {
|
||||
this.active = true;
|
||||
this.isConnected = true;
|
||||
},
|
||||
enable: function () {
|
||||
this.active = true;
|
||||
@@ -29,8 +30,29 @@ var CrashManager = core.Class.extend({
|
||||
disable: function () {
|
||||
this.active = false;
|
||||
},
|
||||
rpc_error: function(error) {
|
||||
handleLostConnection: function () {
|
||||
var self = this;
|
||||
if (!this.isConnected) {
|
||||
// already handled, nothing to do. This can happen when several
|
||||
// rpcs are done in parallel and fail because of a lost connection.
|
||||
return;
|
||||
}
|
||||
this.isConnected = false;
|
||||
var delay = 2000;
|
||||
core.bus.trigger('connection_lost');
|
||||
|
||||
setTimeout(function checkConnection() {
|
||||
ajax.jsonRpc('/web/webclient/version_info', 'call', {}, {shadow:true}).then(function () {
|
||||
core.bus.trigger('connection_restored');
|
||||
self.isConnected = true;
|
||||
}).fail(function () {
|
||||
// exponential backoff, with some jitter
|
||||
delay = (delay * 1.5) + 500*Math.random();
|
||||
setTimeout(checkConnection, delay);
|
||||
});
|
||||
}, delay);
|
||||
},
|
||||
rpc_error: function(error) {
|
||||
if (!this.active) {
|
||||
return;
|
||||
}
|
||||
@@ -38,16 +60,7 @@ var CrashManager = core.Class.extend({
|
||||
return;
|
||||
}
|
||||
if (error.code === -32098) {
|
||||
core.bus.trigger('connection_lost');
|
||||
this.connection_lost = true;
|
||||
var timeinterval = setInterval(function() {
|
||||
var options = {shadow: true};
|
||||
ajax.jsonRpc('/web/webclient/version_info', 'call', {}, options).then(function () {
|
||||
clearInterval(timeinterval);
|
||||
core.bus.trigger('connection_restored');
|
||||
self.connection_lost = false;
|
||||
});
|
||||
}, 2000);
|
||||
this.handleLostConnection();
|
||||
return;
|
||||
}
|
||||
var handler = core.crash_registry.get(error.data.name, true);
|
||||
|
||||
@@ -155,7 +155,6 @@
|
||||
|
||||
position: relative;
|
||||
height: @odoo-navbar-height;
|
||||
overflow: hidden;
|
||||
|
||||
> ul {
|
||||
> li {
|
||||
@@ -171,6 +170,7 @@
|
||||
|
||||
&.o_menu_sections {
|
||||
width: 100%;
|
||||
display: none;
|
||||
|
||||
> li.open .dropdown-menu {
|
||||
position: static;
|
||||
@@ -219,6 +219,10 @@
|
||||
.o_main_navbar {
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
|
||||
.o_menu_sections {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -182,7 +182,7 @@ class WebsiteSlides(http.Controller):
|
||||
@http.route('''/slides/slide/<model("slide.slide"):slide>/download''', type='http', auth="public", website=True, sitemap=False)
|
||||
def slide_download(self, slide, **kw):
|
||||
slide = slide.sudo()
|
||||
if slide.download_security == 'public' or (slide.download_security == 'user' and request.session.uid):
|
||||
if slide.download_security == 'public' or (slide.download_security == 'user' and request.env.user and request.env.user != request.website.user_id):
|
||||
filecontent = base64.b64decode(slide.datas)
|
||||
disposition = 'attachment; filename=%s.pdf' % werkzeug.urls.url_quote(slide.name)
|
||||
return request.make_response(
|
||||
|
||||
Reference in New Issue
Block a user