diff --git a/addons/account/__openerp__.py b/addons/account/__openerp__.py index 0cc9c538f75..df8fcf0448b 100644 --- a/addons/account/__openerp__.py +++ b/addons/account/__openerp__.py @@ -151,6 +151,7 @@ for a particular financial year and for preparation of vouchers there is a modul 'test/account_validate_account_move.yml', 'test/test_edi_invoice.yml', 'test/account_report.yml', + 'test/analytic_hierarchy.yml', 'test/account_fiscalyear_close.yml', #last test, as it will definitively close the demo fiscalyear ], 'installable': True, diff --git a/addons/account/account_bank_statement.py b/addons/account/account_bank_statement.py index 857b709e966..4c1b8de12be 100644 --- a/addons/account/account_bank_statement.py +++ b/addons/account/account_bank_statement.py @@ -584,7 +584,11 @@ class account_bank_statement_line(osv.osv): # Look for a matching amount exact_match_amount_field = amount_field in ('debit', 'credit') and 'amount_residual' or 'amount_residual_currency' domain_exact_amount = domain + [(exact_match_amount_field, '=', float_round(amount, precision_digits=precision_digits))] - match_id = self.get_move_lines_for_reconciliation(cr, uid, st_line, excluded_ids=excluded_ids, offset=0, limit=2, additional_domain=domain_exact_amount) + domain_exact_amount_ref = domain_exact_amount + [('ref', '=', st_line.ref)] + match_id = self.get_move_lines_for_reconciliation(cr, uid, st_line, excluded_ids=excluded_ids, offset=0, limit=2, additional_domain=domain_exact_amount_ref) + if not match_id: + match_id = self.get_move_lines_for_reconciliation(cr, uid, st_line, excluded_ids=excluded_ids, offset=0, limit=2, additional_domain=domain_exact_amount) + if match_id and len(match_id) == 1: return match_id diff --git a/addons/account/account_invoice.py b/addons/account/account_invoice.py index 9eadf12e9e5..2125dceeace 100644 --- a/addons/account/account_invoice.py +++ b/addons/account/account_invoice.py @@ -346,8 +346,8 @@ class account_invoice(models.Model): # adapt selection of field journal_id for field in res['fields']: - if field == 'journal_id' and type: - journal_select = self.env['account.journal']._name_search('', [('type', '=', type)], name_get_uid=1) + if field == 'journal_id' and context.get('journal_type'): + journal_select = self.env['account.journal']._name_search('', [('type', '=', context['journal_type'])], name_get_uid=1) res['fields'][field]['selection'] = journal_select doc = etree.XML(res['arch']) diff --git a/addons/account/test/analytic_hierarchy.yml b/addons/account/test/analytic_hierarchy.yml new file mode 100644 index 00000000000..117d6506b21 --- /dev/null +++ b/addons/account/test/analytic_hierarchy.yml @@ -0,0 +1,56 @@ +- + We create a simple hierarchy like this + Toplevel view account + ├ Real analytic account 1 + └ Real analytic account 2 +- + !record {model: account.analytic.account, id: analytic_account_view1}: + name: 'Toplevel view account' + type: 'view' +- + !record {model: account.analytic.account, id: analytic_account1}: + name: 'Real analytic account 1' + type: 'normal' + parent_id: analytic_account_view1 +- + !record {model: account.analytic.account, id: analytic_account2}: + name: 'Real analytic account 2' + type: 'normal' + parent_id: analytic_account_view1 +- + We add analytic lines in real accounts and journal +- + !record {model: account.analytic.journal, id: analytic_journal1}: + name: 'Analytic journal' +- + !record {model: account.analytic.line, id: analytic_line1}: + name: 'Analytic line 1' + account_id: analytic_account1 + journal_id: analytic_journal1 + general_account_id: account.a_expense +- + !record {model: account.analytic.line, id: analytic_line2}: + name: 'Analytic line 2' + account_id: analytic_account2 + journal_id: analytic_journal1 + general_account_id: account.a_expense +- + Now we copy the toplevel account +- + !function {model: account.analytic.account, name: copy, id: analytic_account_view1}: + - eval: "ref('analytic_account_view1')" +- + !python {model: account.analytic.account}: | + # the toplevel account has been copied with the (copy) suffix + accounts = self.search(cr, uid, [('name', 'like', '%Toplevel view account%')]) + assert len(accounts) == 2, 'The toplevel account has not been copied' + accounts = self.search(cr, uid, [('name', '=', 'Toplevel view account')]) + assert len(accounts) == 1, 'The toplevel account copy has a bad name' + # the sub-accounts and sub-accounts have been copied without the (copy) suffix + accounts = self.search(cr, uid, [('name', '=', 'Real analytic account 1')]) + assert len(accounts) == 2, 'The sub-account has not been copied' + accounts = self.search(cr, uid, [('name', '=', 'Real analytic account 2')]) + assert len(accounts) == 2, 'The sub-account has not been copied' + # the analytic lines should not be duplicated + lines = self.pool.get('account.analytic.line').search(cr, uid, [('name', 'like', '%Analytic line %')]) + assert len(lines) == 2, "The analytic lines shouldn't have been copied" diff --git a/addons/analytic/models/analytic.py b/addons/analytic/models/analytic.py index 227683f8bd0..3a3e305b3a4 100644 --- a/addons/analytic/models/analytic.py +++ b/addons/analytic/models/analytic.py @@ -184,9 +184,9 @@ class account_analytic_account(osv.osv): 'template_id': fields.many2one('account.analytic.account', 'Template of Contract'), 'description': fields.text('Description'), 'parent_id': fields.many2one('account.analytic.account', 'Parent Analytic Account', select=2), - 'child_ids': fields.one2many('account.analytic.account', 'parent_id', 'Child Accounts'), + 'child_ids': fields.one2many('account.analytic.account', 'parent_id', 'Child Accounts', copy=True), 'child_complete_ids': fields.function(_child_compute, relation='account.analytic.account', string="Account Hierarchy", type='many2many'), - 'line_ids': fields.one2many('account.analytic.line', 'account_id', 'Analytic Entries'), + 'line_ids': fields.one2many('account.analytic.line', 'account_id', 'Analytic Entries', copy=False), 'balance': fields.function(_debit_credit_bal_qtty, type='float', string='Balance', multi='debit_credit_bal_qtty', digits_compute=dp.get_precision('Account')), 'debit': fields.function(_debit_credit_bal_qtty, type='float', string='Debit', multi='debit_credit_bal_qtty', digits_compute=dp.get_precision('Account')), 'credit': fields.function(_debit_credit_bal_qtty, type='float', string='Credit', multi='debit_credit_bal_qtty', digits_compute=dp.get_precision('Account')), @@ -273,6 +273,8 @@ class account_analytic_account(osv.osv): raise UserError(_("Quick account creation disallowed.")) def copy(self, cr, uid, id, default=None, context=None): + """ executed only on the toplevel copied object of the hierarchy. + Subobject are actually copied with copy_data""" if not default: default = {} analytic = self.browse(cr, uid, id, context=context) diff --git a/addons/delivery/security/ir.model.access.csv b/addons/delivery/security/ir.model.access.csv index 5f7791a1a62..8a8bc9c2ea8 100644 --- a/addons/delivery/security/ir.model.access.csv +++ b/addons/delivery/security/ir.model.access.csv @@ -6,6 +6,7 @@ access_delivery_carrier_manager,delivery.carrier,model_delivery_carrier,base.gro access_delivery_grid_manager,delivery.grid,model_delivery_grid,base.group_sale_manager,1,1,1,1 access_delivery_grid_line_manager,delivery.grid.line,model_delivery_grid_line,base.group_sale_manager,1,1,1,1 access_delivery_carrier_partner_manager,delivery.carrier partner_manager,model_delivery_carrier,base.group_partner_manager,1,0,0,0 +access_delivery_carrier_stock_user,delivery.carrier stock_user,model_delivery_carrier,stock.group_stock_user,1,0,0,0 access_delivery_carrier_stock_manager,delivery.carrier,model_delivery_carrier,stock.group_stock_manager,1,1,1,1 access_delivery_grid_stock_manager,delivery.grid,model_delivery_grid,stock.group_stock_manager,1,1,1,1 access_delivery_grid_line_stock_manager,delivery.grid.line,model_delivery_grid_line,stock.group_stock_manager,1,1,1,1 diff --git a/addons/hw_escpos/escpos/constants.py b/addons/hw_escpos/escpos/constants.py index 0d1559520d6..238e8bdbf0f 100644 --- a/addons/hw_escpos/escpos/constants.py +++ b/addons/hw_escpos/escpos/constants.py @@ -75,6 +75,7 @@ TXT_ENC_PC861 = '\x1b\x74\x23' # PC861 Icelandic TXT_ENC_PC862 = '\x1b\x74\x24' # PC862 Hebrew TXT_ENC_PC864 = '\x1b\x74\x25' # PC864 Arabic TXT_ENC_PC869 = '\x1b\x74\x26' # PC869 Greek +TXT_ENC_PC936 = '\x1C\x21\x00' # PC936 GBK(Guobiao Kuozhan) TXT_ENC_8859_2 = '\x1b\x74\x27' # ISO8859-2 Latin2 TXT_ENC_8859_9 = '\x1b\x74\x28' # ISO8859-2 Latin9 TXT_ENC_PC1098 = '\x1b\x74\x29' # PC1098 Farsi diff --git a/addons/hw_escpos/escpos/escpos.py b/addons/hw_escpos/escpos/escpos.py index 9fbcda93993..5087b392922 100644 --- a/addons/hw_escpos/escpos/escpos.py +++ b/addons/hw_escpos/escpos/escpos.py @@ -748,6 +748,7 @@ class Escpos: 'cp866': TXT_ENC_PC866, 'cp862': TXT_ENC_PC862, 'cp720': TXT_ENC_PC720, + 'cp936': TXT_ENC_PC936, 'iso8859_2': TXT_ENC_8859_2, 'iso8859_7': TXT_ENC_8859_7, 'iso8859_9': TXT_ENC_8859_9, diff --git a/addons/mail/static/src/xml/mail.xml b/addons/mail/static/src/xml/mail.xml index 05ed0debb5d..554a9cafa05 100644 --- a/addons/mail/static/src/xml/mail.xml +++ b/addons/mail/static/src/xml/mail.xml @@ -92,7 +92,7 @@
- +
diff --git a/addons/mrp/stock.py b/addons/mrp/stock.py index 58fc0dad0f6..35acbf72e53 100644 --- a/addons/mrp/stock.py +++ b/addons/mrp/stock.py @@ -32,7 +32,7 @@ class StockMove(osv.osv): _inherit = 'stock.move' _columns = { - 'production_id': fields.many2one('mrp.production', 'Production Order for Produced Products', select=True), + 'production_id': fields.many2one('mrp.production', 'Production Order for Produced Products', select=True, copy=False), 'raw_material_production_id': fields.many2one('mrp.production', 'Production Order for Raw Materials', select=True), 'consumed_for': fields.many2one('stock.move', 'Consumed for', help='Technical field used to make the traceability of produced products'), } @@ -193,6 +193,8 @@ class StockMove(osv.osv): quantity_rest_uom = move.product_uom_qty - self.pool.get("product.uom")._compute_qty_obj(cr, uid, move.product_id.uom_id, product_qty, move.product_uom) if float_compare(quantity_rest_uom, 0, precision_rounding=move.product_uom.rounding) != 0: new_mov = self.split(cr, uid, move, quantity_rest, context=context) + if move.production_id: + self.write(cr, uid, [new_mov], {'production_id': move.production_id.id}, context=context) res.append(new_mov) vals = {'restrict_lot_id': restrict_lot_id, 'restrict_partner_id': restrict_partner_id, @@ -224,9 +226,9 @@ class StockMove(osv.osv): production_ids = production_obj.search(cr, uid, [('move_lines', 'in', [move.id])]) for prod_id in production_ids: production_obj.signal_workflow(cr, uid, [prod_id], 'button_produce') - for new_move in new_moves: - production_obj.write(cr, uid, production_ids, {'move_lines': [(4, new_move)]}) - res.append(new_move) + if move.production_id.id: + self.write(cr, uid, new_moves, {'production_id': move.production_id.id}, context=context) + res.append(new_moves) return res def write(self, cr, uid, ids, vals, context=None): diff --git a/addons/mrp_operations/mrp_operations.py b/addons/mrp_operations/mrp_operations.py index d86c8e6cd63..a918b537296 100644 --- a/addons/mrp_operations/mrp_operations.py +++ b/addons/mrp_operations/mrp_operations.py @@ -296,7 +296,8 @@ class mrp_production(osv.osv): if l.state in ('done','cancel','draft'): continue todo += l.move_dest_id_lines - if l.production_id and (l.production_id.date_finished > dt): + date_end = l.production_id.date_finished + if date_end and datetime.strptime(date_end, '%Y-%m-%d %H:%M:%S') > dt: if l.production_id.state not in ('done','cancel'): for wc in l.production_id.workcenter_lines: i = self.pool.get('resource.calendar').interval_min_get( diff --git a/addons/mrp_repair/mrp_repair.py b/addons/mrp_repair/mrp_repair.py index c64cde09afe..18e3dcf93af 100644 --- a/addons/mrp_repair/mrp_repair.py +++ b/addons/mrp_repair/mrp_repair.py @@ -286,7 +286,7 @@ class mrp_repair(osv.osv): else: self.write(cr, uid, [o.id], {'state': 'confirmed'}) for line in o.operations: - if line.product_id.track_production: + if line.product_id.track_production and not line.lot_id: raise UserError(_("Serial number is required for operation line with product '%s'") % (line.product_id.name)) mrp_line_obj.write(cr, uid, [l.id for l in o.operations], {'state': 'confirmed'}) return True diff --git a/addons/payment/models/payment_acquirer.py b/addons/payment/models/payment_acquirer.py index 33ce5482568..06a2b1662e8 100644 --- a/addons/payment/models/payment_acquirer.py +++ b/addons/payment/models/payment_acquirer.py @@ -13,7 +13,7 @@ def _partner_format_address(address1=False, address2=False): def _partner_split_name(partner_name): - return [' '.join(partner_name.split()[-1:]), ' '.join(partner_name.split()[:-1])] + return [' '.join(partner_name.split()[:-1]), ' '.join(partner_name.split()[-1:])] class ValidationError(ValueError): diff --git a/addons/payment_authorize/tests/test_authorize.py b/addons/payment_authorize/tests/test_authorize.py index f49aefaee40..eda6c4d3a5c 100644 --- a/addons/payment_authorize/tests/test_authorize.py +++ b/addons/payment_authorize/tests/test_authorize.py @@ -62,8 +62,8 @@ class AuthorizeForm(AuthorizeCommon): 'return_url': None, 'x_currency_code': 'USD', 'x_invoice_num': 'SO004', - 'x_first_name': 'Buyer', - 'x_last_name': 'Norbert', + 'x_first_name': 'Norbert', + 'x_last_name': 'Buyer', 'x_address': 'Huge Street 2/543', 'x_city': 'Sin City', 'x_zip': '1000', diff --git a/addons/point_of_sale/point_of_sale.py b/addons/point_of_sale/point_of_sale.py index 0ef1613e0ef..ffa1b6d090f 100644 --- a/addons/point_of_sale/point_of_sale.py +++ b/addons/point_of_sale/point_of_sale.py @@ -809,6 +809,8 @@ class pos_order(osv.osv): move_obj = self.pool.get('stock.move') for order in self.browse(cr, uid, ids, context=context): + if all(t == 'service' for t in order.lines.mapped('product_id.type')): + continue addr = order.partner_id and partner_obj.address_get(cr, uid, [order.partner_id.id], ['delivery']) or {} picking_type = order.picking_type_id picking_id = False @@ -881,9 +883,14 @@ class pos_order(osv.osv): statement_line_obj = self.pool.get('account.bank.statement.line') property_obj = self.pool.get('ir.property') order = self.browse(cr, uid, order_id, context=context) + date = data.get('payment_date', time.strftime('%Y-%m-%d')) + if len(date) > 10: + timestamp = datetime.strptime(date, tools.DEFAULT_SERVER_DATETIME_FORMAT) + ts = fields.datetime.context_timestamp(cr, uid, timestamp, context) + date = ts.strftime(tools.DEFAULT_SERVER_DATE_FORMAT) args = { 'amount': data['amount'], - 'date': data.get('payment_date', time.strftime('%Y-%m-%d')), + 'date': date, 'name': order.name + ': ' + (data.get('payment_name', '') or ''), 'partner_id': order.partner_id and self.pool.get("res.partner")._find_accounting_partner(order.partner_id).id or False, } diff --git a/addons/point_of_sale/static/src/js/models.js b/addons/point_of_sale/static/src/js/models.js index 7ea68151cae..0bd4a415e9b 100644 --- a/addons/point_of_sale/static/src/js/models.js +++ b/addons/point_of_sale/static/src/js/models.js @@ -269,7 +269,7 @@ openerp.point_of_sale.load_models = function load_models(instance, module){ //mo loaded: function(self, pricelists){ self.pricelist = pricelists[0]; }, },{ model: 'res.currency', - fields: ['symbol','position','rounding','accuracy'], + fields: ['name','symbol','position','rounding','accuracy'], ids: function(self){ return [self.pricelist.currency_id[0]]; }, loaded: function(self, currencies){ self.currency = currencies[0]; diff --git a/addons/product_extended/product_extended.py b/addons/product_extended/product_extended.py index d434e5b822a..6bc62a55e8e 100644 --- a/addons/product_extended/product_extended.py +++ b/addons/product_extended/product_extended.py @@ -76,9 +76,8 @@ class product_template(osv.osv): for sbom in bom.bom_line_ids: my_qty = sbom.product_qty if not sbom.attribute_value_ids: - bom_product = sbom.product_id # No attribute_value_ids means the bom line is not variant specific - price += uom_obj._compute_price(cr, uid, bom_product.uom_po_id and bom_product.uom_po_id.id or bom_product.uom_id.id, bom_product.standard_price, sbom.product_uom.id) * my_qty + price += uom_obj._compute_price(cr, uid, sbom.product_id.uom_id.id, sbom.product_id.standard_price, sbom.product_uom.id) * my_qty if bom.routing_id: for wline in bom.routing_id.workcenter_lines: diff --git a/addons/purchase/purchase.py b/addons/purchase/purchase.py index 9ae4cad20e3..b9426f67020 100644 --- a/addons/purchase/purchase.py +++ b/addons/purchase/purchase.py @@ -1379,7 +1379,11 @@ class procurement_order(osv.osv): names_dict[id] = name for procurement in procurements: taxes_ids = procurement.product_id.supplier_taxes_id - taxes = acc_pos_obj.map_tax(cr, uid, partner.property_account_position, taxes_ids) + # It is necessary to have the appropriate fiscal position to get the right tax mapping + fp = acc_pos_obj.get_fiscal_position(cr, uid, None, partner.id, context=context) + if fp: + fp = acc_pos_obj.browse(cr, uid, fp, context=context) + taxes = acc_pos_obj.map_tax(cr, uid, fp, taxes_ids) name = names_dict[procurement.product_id.id] if procurement.product_id.description_purchase: name += '\n' + procurement.product_id.description_purchase @@ -1500,6 +1504,7 @@ class procurement_order(osv.osv): po_line_obj = self.pool.get('purchase.order.line') seq_obj = self.pool.get('ir.sequence') uom_obj = self.pool.get('product.uom') + acc_pos_obj = self.pool.get('account.fiscal.position') add_purchase_procs, create_purchase_procs = self._get_grouping_dicts(cr, uid, ids, context=context) procs_done = [] @@ -1571,6 +1576,9 @@ class procurement_order(osv.osv): name = seq_obj.next_by_code(cr, uid, 'purchase.order') or _('PO: %s') % procurement.name gpo = procurement.rule_id.group_propagation_option group = (gpo == 'fixed' and procurement.rule_id.group_id.id) or (gpo == 'propagate' and procurement.group_id.id) or False + fp = acc_pos_obj.get_fiscal_position(cr, uid, None, partner.id, context=context) + if fp: + fp = acc_pos_obj.browse(cr, uid, fp, context=context) po_vals = { 'name': name, 'origin': procurement.origin, @@ -1580,7 +1588,7 @@ class procurement_order(osv.osv): 'pricelist_id': partner.property_product_pricelist_purchase.id, 'date_order': purchase_date.strftime(DEFAULT_SERVER_DATETIME_FORMAT), 'company_id': procurement.company_id.id, - 'fiscal_position': partner.property_account_position.id, + 'fiscal_position': fp, 'payment_term_id': partner.property_supplier_payment_term.id, 'dest_address_id': procurement.partner_dest_id.id, 'group_id': group, diff --git a/addons/stock/product.py b/addons/stock/product.py index 21f88a8d63d..db5866180d3 100644 --- a/addons/stock/product.py +++ b/addons/stock/product.py @@ -285,7 +285,7 @@ class product_product(osv.osv): toolbar=toolbar, submenu=submenu) if context is None: context = {} - if ('location' in context) and context['location']: + if context.get('location') and isinstance(context['location'], int): location_info = self.pool.get('stock.location').browse(cr, uid, context['location']) fields=res.get('fields',{}) if fields: diff --git a/addons/stock/product_view.xml b/addons/stock/product_view.xml index c970a21e8c6..052dff573a2 100644 --- a/addons/stock/product_view.xml +++ b/addons/stock/product_view.xml @@ -8,7 +8,7 @@ - {'red':virtual_available<0, 'blue':virtual_available>=0 and state in ('draft', 'end', 'obsolete'), 'black':virtual_available>=0 and state not in ('draft', 'end', 'obsolete')} + red:virtual_available<0;blue:virtual_available>=0 and state in ('draft', 'end', 'obsolete');black:virtual_available>=0 and state not in ('draft', 'end', 'obsolete') diff --git a/addons/stock/stock.py b/addons/stock/stock.py index 25157372c22..2a904e0cde1 100644 --- a/addons/stock/stock.py +++ b/addons/stock/stock.py @@ -1822,7 +1822,7 @@ class stock_move(osv.osv): "the product reservation, and should be done with care." ), 'product_uom': fields.many2one('product.uom', 'Unit of Measure', required=True, states={'done': [('readonly', True)]}), - 'product_uos_qty': fields.float('Quantity (UOS)', digits_compute=dp.get_precision('Product Unit of Measure'), states={'done': [('readonly', True)]}), + 'product_uos_qty': fields.float('Quantity (UOS)', digits_compute=dp.get_precision('Product UoS'), states={'done': [('readonly', True)]}), 'product_uos': fields.many2one('product.uom', 'Product UOS', states={'done': [('readonly', True)]}), 'product_tmpl_id': fields.related('product_id', 'product_tmpl_id', type='many2one', relation='product.template', string='Product Template'), @@ -2102,7 +2102,8 @@ class stock_move(osv.osv): break if product_uos and product_uom and (product_uom != product_uos): - result['product_uos_qty'] = product_qty * uos_coeff['uos_coeff'] + precision = self.pool.get('decimal.precision').precision_get(cr, uid, 'Product UoS') + result['product_uos_qty'] = float_round(product_qty * uos_coeff['uos_coeff'], precision_digits=precision) else: result['product_uos_qty'] = product_qty @@ -2132,7 +2133,8 @@ class stock_move(osv.osv): # The clients should call onchange_quantity too anyway if product_uos and product_uom and (product_uom != product_uos): - result['product_uom_qty'] = product_uos_qty / uos_coeff['uos_coeff'] + precision = self.pool.get('decimal.precision').precision_get(cr, uid, 'Product Unit of Measure') + result['product_uom_qty'] = float_round(product_uos_qty / uos_coeff['uos_coeff'], precision_digits=precision) else: result['product_uom_qty'] = product_uos_qty return {'value': result} @@ -3393,7 +3395,10 @@ class stock_warehouse(osv.osv): output_loc = warehouse.lot_stock_id picking_type_obj.write(cr, uid, warehouse.in_type_id.id, {'default_location_dest_id': input_loc.id}, context=context) picking_type_obj.write(cr, uid, warehouse.out_type_id.id, {'default_location_src_id': output_loc.id}, context=context) - picking_type_obj.write(cr, uid, warehouse.pick_type_id.id, {'active': new_delivery_step != 'ship_only'}, context=context) + picking_type_obj.write(cr, uid, warehouse.pick_type_id.id, { + 'active': new_delivery_step != 'ship_only', + 'default_location_dest_id': output_loc.id if new_delivery_step == 'pick_ship' else warehouse.wh_pack_stock_loc_id.id, + }, context=context) picking_type_obj.write(cr, uid, warehouse.pack_type_id.id, {'active': new_delivery_step == 'pick_pack_ship'}, context=context) routes_dict = self.get_routes_dict(cr, uid, ids, warehouse, context=context) @@ -3515,7 +3520,7 @@ class stock_warehouse(osv.osv): 'code': 'internal', 'sequence_id': pick_seq_id, 'default_location_src_id': wh_stock_loc.id, - 'default_location_dest_id': wh_pack_stock_loc.id, + 'default_location_dest_id': output_loc.id if warehouse.delivery_steps == 'pick_ship' else wh_pack_stock_loc.id, 'active': warehouse.delivery_steps != 'ship_only', 'sequence': max_sequence + 2, 'color': color}, context=context) @@ -3818,6 +3823,7 @@ class stock_location_path(osv.osv): def _prepare_push_apply(self, cr, uid, rule, move, context=None): newdate = (datetime.strptime(move.date_expected, DEFAULT_SERVER_DATETIME_FORMAT) + relativedelta.relativedelta(days=rule.delay or 0)).strftime(DEFAULT_SERVER_DATETIME_FORMAT) return { + 'origin': move.origin or move.picking_id.name or "/", 'location_id': move.location_dest_id.id, 'location_dest_id': rule.location_dest_id.id, 'date': newdate, diff --git a/addons/stock_account/wizard/stock_valuation_history.py b/addons/stock_account/wizard/stock_valuation_history.py index dc5e9ed8605..c194d28bd96 100644 --- a/addons/stock_account/wizard/stock_valuation_history.py +++ b/addons/stock_account/wizard/stock_valuation_history.py @@ -65,8 +65,10 @@ class stock_history(osv.osv): products_rec = self.pool['product.product'].read(cr, uid, product_ids, ['cost_method', 'product_tmpl_id'], context=context) products_dict = dict((product['id'], product) for product in products_rec) cost_method_product_tmpl_ids = list(set(product['product_tmpl_id'][0] for product in products_rec if product['cost_method'] != 'real')) - cr.execute('SELECT DISTINCT ON (product_template_id, company_id) product_template_id, company_id, cost FROM product_price_history WHERE product_template_id in %s AND datetime <= %s ORDER BY product_template_id, company_id, datetime DESC', (tuple(cost_method_product_tmpl_ids), date)) - histories = cr.dictfetchall() + histories = [] + if cost_method_product_tmpl_ids: + cr.execute('SELECT DISTINCT ON (product_template_id, company_id) product_template_id, company_id, cost FROM product_price_history WHERE product_template_id in %s AND datetime <= %s ORDER BY product_template_id, company_id, datetime DESC', (tuple(cost_method_product_tmpl_ids), date)) + histories = cr.dictfetchall() histories_dict = {} for history in histories: histories_dict[(history['product_template_id'], history['company_id'])] = history['cost'] diff --git a/addons/stock_landed_costs/stock_landed_costs.py b/addons/stock_landed_costs/stock_landed_costs.py index 17ed333c677..a6993597fb4 100644 --- a/addons/stock_landed_costs/stock_landed_costs.py +++ b/addons/stock_landed_costs/stock_landed_costs.py @@ -144,10 +144,12 @@ class stock_landed_cost(osv.osv): if qty_out > 0: debit_line = dict(debit_line, name=(line.name + ": " + str(qty_out) + _(' already out')), - quantity=qty_out) + quantity=qty_out, + account_id=already_out_account_id) credit_line = dict(credit_line, name=(line.name + ": " + str(qty_out) + _(' already out')), - quantity=qty_out) + quantity=qty_out, + account_id=debit_account_id) diff = diff * qty_out / line.quantity if diff > 0: debit_line['debit'] = diff diff --git a/addons/web/static/src/css/base.css b/addons/web/static/src/css/base.css index 14edf1cf14d..4e23cea7676 100644 --- a/addons/web/static/src/css/base.css +++ b/addons/web/static/src/css/base.css @@ -577,6 +577,7 @@ border-radius: 3px; background: #f0f0fa; color: #4C4C4C; + display: inline-block; word-break: break-word; } .openerp .oe_tag_dark { diff --git a/addons/web/static/src/css/base.sass b/addons/web/static/src/css/base.sass index 30ff7e83c0e..a8f0ec32085 100644 --- a/addons/web/static/src/css/base.sass +++ b/addons/web/static/src/css/base.sass @@ -514,6 +514,7 @@ $sheet-padding: 16px @include radius(3px) background: $tag-bg-light color: #4C4C4C + display: inline-block word-break: break-word .oe_tag_dark background: $tag-bg-dark diff --git a/addons/web/static/src/js/view_list_editable.js b/addons/web/static/src/js/view_list_editable.js index 5b6d4015655..150ca20a110 100644 --- a/addons/web/static/src/js/view_list_editable.js +++ b/addons/web/static/src/js/view_list_editable.js @@ -659,6 +659,7 @@ e.preventDefault(); return this._next(); } + this.editor.form.__clicked_inside = true; return $.when(); } }); diff --git a/addons/website_blog/controllers/main.py b/addons/website_blog/controllers/main.py index 76ad5c13f4e..11532ac9d5d 100644 --- a/addons/website_blog/controllers/main.py +++ b/addons/website_blog/controllers/main.py @@ -6,6 +6,7 @@ import werkzeug from openerp import tools from openerp.addons.web import http +from openerp.addons.web.controllers.main import login_redirect from openerp.addons.web.http import request from openerp.addons.website.models.website import slug, unslug from openerp.exceptions import UserError @@ -271,7 +272,8 @@ class WebsiteBlog(http.Controller): @http.route(['/blog/post_comment'], type='http', auth="public", methods=['GET', 'POST'], website=True) def blog_post_comment(self, blog_post_id=0, **kw): cr, uid, context = request.cr, request.uid, request.context - redirect_url = request.httprequest.referrer + "#comments" + post = request.registry['blog.post'].browse(cr, uid, int(blog_post_id), context=context) + redirect_url = "/blog/%s/post/%s#comments" % (slug(post.blog_id), slug(post)) if kw.get('comment'): if not request.session.uid: # if not logged, redirect to the login form, keeping the url to post the comment kw['comment'] = kw.get('comment').encode('utf8') # avoid crash from urlencode if accent @@ -282,7 +284,7 @@ class WebsiteBlog(http.Controller): blog_post = request.registry['blog.post'] post = blog_post.browse(cr, uid, blog_post_id, context=context) self._blog_post_message(blog_post_id, kw.get('comment'), **kw) - redirect_url = "/blog/%s/post/%s#comments" % (slug(post.blog_id), slug(post)) + return werkzeug.utils.redirect(redirect_url) diff --git a/addons/website_crm/views/website_crm.xml b/addons/website_crm/views/website_crm.xml index 836832e668e..a191cf9ea2e 100644 --- a/addons/website_crm/views/website_crm.xml +++ b/addons/website_crm/views/website_crm.xml @@ -14,7 +14,7 @@
- +
diff --git a/addons/website_quote/models/order.py b/addons/website_quote/models/order.py index 1e2636563fc..04a7b0a5058 100644 --- a/addons/website_quote/models/order.py +++ b/addons/website_quote/models/order.py @@ -19,6 +19,7 @@ # ############################################################################## +from openerp import api from openerp.osv import osv, fields import uuid import time @@ -370,6 +371,8 @@ class sale_order_option(osv.osv): _defaults = { 'quantity': 1, } + + # TODO master: to remove, replaced by onchange of the new api def on_change_product_id(self, cr, uid, ids, product, uom_id=None, context=None): vals, domain = {}, [] if not product: @@ -399,6 +402,17 @@ class sale_order_option(osv.osv): return {'value': {'price_unit': 0.0, 'uom_id': False}} return self.on_change_product_id(cr, uid, ids, product, uom_id=uom_id, context=context) + @api.onchange('product_id') + def _onchange_product_id(self): + product = self.product_id.with_context(lang=self.order_id.partner_id.lang) + self.price_unit = product.list_price + self.website_description = product.quote_description or product.website_description + self.name = product.name + if product.description_sale: + self.name += '\n' + product.description_sale + self.uom_id = product.product_tmpl_id.uom_id + + class product_template(osv.Model): _inherit = "product.template" diff --git a/addons/website_quote/views/website_quotation_backend.xml b/addons/website_quote/views/website_quotation_backend.xml index faaaf714d12..5d72cad630f 100644 --- a/addons/website_quote/views/website_quotation_backend.xml +++ b/addons/website_quote/views/website_quotation_backend.xml @@ -31,7 +31,7 @@ - + diff --git a/doc/Makefile b/doc/Makefile index c456284fcfb..0808b6736b5 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -152,3 +152,8 @@ doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." + +pseudoxml: + $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml + @echo + @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." diff --git a/doc/README.rst b/doc/README.rst deleted file mode 100644 index 883730b84f3..00000000000 --- a/doc/README.rst +++ /dev/null @@ -1,77 +0,0 @@ -:orphan: - -================== -Odoo Documentation -================== - -Odoo Theme -========== - -The Odoo Documentation theme is a bootstrap-based mix of http://odoo.com and -http://getbootstrap.com with additional changes and additions, bundled as -a Sphinx theme. - -The main style file is ``_themes/odoodoc/static/style.less``, it is not -converted on the fly and must be compiled manually when altere, using the -official node-based lessc_ tool. - -``odoodoc`` must be added as an extension to a project using the theme, it -fixes some discrepancies between bootstrap_ and Sphinx_ and adds a few -features: - -Sphinx Customizations ---------------------- - -* toctree bullet lists (HTML ``
    ``) are given the ``nav`` class -* the main navigation bar also gets the ``navbar-nav`` and ``navbar-right`` - set on its root (``navbar-right`` could probably be handled in CSS to avoid - having it in the markup) -* tables are given the ``table`` class -* colspecs are removed from tables, tables should autolayout -* ``data-`` attributes are copied straight from the docutils node to the - output HTML node -* an ``odoo`` pygments style based on the bootstrap_ documentation's -* the normal Sphinx sidebars are suppressed and a new sidebar is injected in - ``div.document`` (``sidebar1`` is outside in the base Sphinx layout) -* HTML5 doctype - -Additional features -------------------- - -* versions switcher, uses the ``canonical_root`` setting and an additional - ``versions`` setting which should be a comma-separated list of available - versions. Appends the each version and page name to the root, and displays - a list of those links on the current page -* canonical urls, requires a ``canonical_root`` setting value, and optionally - a ``canonical_branch`` (default: ``master``) -* :guilabel:`Edit on github` link in Sphinx pages if ``github_user`` and - ``github_project`` are provided -* :guilabel:`[source]` links in autodoc content links to github with the same - requirements (requires Sphinx 1.2) -* ``aphorism`` class for admonitions, makes the first line of the admonition - inline and the same size as the admonition category (mostly for short, - single-phrase admonitions) -* ``exercise`` directive, mostly for training-type documents, the - ``solutions`` tag_ can be used to conditionally show e.g. solutions -* a number of straight-to-HTML directives: - - ``h:div`` - a straight div, can be used instead of ``container`` (which adds the - ``container`` class to the div it generates, that's really not compatible - with Bootstrap_) - ``h:address`` - generates an ``
    `` node - a bunch of roles straight to HTML inline - ``mark``, ``insert``, ``delete``, ``strikethrough``, ``small`, ``kbd`` and - ``var`` generate the corresponding HTML element - -Requirements ------------- - -* Sphinx 1.1, 1.2 for code Python code links -* sphinx-patchqueue (for the content, not the theme) - -.. _lessc: http://lesscss.org/#using-less -.. _bootstrap: http://getbootstrap.com -.. _sphinx: http://sphinx-doc.org -.. _tag: http://sphinx-doc.org/markup/misc.html#including-content-based-on-tags diff --git a/doc/_themes/odoodoc/__init__.py b/doc/_extensions/exercise_admonition.py similarity index 63% rename from doc/_themes/odoodoc/__init__.py rename to doc/_extensions/exercise_admonition.py index dd825ca1586..4256ca73b78 100644 --- a/doc/_themes/odoodoc/__init__.py +++ b/doc/_extensions/exercise_admonition.py @@ -1,16 +1,10 @@ # -*- coding: utf-8 -*- -from . import html_domain -from . import github -# add Odoo style to pygments -from . import odoo_pygments -from . import sphinx_monkeypatch -sphinx_monkeypatch.patch() +""" +Adds a new "exercise" admonition type +""" def setup(app): - html_domain.setup(app) - github.setup(app) - app.add_directive('exercise', Exercise) app.add_node(exercise, html=( lambda self, node: self.visit_admonition(node, 'exercise'), @@ -28,8 +22,3 @@ class Exercise(admonitions.BaseAdmonition): from sphinx.locale import admonitionlabels, l_ admonitionlabels['exercise'] = l_('Exercise') - -# monkeypatch PHP lexer to not require don't dropdownify + if len(list_item.children) < 2 or not list_item.children[1].children: + continue + + list_item['classes'].append('dropdown') + # list_item.compact_paragraph.reference + link = list_item.children[0].children[0] + link['classes'].append('dropdown-toggle') + link.attributes['data-toggle'] = 'dropdown' + # list_item.bullet_list + list_item.children[1]['classes'].append('dropdown-menu') + +def resolve_content_toctree( + environment, docname, builder, toctree, prune=True, maxdepth=0, + titles_only=False, collapse=False, includehidden=False): + """Alternative toctree resolution for main content: don't resolve the + toctree, just handle it as a normal node, in the translator + """ + return toctree + +class monkey(object): + def __init__(self, obj): + self.obj = obj + def __call__(self, fn): + name = fn.__name__ + old = getattr(self.obj, name) + setattr(self.obj, name, lambda self_, *args, **kwargs: \ + fn(old, self_, *args, **kwargs)) + +@monkey(sphinx.environment.BuildEnvironment) +def resolve_toctree(old_resolve, self, docname, *args, **kwargs): + """ If navbar, bootstrapify TOC to yield a navbar + + """ + navbar = kwargs.pop('navbar', None) + if docname == self.config.master_doc and not navbar: + return resolve_content_toctree(self, docname, *args, **kwargs) + toc = old_resolve(self, docname, *args, **kwargs) + if toc is None: + return None + + navbarify(toc[0], navbar=navbar) + return toc + +@monkey(sphinx.builders.html.StandaloneHTMLBuilder) +def render_partial(old_partial, self, node): + if isinstance(node, nodes.bullet_list) and node.children: + # side nav? + # remove single top-level item + # bullet_list/0(list_item)/1(bullet_list) + level1 = node.children[0].children + if len(level1) > 1: + node = level1[1] + node['classes'].extend(['list-group', 'nav', 'text-left']) + for n in node.traverse(): + if isinstance(n, nodes.list_item): + n['classes'].append('list-group-item') + elif isinstance(n, nodes.reference): + n['classes'].append('ripple') + else: + node.clear() + return old_partial(self, node) diff --git a/doc/_extensions/odoo/layout.html b/doc/_extensions/odoo/layout.html new file mode 100644 index 00000000000..0f9661ba6de --- /dev/null +++ b/doc/_extensions/odoo/layout.html @@ -0,0 +1,134 @@ +{% extends "basic/layout.html" %} + +{% set script_files = script_files + [ +'_static/jquery.min.js', +'_static/bootstrap.js', +'_static/doc.js', +'_static/jquery.noconflict.js', +] %} + +{% set classes = [] %} +{% if pagename == master_doc %} +{% set classes = classes + ['index'] %} +{% endif %} +{% if 'code-column' in meta %} +{% set classes = classes + ['has_code_col'] %} +{% endif %} + +{%- block doctype -%} + +{%- endblock -%} +{%- block htmltitle -%} + + + {{ super() }} +{%- endblock -%} + +{%- block sidebar1 -%}{%- endblock -%} +{%- block sidebar2 -%}{%- endblock -%} +{%- block relbar1 -%}{%- endblock -%} +{%- block relbar2 -%}{%- endblock -%} + +{%- block footer -%} + {%- if google_analytics_key -%} + + {%- endif -%} +{%- endblock -%} + +{%- block header -%} +
    +
    + +
    +
    + +{%- endblock -%} + +{%- block content -%} +
    + {% if pagename != master_doc %} +
    + +
    + {% endif %} + {% block body %} {% endblock %} + {% if pagename != master_doc %}
    +
    + {% endif %} +
    +
    + + +{%- endblock -%} diff --git a/doc/_extensions/odoo/pygments_override.py b/doc/_extensions/odoo/pygments_override.py new file mode 100644 index 00000000000..58458f417e1 --- /dev/null +++ b/doc/_extensions/odoo/pygments_override.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +from pygments.style import Style +from pygments.styles.paraiso_dark import ParaisoDarkStyle +from pygments.token import * + +# extracted from getbootstrap.com +class OdooStyle(Style): + background_color = '#272727' + highlight_color = ParaisoDarkStyle.highlight_color + + styles = dict(ParaisoDarkStyle.styles) + styles[Keyword] = '#cb49a8' + styles[Name.Tag] = '#21b799' + +import imp +import sys +modname = 'pygments.styles.odoo' +m = imp.new_module(modname) +m.OdooStyle = OdooStyle +sys.modules[modname] = m diff --git a/doc/_extensions/odoo/static/animations.less b/doc/_extensions/odoo/static/animations.less new file mode 100644 index 00000000000..c95cd342322 --- /dev/null +++ b/doc/_extensions/odoo/static/animations.less @@ -0,0 +1,59 @@ + +// Animations + +.keyframes(ripple; { + 100% { + opacity: 0; + .scale(2.5); + } +}); + +.keyframes(fadeInUp; { + 0% { + opacity: 0; + .translate(0; 60px); + } + 40% { opacity: 1} + 100% { + opacity: 1; + .translate(none; none); + } +}); + +.keyframes(fadeIn; { + 0% { + opacity: 0; + } + 100% { + opacity: 1; + } +}); + + +.fadeInUp { + .animation(fadeInUp 1s); +} + +.fadeIn { + .animation(fadeIn 1s); +} + + +// Ripple Buttons + +.ripple { + z-index: 2; +} +.inner-ripple { + display: block; + position: absolute; + border-radius: 100%; + opacity: 1.3; + z-index: -1; + background: rgba(0, 0, 0, .2); + pointer-events: none; + .scale(0); +} +.inner-ripple-animated { + .animation(ripple 0.35s ease-in); +} \ No newline at end of file diff --git a/doc/_themes/odoodoc/static/bootstrap/less/alerts.less b/doc/_extensions/odoo/static/bootstrap-3.3.4/less/alerts.less old mode 100644 new mode 100755 similarity index 99% rename from doc/_themes/odoodoc/static/bootstrap/less/alerts.less rename to doc/_extensions/odoo/static/bootstrap-3.3.4/less/alerts.less index df070b8ab23..c4199db927e --- a/doc/_themes/odoodoc/static/bootstrap/less/alerts.less +++ b/doc/_extensions/odoo/static/bootstrap-3.3.4/less/alerts.less @@ -18,6 +18,7 @@ // Specified for the h4 to prevent conflicts of changing @headings-color color: inherit; } + // Provide class for links that match alerts .alert-link { font-weight: @alert-link-font-weight; @@ -28,6 +29,7 @@ > ul { margin-bottom: 0; } + > p + p { margin-top: 5px; } @@ -57,12 +59,15 @@ .alert-success { .alert-variant(@alert-success-bg; @alert-success-border; @alert-success-text); } + .alert-info { .alert-variant(@alert-info-bg; @alert-info-border; @alert-info-text); } + .alert-warning { .alert-variant(@alert-warning-bg; @alert-warning-border; @alert-warning-text); } + .alert-danger { .alert-variant(@alert-danger-bg; @alert-danger-border; @alert-danger-text); } diff --git a/doc/_themes/odoodoc/static/bootstrap/less/badges.less b/doc/_extensions/odoo/static/bootstrap-3.3.4/less/badges.less old mode 100644 new mode 100755 similarity index 85% rename from doc/_themes/odoodoc/static/bootstrap/less/badges.less rename to doc/_extensions/odoo/static/bootstrap-3.3.4/less/badges.less index 20624f30db6..c70bb939ebe --- a/doc/_themes/odoodoc/static/bootstrap/less/badges.less +++ b/doc/_extensions/odoo/static/bootstrap-3.3.4/less/badges.less @@ -28,7 +28,9 @@ position: relative; top: -1px; } - .btn-xs & { + + .btn-xs &, + .btn-group-xs > .btn & { top: 0; padding: 1px 5px; } @@ -44,11 +46,20 @@ } // Account for badges in navs - a.list-group-item.active > &, + .list-group-item.active > &, .nav-pills > .active > a > & { color: @badge-active-color; background-color: @badge-active-bg; } + + .list-group-item > & { + float: right; + } + + .list-group-item > & + & { + margin-right: 5px; + } + .nav-pills > li > a > & { margin-left: 3px; } diff --git a/doc/_extensions/odoo/static/bootstrap-3.3.4/less/bootstrap.less b/doc/_extensions/odoo/static/bootstrap-3.3.4/less/bootstrap.less new file mode 100755 index 00000000000..10cad64fac8 --- /dev/null +++ b/doc/_extensions/odoo/static/bootstrap-3.3.4/less/bootstrap.less @@ -0,0 +1,50 @@ +// Core variables and mixins +@import "variables.less"; +@import "mixins.less"; + +// Reset and dependencies +@import "normalize.less"; +@import "print.less"; +//@import "glyphicons.less"; + +// Core CSS +@import "scaffolding.less"; +@import "type.less"; +@import "code.less"; +@import "grid.less"; +@import "tables.less"; +@import "forms.less"; +@import "buttons.less"; + +// Components +@import "component-animations.less"; +@import "dropdowns.less"; +@import "button-groups.less"; +@import "input-groups.less"; +@import "navs.less"; +@import "navbar.less"; +@import "breadcrumbs.less"; +@import "pagination.less"; +@import "pager.less"; +@import "labels.less"; +@import "badges.less"; +@import "jumbotron.less"; +@import "thumbnails.less"; +@import "alerts.less"; +@import "progress-bars.less"; +@import "media.less"; +@import "list-group.less"; +@import "panels.less"; +@import "responsive-embed.less"; +@import "wells.less"; +@import "close.less"; + +// Components w/ JavaScript +@import "modals.less"; +@import "tooltip.less"; +@import "popovers.less"; +@import "carousel.less"; + +// Utility classes +@import "utilities.less"; +@import "responsive-utilities.less"; diff --git a/doc/_themes/odoodoc/static/bootstrap/less/breadcrumbs.less b/doc/_extensions/odoo/static/bootstrap-3.3.4/less/breadcrumbs.less old mode 100644 new mode 100755 similarity index 100% rename from doc/_themes/odoodoc/static/bootstrap/less/breadcrumbs.less rename to doc/_extensions/odoo/static/bootstrap-3.3.4/less/breadcrumbs.less diff --git a/doc/_themes/odoodoc/static/bootstrap/less/button-groups.less b/doc/_extensions/odoo/static/bootstrap-3.3.4/less/button-groups.less old mode 100644 new mode 100755 similarity index 86% rename from doc/_themes/odoodoc/static/bootstrap/less/button-groups.less rename to doc/_extensions/odoo/static/bootstrap-3.3.4/less/button-groups.less index 7021ecd1712..13c1800deaa --- a/doc/_themes/odoodoc/static/bootstrap/less/button-groups.less +++ b/doc/_extensions/odoo/static/bootstrap-3.3.4/less/button-groups.less @@ -18,10 +18,6 @@ &.active { z-index: 2; } - &:focus { - // Remove focus outline when dropdown JS adds it after closing the menu - outline: 0; - } } } @@ -75,13 +71,13 @@ .btn-group > .btn-group:not(:first-child):not(:last-child) > .btn { border-radius: 0; } -.btn-group > .btn-group:first-child { +.btn-group > .btn-group:first-child:not(:last-child) { > .btn:last-child, > .dropdown-toggle { .border-right-radius(0); } } -.btn-group > .btn-group:last-child > .btn:first-child { +.btn-group > .btn-group:last-child:not(:first-child) > .btn:first-child { .border-left-radius(0); } @@ -198,7 +194,6 @@ } - // Justified button groups // ---------------------- @@ -226,15 +221,23 @@ // Checkbox and radio options // // In order to support the browser's form validation feedback, powered by the -// `required` attribute, we have to "hide" the inputs via `opacity`. We cannot -// use `display: none;` or `visibility: hidden;` as that also hides the popover. +// `required` attribute, we have to "hide" the inputs via `clip`. We cannot use +// `display: none;` or `visibility: hidden;` as that also hides the popover. +// Simply visually hiding the inputs via `opacity` would leave them clickable in +// certain cases which is prevented by using `clip` and `pointer-events`. // This way, we ensure a DOM element is visible to position the popover from. // -// See https://github.com/twbs/bootstrap/pull/12794 for more. +// See https://github.com/twbs/bootstrap/pull/12794 and +// https://github.com/twbs/bootstrap/pull/14559 for more information. -[data-toggle="buttons"] > .btn > input[type="radio"], -[data-toggle="buttons"] > .btn > input[type="checkbox"] { - position: absolute; - z-index: -1; - .opacity(0); +[data-toggle="buttons"] { + > .btn, + > .btn-group > .btn { + input[type="radio"], + input[type="checkbox"] { + position: absolute; + clip: rect(0,0,0,0); + pointer-events: none; + } + } } diff --git a/doc/_themes/odoodoc/static/bootstrap/less/buttons.less b/doc/_extensions/odoo/static/bootstrap-3.3.4/less/buttons.less old mode 100644 new mode 100755 similarity index 95% rename from doc/_themes/odoodoc/static/bootstrap/less/buttons.less rename to doc/_extensions/odoo/static/bootstrap-3.3.4/less/buttons.less index 492bdc65ae8..5a746049e3c --- a/doc/_themes/odoodoc/static/bootstrap/less/buttons.less +++ b/doc/_extensions/odoo/static/bootstrap-3.3.4/less/buttons.less @@ -12,6 +12,7 @@ font-weight: @btn-font-weight; text-align: center; vertical-align: middle; + touch-action: manipulation; cursor: pointer; background-image: none; // Reset unusual Firefox-on-Android default style; see https://github.com/necolas/normalize.css/issues/214 border: 1px solid transparent; @@ -22,13 +23,15 @@ &, &:active, &.active { - &:focus { + &:focus, + &.focus { .tab-focus(); } } &:hover, - &:focus { + &:focus, + &.focus { color: @btn-default-color; text-decoration: none; } @@ -43,7 +46,7 @@ &.disabled, &[disabled], fieldset[disabled] & { - cursor: not-allowed; + cursor: @cursor-disabled; pointer-events: none; // Future-proof disabling of clicks .opacity(.65); .box-shadow(none); @@ -85,11 +88,11 @@ .btn-link { color: @link-color; font-weight: normal; - cursor: pointer; border-radius: 0; &, &:active, + &.active, &[disabled], fieldset[disabled] & { background-color: transparent; @@ -104,7 +107,7 @@ &:hover, &:focus { color: @link-hover-color; - text-decoration: underline; + text-decoration: @link-hover-decoration; background-color: transparent; } &[disabled], diff --git a/doc/_themes/odoodoc/static/bootstrap/less/carousel.less b/doc/_extensions/odoo/static/bootstrap-3.3.4/less/carousel.less old mode 100644 new mode 100755 similarity index 88% rename from doc/_themes/odoodoc/static/bootstrap/less/carousel.less rename to doc/_extensions/odoo/static/bootstrap-3.3.4/less/carousel.less index 1644ddf7f53..4bbe946d3aa --- a/doc/_themes/odoodoc/static/bootstrap/less/carousel.less +++ b/doc/_extensions/odoo/static/bootstrap-3.3.4/less/carousel.less @@ -24,6 +24,30 @@ &:extend(.img-responsive); line-height: 1; } + + // WebKit CSS3 transforms for supported devices + @media all and (transform-3d), (-webkit-transform-3d) { + .transition-transform(~'0.6s ease-in-out'); + .backface-visibility(~'hidden'); + .perspective(1000); + + &.next, + &.active.right { + .translate3d(100%, 0, 0); + left: 0; + } + &.prev, + &.active.left { + .translate3d(-100%, 0, 0); + left: 0; + } + &.next.left, + &.prev.right, + &.active { + .translate3d(0, 0, 0); + left: 0; + } + } } > .active, @@ -124,6 +148,7 @@ width: 20px; height: 20px; margin-top: -10px; + line-height: 1; font-family: serif; } @@ -171,6 +196,7 @@ // Internet Explorer 8-9 does not support clicks on elements without a set // `background-color`. We cannot use `filter` since that's not viewed as a // background color by the browser. Thus, a hack is needed. + // See https://developer.mozilla.org/en-US/docs/Web/Events/click#Internet_Explorer // // For IE8, we set solid black as it doesn't support `rgba()`. For IE9, we // set alpha transparency for the best results possible. diff --git a/doc/_themes/odoodoc/static/bootstrap/less/close.less b/doc/_extensions/odoo/static/bootstrap-3.3.4/less/close.less old mode 100644 new mode 100755 similarity index 89% rename from doc/_themes/odoodoc/static/bootstrap/less/close.less rename to doc/_extensions/odoo/static/bootstrap-3.3.4/less/close.less index 9b4e74f2b82..6d5bfe087ae --- a/doc/_themes/odoodoc/static/bootstrap/less/close.less +++ b/doc/_extensions/odoo/static/bootstrap-3.3.4/less/close.less @@ -23,6 +23,7 @@ // Additional properties for button version // iOS requires the button element instead of an anchor tag. // If you want the anchor version, it requires `href="#"`. + // See https://developer.mozilla.org/en-US/docs/Web/Events/click#Safari_Mobile button& { padding: 0; cursor: pointer; diff --git a/doc/_themes/odoodoc/static/bootstrap/less/code.less b/doc/_extensions/odoo/static/bootstrap-3.3.4/less/code.less old mode 100644 new mode 100755 similarity index 98% rename from doc/_themes/odoodoc/static/bootstrap/less/code.less rename to doc/_extensions/odoo/static/bootstrap-3.3.4/less/code.less index baa13df613b..a08b4d48c4c --- a/doc/_themes/odoodoc/static/bootstrap/less/code.less +++ b/doc/_extensions/odoo/static/bootstrap-3.3.4/less/code.less @@ -32,6 +32,7 @@ kbd { kbd { padding: 0; font-size: 100%; + font-weight: bold; box-shadow: none; } } diff --git a/doc/_themes/odoodoc/static/bootstrap/less/component-animations.less b/doc/_extensions/odoo/static/bootstrap-3.3.4/less/component-animations.less old mode 100644 new mode 100755 similarity index 82% rename from doc/_themes/odoodoc/static/bootstrap/less/component-animations.less rename to doc/_extensions/odoo/static/bootstrap-3.3.4/less/component-animations.less index 9400a0d32f3..0bcee910ac5 --- a/doc/_themes/odoodoc/static/bootstrap/less/component-animations.less +++ b/doc/_extensions/odoo/static/bootstrap-3.3.4/less/component-animations.less @@ -27,5 +27,7 @@ position: relative; height: 0; overflow: hidden; - .transition(height .35s ease); + .transition-property(~"height, visibility"); + .transition-duration(.35s); + .transition-timing-function(ease); } diff --git a/doc/_themes/odoodoc/static/bootstrap/less/dropdowns.less b/doc/_extensions/odoo/static/bootstrap-3.3.4/less/dropdowns.less old mode 100644 new mode 100755 similarity index 96% rename from doc/_themes/odoodoc/static/bootstrap/less/dropdowns.less rename to doc/_extensions/odoo/static/bootstrap-3.3.4/less/dropdowns.less index 3eb7fc05c44..af344607bd9 --- a/doc/_themes/odoodoc/static/bootstrap/less/dropdowns.less +++ b/doc/_extensions/odoo/static/bootstrap-3.3.4/less/dropdowns.less @@ -10,12 +10,13 @@ height: 0; margin-left: 2px; vertical-align: middle; - border-top: @caret-width-base solid; + border-top: @caret-width-base dashed; border-right: @caret-width-base solid transparent; border-left: @caret-width-base solid transparent; } // The dropdown wrapper (div) +.dropup, .dropdown { position: relative; } @@ -103,16 +104,15 @@ &:focus { color: @dropdown-link-disabled-color; } -} -// Nuke hover/focus effects -.dropdown-menu > .disabled > a { + + // Nuke hover/focus effects &:hover, &:focus { text-decoration: none; background-color: transparent; background-image: none; // Remove CSS gradient .reset-filter(); - cursor: not-allowed; + cursor: @cursor-disabled; } } @@ -191,7 +191,7 @@ .dropdown-menu { top: auto; bottom: 100%; - margin-bottom: 1px; + margin-bottom: 2px; } } @@ -212,4 +212,3 @@ } } } - diff --git a/doc/_themes/odoodoc/static/bootstrap/less/forms.less b/doc/_extensions/odoo/static/bootstrap-3.3.4/less/forms.less old mode 100644 new mode 100755 similarity index 80% rename from doc/_themes/odoodoc/static/bootstrap/less/forms.less rename to doc/_extensions/odoo/static/bootstrap-3.3.4/less/forms.less index 2c5e9bfa931..e4b50629e97 --- a/doc/_themes/odoodoc/static/bootstrap/less/forms.less +++ b/doc/_extensions/odoo/static/bootstrap-3.3.4/less/forms.less @@ -123,7 +123,7 @@ output { background-color: @input-bg; background-image: none; // Reset unusual Firefox-on-Android default style; see https://github.com/necolas/normalize.css/issues/214 border: 1px solid @input-border; - border-radius: @input-border-radius; + border-radius: @input-border-radius; // Note: This has no effect on s in CSS. .box-shadow(inset 0 1px 1px rgba(0,0,0,.075)); .transition(~"border-color ease-in-out .15s, box-shadow ease-in-out .15s"); @@ -141,9 +141,13 @@ output { &[disabled], &[readonly], fieldset[disabled] & { - cursor: not-allowed; background-color: @input-bg-disabled; - opacity: 1; // iOS fix for unreadable disabled content + opacity: 1; // iOS fix for unreadable disabled content; see https://github.com/twbs/bootstrap/issues/11655 + } + + &[disabled], + fieldset[disabled] & { + cursor: @cursor-disabled; } // Reset height for `textarea`s @@ -168,24 +172,26 @@ input[type="search"] { // Special styles for iOS temporal inputs // // In Mobile Safari, setting `display: block` on temporal inputs causes the -// text within the input to become vertically misaligned. -// As a workaround, we set a pixel line-height that matches the -// given height of the input. Since this fucks up everything else, we have to -// appropriately reset it for Internet Explorer and the size variations. +// text within the input to become vertically misaligned. As a workaround, we +// set a pixel line-height that matches the given height of the input, but only +// for Safari. See https://bugs.webkit.org/show_bug.cgi?id=139848 -input[type="date"], -input[type="time"], -input[type="datetime-local"], -input[type="month"] { - line-height: @input-height-base; - // IE8+ misaligns the text within date inputs, so we reset - line-height: @line-height-base ~"\0"; +@media screen and (-webkit-min-device-pixel-ratio: 0) { + input[type="date"], + input[type="time"], + input[type="datetime-local"], + input[type="month"] { + line-height: @input-height-base; - &.input-sm { - line-height: @input-height-small; - } - &.input-lg { - line-height: @input-height-large; + &.input-sm, + .input-group-sm & { + line-height: @input-height-small; + } + + &.input-lg, + .input-group-lg & { + line-height: @input-height-large; + } } } @@ -196,7 +202,7 @@ input[type="month"] { // horizontal forms, use the predefined grid classes. .form-group { - margin-bottom: 15px; + margin-bottom: @form-group-margin-bottom; } @@ -208,11 +214,11 @@ input[type="month"] { .checkbox { position: relative; display: block; - min-height: @line-height-computed; // clear the floating input if there is no label text margin-top: 10px; margin-bottom: 10px; label { + min-height: @line-height-computed; // Ensure the input doesn't jump when there is no text padding-left: 20px; margin-bottom: 0; font-weight: normal; @@ -236,6 +242,7 @@ input[type="month"] { // Radios and checkboxes on same line .radio-inline, .checkbox-inline { + position: relative; display: inline-block; padding-left: 20px; margin-bottom: 0; @@ -258,7 +265,7 @@ input[type="checkbox"] { &[disabled], &.disabled, fieldset[disabled] & { - cursor: not-allowed; + cursor: @cursor-disabled; } } // These classes are used directly on