[MERGE] forward port branch saas-11 up to c19f546

This commit is contained in:
Christophe Simonis
2016-11-08 14:43:28 +01:00
41 changed files with 239 additions and 97 deletions
@@ -352,7 +352,7 @@ class AccountBankStatement(models.Model):
class AccountBankStatementLine(models.Model):
_name = "account.bank.statement.line"
_description = "Bank Statement Line"
_order = "statement_id desc, sequence"
_order = "statement_id desc, sequence, id desc"
_inherit = ['ir.needaction_mixin']
name = fields.Char(string='Memo', required=True)
@@ -956,8 +956,23 @@ class AccountBankStatementLine(models.Model):
aml_dict['currency_id'] = statement_currency.id
# Create write-offs
# When we register a payment on an invoice, the write-off line contains the amount
# currency if all related invoices have the same currency. We apply the same logic in
# the manual reconciliation.
counterpart_aml = self.env['account.move.line']
for aml_dict in counterpart_aml_dicts:
counterpart_aml |= aml_dict.get('move_line', self.env['account.move.line'])
new_aml_currency = False
if counterpart_aml\
and len(counterpart_aml.mapped('currency_id')) == 1\
and counterpart_aml[0].currency_id\
and counterpart_aml[0].currency_id != company_currency:
new_aml_currency = counterpart_aml[0].currency_id
for aml_dict in new_aml_dicts:
aml_dict['payment_id'] = payment_id
if new_aml_currency and not aml_dict.get('currency_id'):
aml_dict['currency_id'] = new_aml_currency.id
aml_dict['amount_currency'] = company_currency.with_context(ctx).compute(aml_dict['debit'] - aml_dict['credit'], new_aml_currency)
aml_obj.with_context(check_move_validity=False).create(aml_dict)
# Create counterpart move lines and reconcile them
+3 -1
View File
@@ -927,6 +927,8 @@ class AccountMoveLine(models.Model):
first_line_dict['account_id'] = self[0].account_id.id
if 'analytic_account_id' in first_line_dict:
del first_line_dict['analytic_account_id']
if 'tax_ids' in first_line_dict:
del first_line_dict['tax_ids']
# Writeoff line in specified writeoff account
second_line_dict = vals.copy()
@@ -1253,7 +1255,7 @@ class AccountMoveLine(models.Model):
""" Prepare the values used to create() an account.analytic.line upon validation of an account.move.line having
an analytic account. This method is intended to be extended in other modules.
"""
amount = (self.credit or 0.0) - (self.debit or 0.0)
amount = (self.debit or 0.0) - (self.credit or 0.0)
return {
'name': self.name,
'date': self.date,
+1 -1
View File
@@ -242,7 +242,7 @@ class TestReconciliation(AccountingTestCase):
self.check_results(bank_stmt.move_line_ids, {
self.account_euro.id: {'debit': 40.0, 'credit': 0.0, 'amount_currency': 0, 'currency_id': False},
self.account_rcv.id: {'debit': 0.0, 'credit': 32.7, 'amount_currency': -41.97, 'currency_id': self.currency_usd_id, 'currency_diff': 0, 'amount_currency_diff': -8.03},
self.diff_income_account.id: {'debit': 0.0, 'credit': 7.3, 'amount_currency': 0, 'currency_id': False},
self.diff_income_account.id: {'debit': 0.0, 'credit': 7.3, 'amount_currency': -9.37, 'currency_id': self.currency_usd_id},
})
# The invoice should be paid, as the payments totally cover its total
+21 -6
View File
@@ -1,12 +1,13 @@
# -*- coding: utf-8 -*-
import calendar
from datetime import date, datetime
from dateutil.relativedelta import relativedelta
from openerp import api, fields, models, _
from openerp.exceptions import UserError, ValidationError
from openerp.tools import DEFAULT_SERVER_DATE_FORMAT as DF
from openerp.tools import float_compare
from openerp.tools import float_compare, float_is_zero
class AccountAssetCategory(models.Model):
@@ -150,14 +151,26 @@ class AccountAssetAsset(models.Model):
if self.prorata:
amount = amount_to_depr / self.method_number
if sequence == 1:
days = (self.company_id.compute_fiscalyear_dates(depreciation_date)['date_to'] - depreciation_date).days + 1
amount = (amount_to_depr / self.method_number) / total_days * days
if self.method_period % 12 != 0:
date = datetime.strptime(self.date, '%Y-%m-%d')
month_days = calendar.monthrange(date.year, date.month)[1]
days = month_days - date.day + 1
amount = (amount_to_depr / self.method_number) / month_days * days
else:
days = (self.company_id.compute_fiscalyear_dates(depreciation_date)['date_to'] - depreciation_date).days + 1
amount = (amount_to_depr / self.method_number) / total_days * days
elif self.method == 'degressive':
amount = residual_amount * self.method_progress_factor
if self.prorata:
if sequence == 1:
days = (self.company_id.compute_fiscalyear_dates(depreciation_date)['date_to'] - depreciation_date).days + 1
amount = (residual_amount * self.method_progress_factor) / total_days * days
if self.method_period % 12 != 0:
date = datetime.strptime(self.date, '%Y-%m-%d')
month_days = calendar.monthrange(date.year, date.month)[1]
days = month_days - date.day + 1
amount = (residual_amount * self.method_progress_factor) / month_days * days
else:
days = (self.company_id.compute_fiscalyear_dates(depreciation_date)['date_to'] - depreciation_date).days + 1
amount = (residual_amount * self.method_progress_factor) / total_days * days
return amount
def _compute_board_undone_dotation_nb(self, depreciation_date, total_days):
@@ -210,6 +223,8 @@ class AccountAssetAsset(models.Model):
sequence = x + 1
amount = self._compute_board_amount(sequence, residual_amount, amount_to_depr, undone_dotation_number, posted_depreciation_line_ids, total_days, depreciation_date)
amount = self.currency_id.round(amount)
if float_is_zero(amount, precision_rounding=self.currency_id.rounding):
continue
residual_amount -= amount
vals = {
'amount': amount,
@@ -407,7 +422,7 @@ class AccountAssetAsset(models.Model):
'res_model': 'account.move',
'view_id': False,
'type': 'ir.actions.act_window',
'domain': [('id', 'in', move_ids)]
'domain': [('id', 'in', move_ids)],
}
@@ -54,7 +54,7 @@ class TestAccountAsset(common.TransactionCase):
asset_modify_number_0.with_context({'active_id': account_asset_asset_office0.id}).modify()
# I check the proper depreciation lines created.
self.assertEqual(account_asset_asset_office0.method_number, len(account_asset_asset_office0.depreciation_line_ids) - 1)
self.assertEqual(account_asset_asset_office0.method_number, len(account_asset_asset_office0.depreciation_line_ids))
# I compute a asset on period.
context = {
"active_ids": [self.ref("account_asset.menu_asset_depreciation_confirmation_wizard")],
+14 -4
View File
@@ -19,6 +19,7 @@ bus.Bus = Widget.extend({
this._super();
this.options = {};
this.activated = false;
this.bus_id = _.uniqueId('bus');
this.channels = [];
this.last = 0;
this.stop = false;
@@ -33,15 +34,24 @@ bus.Bus = Widget.extend({
this.trigger('window_focus', this.is_master);
}
});
$(window).on("focus", _.bind(this.focus_change, this, true));
$(window).on("blur", _.bind(this.focus_change, this, false));
$(window).on("unload", _.bind(this.focus_change, this, false));
$(window).on("focus." + this.bus_id, _.bind(this.focus_change, this, true));
$(window).on("blur." + this.bus_id, _.bind(this.focus_change, this, false));
$(window).on("unload." + this.bus_id, _.bind(this.focus_change, this, false));
_.each('click,keydown,keyup'.split(','), function(evtype) {
$(window).on(evtype, function() {
$(window).on(evtype + "." + self.bus_id, function() {
self.last_presence = new Date().getTime();
});
});
},
destroy: function () {
var self = this;
$(window).off("focus." + this.bus_id);
$(window).off("blur." + this.bus_id);
$(window).off("unload." + this.bus_id);
_.each('click,keydown,keyup'.split(','), function(evtype) {
$(window).off(evtype + "." + self.bus_id);
});
},
start_polling: function(){
if(!this.activated){
this.poll();
+1 -1
View File
@@ -168,7 +168,7 @@
<field name="country_id"/>
<field name="email_from"/>
<field name="phone"/>
<field name="stage_id"/>
<field name="stage_id" invisible="1"/>
<field name="user_id" invisible="1"/>
<field name="partner_id" invisible="1"/>
<field name="team_id"/>
+6 -1
View File
@@ -79,7 +79,7 @@ class StockPicking(models.Model):
def _compute_shipping_weight(self):
self.shipping_weight = self.weight_bulk + sum([pack.shipping_weight for pack in self.package_ids])
carrier_price = fields.Float(string="Shipping Cost", readonly=True)
carrier_price = fields.Float(string="Shipping Cost")
delivery_type = fields.Selection(related='carrier_id.delivery_type', readonly=True)
carrier_id = fields.Many2one("delivery.carrier", string="Carrier")
volume = fields.Float(copy=False)
@@ -91,6 +91,11 @@ class StockPicking(models.Model):
weight_bulk = fields.Float('Bulk Weight', compute='_compute_bulk_weight')
shipping_weight = fields.Float("Weight for Shipping", compute='_compute_shipping_weight')
@api.onchange('carrier_id')
def onchange_carrier(self):
if self.carrier_id.delivery_type in ['fixed', 'base_on_rule']:
self.carrier_price = self.carrier_id.price
@api.depends('product_id', 'move_lines')
def _cal_weight(self):
for picking in self:
@@ -27,6 +27,10 @@ var OdooSupport = Widget.extend({
// bind event
$(window).on("odoo_support_ready_to_bind", this, _.bind(this.bind_actions, this));
},
destroy: function () {
$(window).off("odoo_support_ready_to_bind");
return this._super.apply(this, arguments);
},
bind_actions: function(event, button){
if (button === 'usermenu'){
$('.oe_user_menu_placeholder .odoo_support_contact').on('click', this, _.bind(this.click_action, this));
@@ -661,6 +661,10 @@ var ChatAction = Widget.extend(ControlPanelMixin, {
target: 'current'
});
},
destroy: function() {
this.$buttons.off().destroy();
this._super.apply(this, arguments);
},
});
+5
View File
@@ -461,6 +461,11 @@ var BasicComposer = Widget.extend({
return this._super();
},
destroy: function () {
$(window).off(this.fileupload_id);
return this._super.apply(this, arguments);
},
toggle: function(state) {
this.$el.toggle(state);
},
@@ -42,12 +42,19 @@ odoo.define('payment_stripe.stripe', function(require) {
$(this).attr('disabled','disabled');
var $form = $(e.currentTarget).parents('form');
var acquirer_id = $(e.currentTarget).parents('div.oe_sale_acquirer_button').first().data('id');
var acquirer_id = $(e.currentTarget).closest('div.oe_sale_acquirer_button,div.oe_quote_acquirer_button').data('id');
if (! acquirer_id) {
return false;
}
ajax.jsonRpc('/shop/payment/transaction/' + acquirer_id, 'call', {}).then(function (data) {
var so_token = $("input[name='token']").val();
var so_id = $("input[name='return_url']").val().match(/quote\/([0-9]+)/);
if (so_id) {
so_id = parseInt(so_id[1]);
}
e.preventDefault();
ajax.jsonRpc('/shop/payment/transaction/' + acquirer_id, 'call', {'so_id': so_id, 'so_token': so_token}).then(function (data) {
$form.html(data);
handler.open({
name: $("input[name='merchant']").val(),
@@ -55,7 +62,6 @@ odoo.define('payment_stripe.stripe', function(require) {
currency: $("input[name='currency']").val(),
amount: $("input[name='amount']").val()*100
});
e.preventDefault();
});
});
+4 -3
View File
@@ -214,7 +214,7 @@ class PosOrder(models.Model):
if move is None:
# Create an entry for the sale
journal_id = self.env['ir.config_parameter'].sudo().get_param(
'pos.closing.journal_id', default=order.sale_journal.id)
'pos.closing.journal_id_%s' % current_company.id, default=order.sale_journal.id)
move = self._create_account_move(
order.session_id.start_at, order.name, int(journal_id), order.company_id.id)
@@ -615,7 +615,7 @@ class PosOrder(models.Model):
if moves and not return_picking and not order_picking:
moves.action_confirm()
moves.force_assign()
moves.action_done()
moves.filtered(lambda m: m.product_id.tracking == 'none').action_done()
return True
@@ -625,7 +625,8 @@ class PosOrder(models.Model):
picking.action_confirm()
picking.force_assign()
self.set_pack_operation_lot(picking)
picking.action_done()
if not any([(x.product_id.tracking != 'none') for x in picking.pack_operation_ids]):
picking.action_done()
def set_pack_operation_lot(self, picking=None):
"""Set Serial/Lot number in pack operations to mark the pack operation done."""
+1 -1
View File
@@ -19,7 +19,7 @@ class PosSession(models.Model):
company_id = session.config_id.journal_id.company_id.id
orders = session.order_ids.filtered(lambda order: order.state == 'paid')
journal_id = self.env['ir.config_parameter'].sudo().get_param(
'pos.closing.journal_id', default=session.config_id.journal_id.id)
'pos.closing.journal_id_%s' % company_id, default=session.config_id.journal_id.id)
move = self.env['pos.order'].with_context(force_company=company_id)._create_account_move(session.start_at, session.name, int(journal_id), company_id)
orders.with_context(force_company=company_id)._create_account_move_line(session, move)
for order in session.order_ids.filtered(lambda o: o.state != 'done'):
+22 -9
View File
@@ -1848,11 +1848,9 @@ var PaymentScreenWidget = ScreenWidget.extend({
self.$('.next').removeClass('highlight');
}
},
// Check if the order is paid, then sends it to the backend,
// and complete the sale process
validate_order: function(force_validation) {
var self = this;
order_is_valid: function(force_validation) {
var self = this;
var order = this.pos.get_order();
// FIXME: this check is there because the backend is unable to
@@ -1862,7 +1860,7 @@ var PaymentScreenWidget = ScreenWidget.extend({
'title': _t('Empty Order'),
'body': _t('There must be at least one product in your order before it can be validated'),
});
return;
return false;
}
var plines = order.get_paymentlines();
@@ -1872,12 +1870,12 @@ var PaymentScreenWidget = ScreenWidget.extend({
'message': _t('Negative Bank Payment'),
'comment': _t('You cannot have a negative amount in a Bank payment. Use a cash payment method to return money to the customer.'),
});
return;
return false;
}
}
if (!order.is_paid() || this.invoicing) {
return;
return false;
}
// The exact amount must be paid if there is no cash payment method defined.
@@ -1891,7 +1889,7 @@ var PaymentScreenWidget = ScreenWidget.extend({
title: _t('Cannot return change without a cash payment method'),
body: _t('There is no cash payment method available in this point of sale to handle the change.\n\n Please pay the exact amount or add a cash payment method in the point of sale configuration'),
});
return;
return false;
}
}
@@ -1912,9 +1910,16 @@ var PaymentScreenWidget = ScreenWidget.extend({
self.validate_order('confirm');
},
});
return;
return false;
}
return true;
},
finalize_validation: function() {
var self = this;
var order = this.pos.get_order();
if (order.is_paid_with_cash() && this.pos.config.iface_cashdrawer) {
this.pos.proxy.open_cashbox();
@@ -1964,6 +1969,14 @@ var PaymentScreenWidget = ScreenWidget.extend({
}
},
// Check if the order is paid, then sends it to the backend,
// and complete the sale process
validate_order: function(force_validation) {
if (this.order_is_valid(force_validation)) {
this.finalize_validation();
}
},
});
gui.define_screen({name:'payment', widget: PaymentScreenWidget});
+5 -5
View File
@@ -113,7 +113,7 @@
</group>
<group name="group_standard_price">
<field name="list_price" widget='monetary' options="{'currency_field': 'currency_id'}"/>
<label for="standard_price" groups="base.group_user" attrs="{'invisible': [('product_variant_count', '&gt;', 1)]}"/>
<label for="standard_price" groups="base.group_user"/>
<div name="standard_price_uom" groups="base.group_user">
<field name="standard_price" widget='monetary' options="{'currency_field': 'currency_id'}" class="oe_inline"/>
</div>
@@ -131,12 +131,12 @@
<field name="categ_id" string="Internal Category"/>
</group>
<group name="group_lots_and_weight">
<label for="weight" attrs="{'invisible':['|', ('type', 'not in', ['product', 'consu']), ('product_variant_count', '&gt;', 1)]}"/>
<div class="o_row" name="weight" attrs="{'invisible':['|', ('type', 'not in', ['product', 'consu']), ('product_variant_count', '&gt;', 1)]}">
<label for="weight" attrs="{'invisible':[('type', 'not in', ['product', 'consu']), ('product_variant_count', '&gt;', 1)]}"/>
<div class="o_row" name="weight" attrs="{'invisible':[('type', 'not in', ['product', 'consu']), ('product_variant_count', '&gt;', 1)]}">
<field name="weight"/>
</div>
<label for="volume" attrs="{'invisible':['|', ('type', 'not in', ['product', 'consu']), ('product_variant_count', '&gt;', 1)]}"/>
<div class="o_row" name="volume" attrs="{'invisible':['|', ('type', 'not in', ['product', 'consu']), ('product_variant_count', '&gt;', 1)]}">
<label for="volume" attrs="{'invisible':[('type', 'not in', ['product', 'consu']), ('product_variant_count', '&gt;', 1)]}"/>
<div class="o_row" name="volume" attrs="{'invisible':[('type', 'not in', ['product', 'consu']), ('product_variant_count', '&gt;', 1)]}">
<field name="volume" string="Volume"/>
</div>
</group>
@@ -152,7 +152,6 @@
<t t-name="kanban-tooltip">
<ul class="oe_kanban_tooltip">
<li><b>Project:</b> <field name="project_id"/></li>
<li><b>Category:</b> <field name="tag_ids"/></li>
</ul>
</t>
<t t-name="kanban-box">
@@ -74,7 +74,7 @@
<field name="name">report.intrastat.tree</field>
<field name="model">report.intrastat</field>
<field name="arch" type="xml">
<tree string="Intrastat Data" create="false">
<tree string="Intrastat Data" create="false" delete="false">
<field name="name" />
<field name="month"/>
<field name="code"/>
+3 -2
View File
@@ -839,13 +839,14 @@ class SaleOrderLine(models.Model):
vals = {}
domain = {'product_uom': [('category_id', '=', self.product_id.uom_id.category_id.id)]}
if not self.product_uom or (self.product_id.uom_id.category_id.id != self.product_uom.category_id.id):
if not self.product_uom or (self.product_id.uom_id.id != self.product_uom.id):
vals['product_uom'] = self.product_id.uom_id
vals['product_uom_qty'] = 1.0
product = self.product_id.with_context(
lang=self.order_id.partner_id.lang,
partner=self.order_id.partner_id.id,
quantity=self.product_uom_qty,
quantity=vals.get('product_uom_qty') or self.product_uom_qty,
date=self.order_id.date_order,
pricelist=self.order_id.pricelist_id.id,
uom=self.product_uom.id
+6 -10
View File
@@ -615,21 +615,17 @@ class QuantPackage(models.Model):
@api.one
@api.depends('parent_id', 'children_ids', 'quant_ids.package_id')
def _compute_children_quant_ids(self):
res = dict.fromkeys(self.ids, self.env['stock.quant'])
children_quants = self.env['stock.quant'].search(['package_id', 'child_of', self.ids])
for quant in children_quants:
res[quant.package_id.id] |= quant
for package in self:
package.children_quant_ids = res[package.id].ids
package.children_quant_ids = self.env['stock.quant'].search([('package_id', 'child_of', package.id)]).ids
@api.depends('quant_ids.package_id', 'quant_ids.location_id', 'quant_ids.company_id', 'quant_ids.owner_id', 'ancestor_ids')
def _compute_package_info(self):
res = {}
quants = self.env['stock.quant'].search([('package_id', 'in', self.ids)]) # TDE FIXME: was child_od
for quant in quants:
res[quant.package_id.id] = {'location_id': quant.location_id.id, 'owner_id': quant.owner_id.id, 'company_id': quant.company_id.id}
for package in self:
values = res.get(package.id, {'location_id': False, 'company_id': self.env.user.company_id.id, 'owner_id': False})
quants = package.children_quant_ids
if quants:
values = quants[0]
else:
values = {'location_id': False, 'company_id': self.env.user.company_id.id, 'owner_id': False}
package.location_id = values['location_id']
package.company_id = values['company_id']
package.owner_id = values['owner_id']
@@ -25,8 +25,8 @@
<field name="location_dest_id" domain="[('id', 'child_of', picking_destination_location_id)]"/>
</group>
<group string="Packages" groups="stock.group_tracking_lot">
<field name="package_id" attrs="{'required': [('product_id', '=', False)]}" domain="[('location_id', '=', location_id)]" context="{'location_id': location_id}"/>
<field name="result_package_id" domain="[('location_id', '=', location_dest_id)]" context="{'location_id': location_dest_id}"/>
<field name="package_id" attrs="{'required': [('product_id', '=', False)]}" domain="[('location_id', '=', location_id)]" context="{'default_location_id': location_id}"/>
<field name="result_package_id" domain="[('location_id', '=', location_dest_id)]" context="{'default_location_id': location_dest_id}"/>
</group>
</group>
</form>
@@ -85,7 +85,7 @@
<field name="lot_name" invisible="not context.get('only_create', False)"/>
<field name="lot_id" invisible="context.get('only_create', False)"
domain="[('product_id','=', parent.product_id)]"
context="{'product_id': parent.product_id}"/>
context="{'default_product_id': parent.product_id}"/>
<field name="qty_todo" invisible="not context.get('show_reserved') or context.get('serial') or context.get('state_done')" readonly="1"/>
<field name="qty" invisible="context.get('serial')"/>
<button name="do_minus" type="object" icon="fa-minus-square" attrs="{'invisible': [('qty', '&lt;=', 0.99)]}" invisible="not context.get('show_reserved') or context.get('state_done')"/>
+3 -3
View File
@@ -260,7 +260,7 @@
<field name="location_dest_id" domain="[('id', 'child_of', parent.location_dest_id)]" invisible="1"/>
<field name="from_loc" groups="stock.group_stock_multi_locations,stock.group_tracking_lot"/>
<field name="to_loc" groups="stock.group_stock_multi_locations,stock.group_tracking_lot"/>
<field name="result_package_id" groups="stock.group_tracking_lot" context="{'location_id': location_dest_id}" invisible="1"/>
<field name="result_package_id" groups="stock.group_tracking_lot" context="{'default_location_id': location_dest_id}" invisible="1"/>
<field name="state" invisible="1"/>
<field name="product_qty" readonly="1" attrs="{'required': [('product_id', '!=', False)]}"/>
<field name="qty_done" attrs="{'readonly': [('lots_visible', '=', True)]}"/>
@@ -280,7 +280,7 @@
<field name="location_dest_id" domain="[('id', 'child_of', parent.location_dest_id)]" invisible="1"/>
<field name="from_loc" groups="stock.group_locations,stock.group_tracking_lot"/>
<field name="to_loc" groups="stock.group_locations,stock.group_tracking_lot"/>
<field name="result_package_id" groups="stock.group_tracking_lot" context="{'location_id': location_dest_id}" invisible="1"/>
<field name="result_package_id" groups="stock.group_tracking_lot" context="{'default_location_id': location_dest_id}" invisible="1"/>
<field name="state" invisible="1"/>
<field name="product_qty" readonly="1" attrs="{'required': [('product_id', '!=', False)]}"/>
<field name="qty_done" attrs="{'readonly': [('lots_visible', '=', True)]}"/>
@@ -325,7 +325,7 @@
<field name="from_loc" groups="stock.group_stock_multi_locations,stock.group_tracking_lot"/>
<field name="to_loc" groups="stock.group_stock_multi_locations,stock.group_tracking_lot"/>
<field name="location_dest_id" domain="[('id', 'child_of', parent.location_dest_id)]" invisible="1"/>
<field name="result_package_id" groups="stock.group_tracking_lot" context="{'location_id': location_dest_id}" invisible="1"/>
<field name="result_package_id" groups="stock.group_tracking_lot" context="{'default_location_id': location_dest_id}" invisible="1"/>
<field name="state" invisible="1"/>
<field name="qty_done" invisible="1"/>
<field name="lots_visible" invisible="1"/>
+1 -1
View File
@@ -39,7 +39,7 @@
</field>
<field name="standard_price" position="replace">
<field name="standard_price"
attrs="{'readonly':['&amp;', ('valuation','=','real_time'), ('cost_method', 'in', ['standard', 'average'])], 'invisible': [('product_variant_count', '&gt;', 1)]}" class="oe_inline"
attrs="{'readonly':['&amp;', ('valuation','=','real_time'), ('cost_method', 'in', ['standard', 'average'])]}" class="oe_inline"
widget='monetary' options="{'currency_field': 'currency_id'}"/>
</field>
<div name="standard_price_uom" position="after">
-1
View File
@@ -1,3 +1,2 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import models # noqa
@@ -1,4 +0,0 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import stock_dropshipping # noqa
@@ -1,19 +0,0 @@
# coding: utf-8
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from openerp import models, api
class AccountInvoice(models.Model):
_inherit = 'account.invoice'
@api.model
def _anglo_saxon_sale_move_lines(self, i_line):
for sale_line in i_line.sale_line_ids:
for proc in sale_line.procurement_ids:
if proc.purchase_line_id:
# if the invoice line is related to sale order lines having one of its
# procurement_ids with a purchase_line_id set, it means that it is a
# confirmed dropship and in that case we mustn't create the cost of
# sale line (because the product won't enter the stock)
return []
return super(AccountInvoice, self)._anglo_saxon_sale_move_lines(i_line)
+1 -1
View File
@@ -189,7 +189,7 @@
if (odoo.debug && !_.isEmpty(debug_jobs)) log.push('\nDebug: ', debug_jobs);
if (odoo.debug || !_.isEmpty(failed) || !_.isEmpty(unloaded)) {
console[_.isEmpty(unloaded) ? 'info' : 'error'].apply(console, log);
console[_.isEmpty(failed) || _.isEmpty(unloaded) ? 'info' : 'error'].apply(console, log);
}
}
},
+18 -1
View File
@@ -76,6 +76,10 @@ var ControlPanel = Widget.extend({
return this._super();
},
destroy: function() {
this._clear_breadcrumbs_handlers();
return this._super.apply(this, arguments);
},
/**
* @return {Object} the Bus the ControlPanel is listening on
*/
@@ -108,7 +112,9 @@ var ControlPanel = Widget.extend({
// Render the breadcrumbs
if (status.breadcrumbs) {
new_cp_content.$breadcrumbs = this._render_breadcrumbs(status.breadcrumbs);
this._clear_breadcrumbs_handlers();
this.$breadcrumbs = this._render_breadcrumbs(status.breadcrumbs);
new_cp_content.$breadcrumbs = this.$breadcrumbs;
}
// Detach control_panel old content and attach new elements
@@ -207,6 +213,17 @@ var ControlPanel = Widget.extend({
}
return $bc;
},
/**
* Private function that removes event handlers attached on the currently
* displayed breadcrumbs.
*/
_clear_breadcrumbs_handlers: function () {
if (this.$breadcrumbs) {
_.each(this.$breadcrumbs, function ($bc) {
$bc.off();
});
}
},
/**
* Private function that updates the SearchView's visibility and extend the
* breadcrumbs area if the SearchView is not visible
@@ -196,6 +196,12 @@ var View = Widget.extend({
get_context: function () {
return {};
},
destroy: function () {
if (this.$buttons) {
this.$buttons.off();
}
return this._super.apply(this, arguments);
},
set_scrollTop: function(scrollTop) {
this.scrollTop = scrollTop;
},
+8
View File
@@ -463,6 +463,14 @@ var ViewManager = Widget.extend(ControlPanelMixin, {
}
this.active_view.controller.do_load_state(state, warm);
},
destroy: function () {
if (this.control_elements) {
if (this.control_elements.$switch_buttons) {
this.control_elements.$switch_buttons.off();
}
}
return this._super.apply(this, arguments);
},
});
return ViewManager;
@@ -99,6 +99,9 @@ var FieldMany2One = common.AbstractField.extend(common.CompletionFieldMixin, com
delete this.$dropdown;
}
if (this.$input) {
if (this.$input.data('ui-autocomplete')) {
this.$input.autocomplete("destroy");
}
this.$input.closest(".modal .modal-content").off('scroll');
this.$input.off('keyup blur autocompleteclose autocompleteopen ' +
'focus focusout change keydown');
@@ -94,6 +94,9 @@ var FormView = View.extend(common.FieldManagerMixin, {
});
},
start: function() {
if (this.$pager) {
this.$pager.off();
}
var self = this;
this.rendering_engine.set_fields_registry(this.fields_registry);
@@ -129,6 +129,12 @@ var GraphView = View.extend({
this.widget.set_measure(this.active_measure);
}
},
destroy: function () {
if (this.$buttons) {
this.$buttons.find('button').off(); // remove jquery's tooltip() handlers
}
return this._super.apply(this, arguments);
},
});
core.view_registry.add('graph', GraphView);
+1 -1
View File
@@ -267,7 +267,7 @@ var ListView = View.extend({
render_buttons: function($node) {
if (!this.$buttons) {
this.$buttons = $(QWeb.render("ListView.buttons", {'widget': this}));
this.$buttons.find('.o_list_button_add').click(this.proxy('do_add_record'));
this.$buttons.on('click', '.o_list_button_add', this.proxy('do_add_record'));
this.$buttons.appendTo($node);
}
},
@@ -827,6 +827,12 @@ var PivotView = View.extend({
error: crash_manager.rpc_error.bind(crash_manager)
});
},
destroy: function () {
if (this.$buttons) {
this.$buttons.find('button').off(); // remove jquery's tooltip() handlers
}
return this._super.apply(this, arguments);
},
});
function traverse_tree(root, f, arg1, arg2, arg3) {
@@ -52,6 +52,10 @@ var Sidebar = Widget.extend({
event.preventDefault();
});
},
destroy: function() {
$(window).off(this.fileupload_id);
return this._super.apply(this, arguments);
},
redraw: function() {
this.$el.html(QWeb.render('Sidebar', {widget: this}));
+12 -7
View File
@@ -113,11 +113,16 @@ var FieldTextHtmlSimple = widget.extend({
},
text_to_html: function (text) {
var value = text || "";
if (value.match(/^\s*$/)) {
value = '<p><br/></p>';
} else {
value = "<p>"+value.split(/<br\/?>/).join("<br/></p><p>")+"</p>";
value = value.replace(/<p><\/p>/g, '').replace('<p><p>', '<p>').replace('<p><p ', '<p ').replace('</p></p>', '</p>');
try {
$(text)[0].innerHTML;
return text;
} catch (e) {
if (value.match(/^\s*$/)) {
value = '<p><br/></p>';
} else {
value = "<p>"+value.split(/<br\/?>/).join("<br/></p><p>")+"</p>";
value = value.replace(/<p><\/p>/g, '').replace('<p><p>', '<p>').replace('<p><p ', '<p ').replace('</p></p>', '</p>');
}
}
return value;
},
@@ -213,7 +218,7 @@ var FieldTextHtml = widget.extend({
self.$iframe.css("height", (self.$body.find("#oe_snippets").length ? 500 : 300) + "px");
}
};
$(window).on('resize', self.resize);
$(window).on('resize', this.resize);
var def = this._super.apply(this, arguments);
this.$translate.remove();
@@ -374,7 +379,7 @@ var FieldTextHtml = widget.extend({
}
},
destroy: function () {
$(window).off('resize', self.resize);
$(window).off('resize', this.resize);
delete window.odoo[this.callback+"_editor"];
delete window.odoo[this.callback+"_content"];
delete window.odoo[this.callback+"_updown"];
@@ -64,6 +64,7 @@ var KanbanRecord = Widget.extend({
qweb_context[p] = _.bind(this[p], this);
}
}
this.qweb_context = qweb_context;
this.content = this.qweb.render('kanban-box', qweb_context);
},
@@ -81,7 +82,18 @@ var KanbanRecord = Widget.extend({
},
start: function() {
var self = this;
this.add_widgets();
this.$('[tooltip]').each(function () {
var $el = $(this);
var tooltip = $el.attr('tooltip');
if (tooltip) {
$el.tooltip({
'html': true,
'title': self.qweb.render(tooltip, self.qweb_context)
});
}
});
},
renderElement: function () {
@@ -172,14 +184,15 @@ var KanbanRecord = Widget.extend({
if (elem == ev.currentTarget) {
ischild = false;
}
var test_event = events && events.click && (events.click.length > 1 || events.click[0].namespace !== "tooltip");
if (ischild) {
children.push(elem);
if (events && events.click) {
if (test_event) {
// do not trigger global click if one child has a click event registered
trigger = false;
}
}
if (trigger && events && events.click) {
if (trigger && test_event) {
_.each(events.click, function(click_event) {
if (click_event.selector) {
// For each parent of original target, check if a
@@ -360,7 +360,16 @@ options.registry.gallery_img = options.Class.extend({
this.buildingBlock.make_active($parent);
$parent.data('snippet-editor').styles.gallery.reapply();
}).bind(this));
}
},
on_focus: function () {
this._super.apply(this, arguments);
if (this._current_src && this._current_src !== this.$target.attr("src")) {
_.defer((function () {
snippet_editor.globalSelector.closest(this.$target.parent()).data('snippet-editor').styles.gallery.reapply();
}).bind(this));
}
this._current_src = this.$target.attr("src");
},
});
+11 -1
View File
@@ -723,7 +723,7 @@ class WebsiteSale(http.Controller):
return request.website.render("website_sale.payment", values)
@http.route(['/shop/payment/transaction/<int:acquirer_id>'], type='json', auth="public", website=True)
def payment_transaction(self, acquirer_id):
def payment_transaction(self, acquirer_id, **kwargs):
""" Json method that creates a payment.transaction, used to create a
transaction when the user clicks on 'pay now' button. After having
created the transaction, the event continues and the user is redirected
@@ -735,6 +735,16 @@ class WebsiteSale(http.Controller):
Transaction = request.env['payment.transaction'].sudo()
order = request.website.sale_get_order()
# In case the route is called directly from the JS (as done in Stripe payment method)
so_id = kwargs.get('so_id')
so_token = kwargs.get('so_token')
if so_id and so_token:
order = request.env['sale.order'].sudo().search([('id', '=', so_id), ('access_token', '=', so_token)])
elif so_id:
order = request.env['sale.order'].search([('id', '=', so_id)])
else:
order = request.website.sale_get_order()
if not order or not order.order_line or acquirer_id is None:
return request.redirect("/shop/checkout")
+1 -1
View File
@@ -806,7 +806,7 @@ class HttpRequest(WebRequest):
if request.httprequest.method == 'OPTIONS' and request.endpoint and request.endpoint.routing.get('cors'):
headers = {
'Access-Control-Max-Age': 60 * 60 * 24,
'Access-Control-Allow-Headers': 'Origin, X-Requested-With, Content-Type, Accept'
'Access-Control-Allow-Headers': 'Origin, X-Requested-With, Content-Type, Accept, X-Debug-Mode'
}
return Response(status=200, headers=headers)