[MERGE] forward port branch 11.0 up to fd122aec52

This commit is contained in:
Christophe Simonis
2018-09-26 19:37:29 +02:00
53 changed files with 455 additions and 70 deletions
+1 -1
View File
@@ -24,7 +24,7 @@ class MailController(MailController):
except AccessError:
if record_sudo.access_token and consteq(record_sudo.access_token, access_token):
record_action = record_sudo.with_context(
force_website=True).get_access_action(uid)
force_website=True).get_access_action()
if record_action['type'] == 'ir.actions.act_url':
return werkzeug.utils.redirect(record_action['url'])
return super(MailController, cls)._redirect_to_record(model, res_id, access_token=access_token)
+2
View File
@@ -177,6 +177,8 @@ class AccountAccount(models.Model):
@api.onchange('internal_type')
def onchange_internal_type(self):
self.reconcile = self.internal_type in ('receivable', 'payable')
if self.internal_type == 'liquidity':
self.reconcile = False
@api.onchange('code')
def onchange_code(self):
+1 -1
View File
@@ -1475,7 +1475,7 @@ class AccountInvoiceLine(models.Model):
ondelete='set null', index=True, oldname='uos_id')
product_id = fields.Many2one('product.product', string='Product',
ondelete='restrict', index=True)
product_image = fields.Binary('Product Image', related="product_id.image", store=False)
product_image = fields.Binary('Product Image', related="product_id.image", store=False, readonly=True)
account_id = fields.Many2one('account.account', string='Account',
required=True, domain=[('deprecated', '=', False)],
default=_default_account,
+2 -2
View File
@@ -22,8 +22,8 @@
<field name="company_id" options="{'no_create': True}" groups="base.group_multi_company"/>
<field name="currency_id" options="{'no_create': True}" groups="base.group_multi_currency"/>
<field name="internal_type" invisible="1" readonly="1"/>
<label for="reconcile"/>
<div>
<label for="reconcile" attrs="{'invisible': [('internal_type','=','liquidity')]}"/>
<div attrs="{'invisible': [('internal_type','=','liquidity')]}">
<field name="reconcile"/>
<button name="action_open_reconcile" class="oe_link" type="object" string=" -> Reconcile" attrs="{'invisible': [('reconcile', '=', False)]}"/>
</div>
@@ -27,7 +27,7 @@ class PaymentPortal(http.Controller):
except:
return False
if request.env.user == request.env.ref('base.public_user'):
if request.env.user._is_public():
save_token = False # we avoid to create a token for the public user
token = request.env['payment.token'].sudo() # currently no support of payment tokens
+1 -1
View File
@@ -11,7 +11,7 @@ class PortalAccount(PortalAccount):
values = super(PortalAccount, self)._invoice_get_page_view_values(invoice, access_token, **kwargs)
payment_inputs = request.env['payment.acquirer']._get_available_payment_input(company=invoice.company_id)
# if not connected (using public user), the method _get_available_payment_input will return public user tokens
is_public_user = request.env.ref('base.public_user') == request.env.user
is_public_user = request.env.user._is_public()
if is_public_user:
# we should not display payment tokens owned by the public user
payment_inputs.pop('pms', None)
+2 -3
View File
@@ -1082,9 +1082,8 @@ class Meeting(models.Model):
else:
sort_fields[field] = self[field]
if isinstance(self[field], models.BaseModel):
name_get = self[field].name_get()
if len(name_get) and len(name_get[0]) >= 2:
sort_fields[field] = name_get[0][1]
name_get = self[field].mapped('display_name')
sort_fields[field] = name_get and name_get[0] or ''
if r_date:
sort_fields['sort_start'] = r_date.strftime(VIRTUALID_DATETIME_FORMAT)
else:
+3 -4
View File
@@ -50,10 +50,9 @@ Sidebar.include({
* @override
*/
updateEnv: function (env) {
if (this.hasAttachments) {
this.env = env;
this._updateAttachments().then(this._redraw.bind(this));
}
var _super = _.bind(this._super, this, env);
var def = this.hasAttachments ? this._updateAttachments() : $.when();
def.then(_super);
},
//--------------------------------------------------------------------------
+2 -1
View File
@@ -146,7 +146,8 @@ class IapAccount(models.Model):
def get(self, service_name):
account = self.search([('service_name', '=', service_name), ('company_id', 'in', [self.env.user.company_id.id, False])])
if not account:
account = self.create({'service_name': service_name})
# TODO: remove this sudo in v12 as we change the user rights.
account = self.sudo().create({'service_name': service_name}).with_env(self.env)
# Since the account did not exist yet, we will encounter a NoCreditError,
# which is going to rollback the database and undo the account creation,
# preventing the process to continue any further.
@@ -1,5 +1,5 @@
id,code,name,reconcile,user_type_id:id,chart_template_id:id
co_puc_110505,110505,Caja General,TRUE,account.data_account_type_liquidity,l10n_co_chart_template_generic
co_puc_110505,110505,Caja General,FALSE,account.data_account_type_liquidity,l10n_co_chart_template_generic
co_puc_120535,120535,Comercio al Por Mayor y al Por Menor,FALSE,account.data_account_type_current_assets,l10n_co_chart_template_generic
co_puc_129595,129595,Diversas,FALSE,account.data_account_type_current_assets,l10n_co_chart_template_generic
co_puc_130505,130505,Nacionales,TRUE,account.data_account_type_receivable,l10n_co_chart_template_generic
1 id code name reconcile user_type_id:id chart_template_id:id
2 co_puc_110505 110505 Caja General TRUE FALSE account.data_account_type_liquidity l10n_co_chart_template_generic
3 co_puc_120535 120535 Comercio al Por Mayor y al Por Menor FALSE account.data_account_type_current_assets l10n_co_chart_template_generic
4 co_puc_129595 129595 Diversas FALSE account.data_account_type_current_assets l10n_co_chart_template_generic
5 co_puc_130505 130505 Nacionales TRUE account.data_account_type_receivable l10n_co_chart_template_generic
@@ -8,7 +8,7 @@
<field name="code">0-111301</field>
<field name="name">0-Cuenta PayPal 1</field>
<field name="user_type_id" ref="account.data_account_type_liquidity"/>
<field name="reconcile" eval="True"/>
<field name="reconcile" eval="False"/>
<field name="chart_template_id" ref="account_chart_template_0"/>
</record>
<record id="account_account_template_0_111401" model="account.account.template">
+1 -1
View File
@@ -26,7 +26,7 @@
</t>
</xpath>
<xpath expr="//table[@name='invoice_line_table']" position="replace">
<table class="table table-condensed" name="invoice_line_table">
<table class="table table-condensed" name="invoice_line_table" groups="!sale.group_sale_layout">
<thead>
<tr>
<th>Description</th>
+1
View File
@@ -1193,6 +1193,7 @@ class MailThread(models.AbstractModel):
# if a new thread is created, parent is irrelevant
message_dict.pop('parent_id', None)
thread = MessageModel.message_new(message_dict, custom_values)
thread_id = thread.id
else:
if thread_id:
raise ValueError("Posting a message without model should be with a null res_id, to create a private message.")
+1 -1
View File
@@ -235,7 +235,7 @@ class MassMailingCampaign(models.Model):
mass_mailing_ids = fields.One2many(
'mail.mass_mailing', 'mass_mailing_campaign_id',
string='Mass Mailings')
unique_ab_testing = fields.Boolean(string='Allow A/B Testing', default=True,
unique_ab_testing = fields.Boolean(string='Allow A/B Testing', default=False,
help='If checked, recipients will be mailed only once for the whole campaign. '
'This lets you send different mailings to randomly selected recipients and test '
'the effectiveness of the mailings, without causing duplicate messages.')
+1 -1
View File
@@ -465,7 +465,7 @@ class PosOrder(models.Model):
def _filtered_for_reconciliation(self):
filter_states = ['invoiced', 'done']
if self.env['ir.config_parameter'].get_param('point_of_sale.order_reconcile_mode', 'all') == 'partner_only':
if self.env['ir.config_parameter'].sudo().get_param('point_of_sale.order_reconcile_mode', 'all') == 'partner_only':
return self.filtered(lambda order: order.state in filter_states and order.partner_id)
return self.filtered(lambda order: order.state in filter_states)
@@ -36,9 +36,13 @@ class TestAngloSaxonCommon(common.TransactionCase):
self.product = self.product.copy({'name': 'New product','standard_price': 100})
self.company.anglo_saxon_accounting = True
self.product.categ_id = self.category
self.product.property_account_expense_id = account_expense
self.product.property_account_income_id = self.account
sale_journal = self.env['account.journal'].create({'name': 'POS journal', 'type': 'sale', 'code': 'POS00'})
self.pos_config.journal_id = sale_journal
self.cash_journal = self.env['account.journal'].create({'name': 'CASH journal', 'type': 'cash', 'code': 'CSH00'})
self.pos_config.invoice_journal_id = False
self.pos_config.journal_ids = [self.cash_journal.id]
class TestAngloSaxonFlow(TestAngloSaxonCommon):
+1 -1
View File
@@ -37,7 +37,7 @@ def _message_post_helper(res_model='', res_id=None, message='', token='', nosubs
access_as_sudo = _has_token_access(res_model, res_id, token=token)
if access_as_sudo:
record = record.sudo()
if request.env.user == request.env.ref('base.public_user'):
if request.env.user._is_public():
author_id = record.partner_id.id if hasattr(record, 'partner_id') else author_id
else:
if not author_id:
+5
View File
@@ -123,6 +123,7 @@ class ProductProduct(models.Model):
image_medium = fields.Binary(
"Medium-sized image", compute='_compute_images', inverse='_set_image_medium',
help="Image of the product variant (Medium-sized image of product template if false).")
is_product_variant = fields.Boolean(compute='_compute_is_product_variant')
standard_price = fields.Float(
'Cost', company_dependent=True,
@@ -150,6 +151,10 @@ class ProductProduct(models.Model):
def _get_invoice_policy(self):
return False
def _compute_is_product_variant(self):
for product in self:
product.is_product_variant = True
def _compute_product_price(self):
prices = {}
pricelist_id_or_name = self._context.get('pricelist')
+1 -4
View File
@@ -239,10 +239,7 @@ class ProductTemplate(models.Model):
def _compute_is_product_variant(self):
for template in self:
if template._name == 'product.template':
template.is_product_variant = False
else:
template.is_product_variant = True
template.is_product_variant = False
@api.model
def _get_weight_uom_id_from_ir_config_parameter(self):
+1
View File
@@ -36,6 +36,7 @@ class TestProductCommon(common.SavepointCase):
cls.product_1 = Product.create({
'name': 'Courage',
'type': 'consu',
'default_code': 'PROD-1',
'uom_id': cls.uom_dunit.id,
'uom_po_id': cls.uom_dunit.id})
+8
View File
@@ -60,6 +60,14 @@ class TestVariants(common.TestProductCommon):
self.size_attr_value_l = self.env['product.attribute.value'].create({'name': 'L', 'attribute_id': self.size_attr.id})
return res
def test_variants_is_product_variant(self):
template = self.product_7_template
variants = template.product_variant_ids
self.assertFalse(template.is_product_variant,
'Product template is not a variant')
self.assertEqual({True}, set(v.is_product_variant for v in variants),
'Product variants are variants')
def test_variants_creation_mono(self):
test_template = self.env['product.template'].create({
'name': 'Sofa',
+3 -2
View File
@@ -271,7 +271,9 @@ class Project(models.Model):
def map_tasks(self, new_project_id):
""" copy and map tasks from old to new project """
tasks = self.env['project.task']
for task in self.tasks:
# We want to copy archived task, but do not propagate an active_test context key
task_ids = self.env['project.task'].with_context(active_test=False).search([('project_id', '=', self.id)]).ids
for task in self.env['project.task'].browse(task_ids):
# preserve task name and stage, normally altered during copy
defaults = {'stage_id': task.stage_id.id,
'name': task.name}
@@ -283,7 +285,6 @@ class Project(models.Model):
def copy(self, default=None):
if default is None:
default = {}
self = self.with_context(active_test=False)
if not default.get('name'):
default['name'] = _("%s (copy)") % (self.name)
project = super(Project, self).copy(default)
+1 -1
View File
@@ -731,7 +731,7 @@
<field name="name">Project's tasks</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form,calendar,graph,kanban</field>
<field name="domain">[('project_id', 'child_of', [active_id])]</field>
<field name="domain">[('project_id', '=', active_id)]</field>
<field name="context">{'project_id':active_id}</field>
</record>
@@ -119,6 +119,7 @@ class TestCreatePicking(common.TestProductCommon):
'route_ids': [(4, self.ref('stock.route_warehouse0_mto')), (4, self.ref('purchase.route_warehouse0_buy'))],
'seller_ids': [(6, 0, [seller.id])],
'categ_id': self.env.ref('product.product_category_all').id,
'supplier_taxes_id': [(6, 0, [])],
})
customer_move = self.env['stock.move'].create({
+1
View File
@@ -218,6 +218,7 @@
</tree>
<form string="Purchase Order Line">
<sheet>
<field name="state" invisible="1"/>
<group>
<group>
<field name="product_id"
+1 -1
View File
@@ -24,7 +24,7 @@ class MailController(MailController):
except AccessError:
if record_sudo.access_token and consteq(record_sudo.access_token, access_token):
record_action = record_sudo.with_context(
force_website=True).get_access_action(uid)
force_website=True).get_access_action()
if record_action['type'] == 'ir.actions.act_url':
return werkzeug.utils.redirect(record_action['url'])
return super(MailController, cls)._redirect_to_record(model, res_id, access_token=access_token)
+2 -2
View File
@@ -30,7 +30,7 @@ class PaymentPortal(http.Controller):
# we check if the order need to create a payment token
save_token = save_token or order_sudo._get_payment_type() == "form_save"
if request.env.user == request.env.ref('base.public_user'):
if request.env.user._is_public():
save_token = False
token = request.env['payment.token'].sudo() # currently no support of payment tokens
@@ -74,7 +74,7 @@ class PaymentPortal(http.Controller):
except (ValueError, TypeError):
token = False
token_owner = order_sudo.partner_id if request.env.user == request.env.ref('base.public_user') else request.env.user.partner_id
token_owner = order_sudo.partner_id if request.env.user._is_public() else request.env.user.partner_id
if not token or token.partner_id != token_owner:
params['error'] = 'pay_sale_invalid_token'
return request.redirect(_build_url_w_params(error_url, params))
+1 -1
View File
@@ -12,7 +12,7 @@ class CustomerPortal(CustomerPortal):
if values['portal_confirmation'] == 'pay':
payment_inputs = request.env['payment.acquirer']._get_available_payment_input(order.partner_id, order.company_id)
# if not connected (using public user), the method _get_available_payment_input will return public user tokens
is_public_user = request.env.ref('base.public_user') == request.env.user
is_public_user = request.env.user._is_public()
if is_public_user:
# we should not display payment tokens owned by the public user
payment_inputs.pop('pms', None)
+4 -1
View File
@@ -235,7 +235,10 @@ class SaleOrderLine(models.Model):
return {}
if self.product_id.type == 'product':
precision = self.env['decimal.precision'].precision_get('Product Unit of Measure')
product = self.product_id.with_context(warehouse=self.order_id.warehouse_id.id)
product = self.product_id.with_context(
warehouse=self.order_id.warehouse_id.id,
lang=self.order_id.partner_id.lang or self.env.user.lang or 'en_US'
)
product_qty = self.product_uom._compute_quantity(self.product_uom_qty, self.product_id.uom_id)
if float_compare(product.virtual_available, product_qty, precision_digits=precision) == -1:
is_available = self._check_routing()
+5
View File
@@ -14,26 +14,31 @@
<field name="location_id" ref="stock_location_suppliers"/>
<field name="usage">supplier</field>
<field name="barcode">2601985</field>
<field name="company_id"></field>
</record>
<record id="stock_location_4" model="stock.location">
<field name="name">Big Vendors</field>
<field name="location_id" ref="stock_location_3"/>
<field name="usage">supplier</field>
<field name="company_id"></field>
</record>
<record id="stock_location_5" model="stock.location">
<field name="name">Generic IT Vendors</field>
<field name="location_id" ref="stock_location_3"/>
<field name="usage">supplier</field>
<field name="company_id"></field>
</record>
<record id="stock_location_7" model="stock.location">
<field name="name">European Customers</field>
<field name="location_id" ref="stock_location_customers"/>
<field name="usage">customer</field>
<field name="company_id"></field>
</record>
<record id="stock_location_8" model="stock.location">
<field name="name">Non European Customers</field>
<field name="location_id" ref="stock_location_customers"/>
<field name="usage">customer</field>
<field name="company_id"></field>
</record>
+5
View File
@@ -3332,6 +3332,11 @@ msgstr ""
msgid "One product only"
msgstr ""
#. module: stock
#: model:ir.ui.view,arch_db:stock.view_quant_package_form
msgid "Ongoing Package Creation: Content"
msgstr ""
#. module: stock
#: model:ir.model.fields,field_description:stock.field_stock_location_path__name
msgid "Operation Name"
+1 -1
View File
@@ -159,7 +159,7 @@ class Product(models.Model):
if self.env.context.get('company_owned', False):
company_id = self.env.user.company_id.id
return (
[('location_id.company_id', '=', company_id)],
[('location_id.company_id', '=', company_id), ('location_id.usage', 'in', ['internal', 'transit'])],
[('location_id.company_id', '=', False), ('location_dest_id.company_id', '=', company_id)],
[('location_id.company_id', '=', company_id), ('location_dest_id.company_id', '=', False),
])
+3 -3
View File
@@ -1033,7 +1033,7 @@ class StockMove(models.Model):
if move.propagate:
# only cancel the next move if all my siblings are also cancelled
if all(state == 'cancel' for state in siblings_states):
move.move_dest_ids._action_cancel()
move.move_dest_ids.filtered(lambda m: m.state != 'done')._action_cancel()
else:
if all(state in ('done', 'cancel') for state in siblings_states):
move.move_dest_ids.write({'procure_method': 'make_to_stock'})
@@ -1043,6 +1043,7 @@ class StockMove(models.Model):
def _prepare_extra_move_vals(self, qty):
vals = {
'origin_returned_move_id': self.origin_returned_move_id.id,
'product_uom_qty': qty,
'picking_id': self.picking_id.id,
'price_unit': self.price_unit,
@@ -1098,8 +1099,7 @@ class StockMove(models.Model):
def _action_done(self):
self.filtered(lambda move: move.state == 'draft')._action_confirm() # MRP allows scrapping draft moves
moves = self.filtered(lambda x: x.state not in ('done', 'cancel'))
moves = self.exists().filtered(lambda x: x.state not in ('done', 'cancel'))
moves_todo = self.env['stock.move']
# Cancel moves where necessary ; we should do it before creating the extra moves because
+1 -1
View File
@@ -460,7 +460,7 @@ class QuantPackage(models.Model):
return action
def _get_contained_quants(self):
return self.env['stock.quant'].search([('package_id', 'child_of', self.ids)])
return self.env['stock.quant'].search([('package_id', 'in', self.ids)])
def _get_all_products_quantities(self):
'''This function computes the different product quantities for the given package
+24
View File
@@ -185,6 +185,30 @@ class TestPickShip(TestStockCommon):
# the client picking should not be assigned anymore, as we returned partially what we took
self.assertEqual(picking_client.state, 'confirmed')
def test_mto_moves_return_extra(self):
picking_pick, picking_client = self.create_pick_ship()
stock_location = self.env['stock.location'].browse(self.stock_location)
self.env['stock.quant']._update_available_quantity(self.productA, stock_location, 10.0)
picking_pick.action_assign()
picking_pick.move_lines[0].move_line_ids[0].qty_done = 10.0
picking_pick.action_done()
self.assertEqual(picking_pick.state, 'done')
self.assertEqual(picking_client.state, 'assigned')
# return more than we've done
stock_return_picking = self.env['stock.return.picking']\
.with_context(active_ids=picking_pick.ids, active_id=picking_pick.ids[0])\
.create({})
stock_return_picking.product_return_moves.quantity = 12.0 # Return 2 extra
stock_return_picking_action = stock_return_picking.create_returns()
return_pick = self.env['stock.picking'].browse(stock_return_picking_action['res_id'])
# Verify the extra move has been merged with the original move
self.assertAlmostEqual(return_pick.move_lines.product_uom_qty, 12.0)
self.assertAlmostEqual(return_pick.move_lines.quantity_done, 0.0)
self.assertAlmostEqual(return_pick.move_lines.reserved_availability, 10.0)
def test_mto_resupply_cancel_ship(self):
""" This test simulates a pick pack ship with a resupply route
set. Pick and pack are validated, ship is cancelled. This test
+2 -2
View File
@@ -62,7 +62,7 @@ class TestWarehouse(TestStockCommon):
# Check inventory obj details (1 inventory with 1 line, because 1 product change)
inventory = self.env['stock.inventory'].search([('id', 'not in', self.existing_inventories.ids)])
self.assertEqual(len(inventory), 1)
self.assertIn('INV: %s' % self.product_1.name, inventory.name)
self.assertIn('INV: %s' % self.product_1.display_name, inventory.name)
self.assertEqual(len(inventory.line_ids), 1)
self.assertEqual(inventory.line_ids.product_id, self.product_1)
self.assertEqual(inventory.line_ids.product_qty, 50.0)
@@ -457,4 +457,4 @@ class TestResupply(TestStockCommon):
'product_min_qty': 10,
'product_max_qty': 100,
'product_uom': self.uom_unit.id,
})
})
+2 -1
View File
@@ -191,7 +191,7 @@
<field name="location_id"/>
</group>
</group>
<separator string="Content" />
<separator string="Ongoing Package Creation: Content" attrs="{'invisible': [('current_picking_id', '=', False)]}"/>
<field name="current_picking_id" invisible="1"/>
<field name="current_picking_move_line_ids" readonly="1" attrs="{'invisible': [('current_picking_id', '=', False)]}">
<tree>
@@ -201,6 +201,7 @@
<field name="product_uom_id" groups="uom.group_uom"/>
</tree>
</field>
<separator string="Content" attrs="{'invisible': [('current_picking_id', '=', True)]}"/>
<field name="quant_ids" attrs="{'invisible': [('current_picking_id', '=', True)]}">
<tree>
<field name="product_id"/>
+2 -1
View File
@@ -383,7 +383,8 @@ class StockMove(models.Model):
qty_done = 0.0
if float_is_zero(product_tot_qty_available, precision_rounding=rounding):
new_std_price = move._get_price_unit()
elif float_is_zero(product_tot_qty_available + move.product_qty, precision_rounding=rounding):
elif float_is_zero(product_tot_qty_available + move.product_qty, precision_rounding=rounding) or \
float_is_zero(product_tot_qty_available + qty_done, precision_rounding=rounding):
new_std_price = move._get_price_unit()
else:
# Get the standard price
@@ -3144,7 +3144,7 @@ class TestStockValuation(TransactionCase):
self.assertEqual(move1.remaining_value, 10)
self.assertEqual(move1.remaining_qty, 1)
self.assertAlmostEqual(self.product1.qty_available, 0.0)
self.assertAlmostEqual(self.product1.qty_at_date, 2.0)
self.assertAlmostEqual(self.product1.qty_at_date, 1.0)
self.assertEqual(self.product1.stock_value, 10)
self.assertTrue(len(move1.account_move_ids), 1)
@@ -107,9 +107,9 @@ class LandedCost(models.Model):
new_landed_cost_value = line.move_id.landed_cost_value + line.additional_landed_cost
line.move_id.write({
'landed_cost_value': new_landed_cost_value,
'value': line.move_id.value + cost_to_add,
'value': line.move_id.value + line.additional_landed_cost,
'remaining_value': line.move_id.remaining_value + cost_to_add,
'price_unit': (line.move_id.value + cost_to_add) / line.move_id.product_qty,
'price_unit': (line.move_id.value + line.additional_landed_cost) / line.move_id.product_qty,
})
# `remaining_qty` is negative if the move is out and delivered proudcts that were not
# in stock.
+288
View File
@@ -243,5 +243,293 @@ response = request.render("website.template_partner_comment", {
<field name="website_id" ref="website2"/>
<field name="page_id" ref="website2_contactus_page" />
</record>
<!-- BS Debug Page -->
<!-- Showcase all (most?) BS components and utilities -->
<record id="bs_debug_page" model="website.page">
<field name="name">BS Debug</field>
<field name="url">/website/demo/bootstrap</field>
<field name="website_published">False</field>
<field name="type">qweb</field>
<field name="key">website.bs_debug_page_view</field>
<field name="arch" type="xml">
<t name="Debug" t-name="website.bs_debug_page_view">
<t t-call="website.layout">
<t t-set="odoo_theme_colors" t-value="[['alpha', 'Alpha'], ['beta', 'Beta'], ['gamma', 'Gamma'], ['delta', 'Delta'], ['epsilon', 'Epsilon']]"/>
<t t-set="bs_basic_colors" t-value="[['primary', 'Primary'], ['default', 'Default']]"/>
<t t-set="bs_state_colors" t-value="[['success', 'Success'], ['info', 'Info'], ['warning', 'Warning'], ['danger', 'Danger']]"/>
<t t-set="bs_theme_colors" t-value="bs_basic_colors + bs_state_colors"/>
<t t-set="bs_gray_colors" t-value="[['white', 'White'], ['gray-lighter', 'Gray Lighter'], ['gray-light', 'Gray Light'], ['gray', 'Gray'], ['gray-dark', 'Gray Dark'], ['gray-darker', 'Gray Darker'], ['black', 'Black']]"/>
<div id="wrap oe_structure">
<section class="py-2">
<div class="container">
<h1>Components</h1>
<div class="row">
<div class="col-md-9">
<h2>Badge</h2>
<span class="badge mb4">This is a badge</span>
<h2 class="mt8">Label</h2>
<t t-foreach="bs_theme_colors" t-as="color">
<span t-attf-class="label mb4 label-#{color[0]}"><t t-esc="color[1]"/></span>
</t>
<h3 class="mt8 h5">Link</h3>
<t t-foreach="bs_theme_colors" t-as="color">
<a href="#" t-attf-class="label mb4 label-#{color[0]}"><t t-esc="color[1]"/></a>
</t>
<h3 class="mt8 h5">Autosizing</h3>
<div class="mt0 h3">
<t t-foreach="bs_theme_colors" t-as="color">
<span t-attf-class="label mb4 label-#{color[0]}"><t t-esc="color[1]"/></span>
</t>
</div>
<h2 class="mt16">Button</h2>
<t t-foreach="odoo_theme_colors" t-as="color">
<button type="button" t-attf-class="btn mb4 btn-#{color[0]}"><t t-esc="color[1]"/></button>
</t>
<br/>
<t t-foreach="bs_theme_colors" t-as="color">
<button type="button" t-attf-class="btn mb4 btn-#{color[0]}"><t t-esc="color[1]"/></button>
</t>
<h3 class="mt8 h6">Extra Small</h3>
<t t-foreach="odoo_theme_colors" t-as="color">
<button type="button" t-attf-class="btn mb4 btn-xs btn-#{color[0]}"><t t-esc="color[1]"/></button>
</t>
<br/>
<t t-foreach="bs_theme_colors" t-as="color">
<button type="button" t-attf-class="btn mb4 btn-xs btn-#{color[0]}"><t t-esc="color[1]"/></button>
</t>
<h3 class="mt8 h5">Small</h3>
<t t-foreach="odoo_theme_colors" t-as="color">
<button type="button" t-attf-class="btn mb4 btn-sm btn-#{color[0]}"><t t-esc="color[1]"/></button>
</t>
<br/>
<t t-foreach="bs_theme_colors" t-as="color">
<button type="button" t-attf-class="btn mb4 btn-sm btn-#{color[0]}"><t t-esc="color[1]"/></button>
</t>
<h3 class="mt8 h5">Large</h3>
<t t-foreach="odoo_theme_colors" t-as="color">
<button type="button" t-attf-class="btn mb4 btn-lg btn-#{color[0]}"><t t-esc="color[1]"/></button>
</t>
<br/>
<t t-foreach="bs_theme_colors" t-as="color">
<button type="button" t-attf-class="btn mb4 btn-lg btn-#{color[0]}"><t t-esc="color[1]"/></button>
</t>
<h2 class="mt16">Dropdown</h2>
<div class="dropdown">
<button type="button" class="btn btn-primary dropdown-toggle" data-toggle="dropdown">Toggle <span class="caret"/></button>
<ul class="dropdown-menu">
<li class="dropdown-header">Header</li>
<li><a href="#">Action</a></li>
<li><a href="#">Something else here</a></li>
<li class="divider"/>
<li><a href="#">Separated link</a></li>
</ul>
</div>
<h2 class="mt16">Navbar</h2>
<nav class="navbar navbar-default">
<div class="navbar-header">
<a class="navbar-brand" href="#">Navbar</a>
<button class="navbar-toggle collapsed" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
</div>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="nav navbar-nav">
<li class="active">
<a href="#">Home <span class="sr-only">(current)</span></a>
</li>
<li>
<a href="#">Link</a>
</li>
<li>
<a class="disabled" href="#">Disabled</a>
</li>
</ul>
<form class="navbar-form navbar-right">
<input class="form-control" type="search" placeholder="Search" aria-label="Search"/>
<button class="btn btn-success" type="submit">Search</button>
</form>
</div>
</nav>
<h2 class="mt16">Form</h2>
<form>
<div class="form-group">
<label for="exampleInputEmail1">Email address</label>
<input type="email" class="form-control" id="exampleInputEmail1" aria-describedby="emailHelp" placeholder="Enter email"/>
<small id="emailHelp" class="help-block text-muted">We'll never share your email with anyone else.</small>
</div>
</form>
<h2 class="mt16">Pagination</h2>
<nav>
<ul class="pagination">
<li class="disabled">
<a href="#" tabindex="-1">Previous</a>
</li>
<li>
<a href="#">1</a>
</li>
<li class="active">
<a href="#">2 <span class="sr-only">(current)</span></a>
</li>
<li>
<a href="#">3</a>
</li>
<li>
<a href="#">Next</a>
</li>
</ul>
</nav>
</div>
<div class="col-md-3">
<h2>Alert</h2>
<t t-foreach="bs_state_colors" t-as="color">
<div t-attf-class="alert alert-#{color[0]}">
This is a "<t t-esc="color[1]"/>" alert with a <a href="#" class="alert-link">link</a>.
</div>
</t>
<h2 class="mt16">Breadcrumb</h2>
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li><a href="#">Home</a></li>
<li><a href="#">Library</a></li>
<li class="active" aria-current="page">Data</li>
</ol>
</nav>
<h2 class="mt16">Card</h2>
<div class="panel panel-default">
<div class="panel-heading">
Card Header
</div>
<div class="panel-body">
Card Body
</div>
<ul class="list-group">
<li class="list-group-item">Item 1</li>
<li class="list-group-item">Item 2</li>
</ul>
<div class="panel-footer">
Card Footer
</div>
</div>
</div>
</div>
</div>
</section>
<section class="py-2">
<div class="container">
<h1>Utilities &amp; Typography</h1>
<div class="row">
<div class="col-md-9">
<div>
<t t-foreach="odoo_theme_colors" t-as="color">
<div t-attf-class="bg-#{color[0]}" style="display: inline-block; padding: 4px 12px;">
<t t-esc="color[1]"/>
</div>
</t>
</div>
<div class="mt8">
<t t-foreach="bs_theme_colors" t-as="color">
<div t-attf-class="bg-#{color[0]}" style="display: inline-block; padding: 4px 12px;">
<t t-esc="color[1]"/>
</div>
</t>
</div>
<div class="mt8">
<t t-foreach="bs_gray_colors" t-as="color">
<div t-attf-class="bg-#{color[0]}" style="display: inline-block; padding: 4px 12px;">
<t t-esc="color[1]"/>
</div>
</t>
</div>
<div class="mt16">
<t t-foreach="odoo_theme_colors" t-as="color">
<div t-attf-class="text-#{color[0]}" style="display: inline-block; padding: 4px 12px;">
<t t-esc="color[1]"/>
</div>
</t>
</div>
<div class="mt8">
<t t-foreach="bs_theme_colors" t-as="color">
<div t-attf-class="text-#{color[0]}" style="display: inline-block; padding: 4px 12px;">
<t t-esc="color[1]"/>
</div>
</t>
</div>
<div class="mt8">
<t t-foreach="bs_gray_colors" t-as="color">
<div t-attf-class="text-#{color[0]}" style="display: inline-block; padding: 4px 12px;">
<t t-esc="color[1]"/>
</div>
</t>
</div>
</div>
<div class="col-md-3">
<h1>Headings 1</h1>
<h2>Headings 2</h2>
<h3>Headings 3</h3>
<h4>Headings 4</h4>
<h5>Headings 5</h5>
<h6>Headings 6</h6>
<p>Paragraph with <strong>bold</strong>, <span class="text-muted">muted</span> and <em>italic</em> texts</p>
<p><a href="#">Link</a></p>
<p><button type="button" class="btn btn-link">Link button</button></p>
</div>
</div>
</div>
</section>
</div>
</t>
</t>
</field>
</record>
<!-- Snippet Debug Page -->
<!-- Showcase all snippets -->
<record id="snippets_debug_page" model="website.page">
<field name="name">Snippet Debug</field>
<field name="url">/website/demo/snippets</field>
<field name="website_published">False</field>
<field name="type">qweb</field>
<field name="key">website.snippets_debug_page_view</field>
<field name="arch" type="xml">
<t name="Debug" t-name="website.snippets_debug_page_view">
<t t-call="website.layout">
<style>
#snippets_menu, #o_scroll > .o_panel > .o_panel_header {
display: none !important;
}
[data-oe-type="snippet"]:not([data-module-id])::before {
content: attr(name);
display: block;
padding: 16px;
background-color: lightgray;
color: black;
font-size: 24px;
}
[data-oe-type="snippet"]:not([data-module-id])::after {
content: "";
display: table;
clear: both;
}
</style>
<div id="wrap oe_structure">
<t t-call="website.snippets"/>
</div>
</t>
</t>
</field>
</record>
</data>
</odoo>
@@ -699,6 +699,7 @@ registry.gallery = Animation.extend({
* @param {Event} ev
*/
_onClickImg: function (ev) {
var self = this;
var $cur = $(ev.currentTarget);
var urls = [];
@@ -743,7 +744,12 @@ registry.gallery = Animation.extend({
$modal.find('.modal-content, .modal-body.o_slideshow').css('height', '100%');
$modal.appendTo(document.body);
this.carousel = new registry.gallery_slider($modal.find('.carousel').carousel());
$modal.one('shown.bs.modal', function () {
self.trigger_up('animation_start_demand', {
editableMode: false,
$target: $modal.find('.modal-body.o_slideshow'),
});
});
},
});
+12 -9
View File
@@ -411,30 +411,27 @@ div.carousel .container {
border-width: 0;
position: absolute;
bottom: 0;
width: 100%;
margin-left: 0;
left: 0%;
> * {
list-style-image: none;
display: inline-block;
width: 40px;
height: 40px;
margin: 0 0px 5px 5px;
margin: 2.5px 2.5px 2.5px 2.5px;
padding: 0;
border: 1px solid #aaa;
text-indent: initial;
background-size: cover;
background-color: #fff;
border-radius: 0;
vertical-align: bottom;
&:not(.active) {
opacity: 0.8;
-webkit-filter: grayscale(100%);
filter: grayscale(100%);
filter: gray;
filter: url("data:image/svg+xml;utf8,<svg version='1.1' xmlns='http://www.w3.org/2000/svg' height='0'><filter id='greyscale'><feColorMatrix type='matrix' values='0.3333 0.3333 0.3333 0 0 0.3333 0.3333 0.3333 0 0 0.3333 0.3333 0.3333 0 0 0 0 0 1 0' /></filter></svg>#greyscale");
}
&:first-child {
margin-left: 0;
filter: grayscale(1);
}
}
}
@@ -444,6 +441,12 @@ div.carousel .container {
background-image: none;
background-color: transparent;
}
&.left * {
right: 0%;
}
&.right * {
left: 0%;
}
}
}
}
@@ -20,11 +20,11 @@
</div>
<ul class="carousel-indicators">
<li class="fa fa-chevron-left o_indicators_left" style="overflow: hidden; padding: 12px; border: 0;"></li>
<li class="fa fa-chevron-left o_indicators_left" style="overflow: hidden; padding: 12px; border: 0; visibility: hidden;"></li>
<t t-foreach="srcs" t-as="src">
<li t-attf-data-target="##{id}" t-att-data-slide-to="src_index" t-att-class="src_index == index and 'active'" t-attf-style="background-image: url(#{src})"></li>
<li t-attf-data-target="##{id}" t-att-data-slide-to="src_index" t-att-class="'hidden ' + (src_index == index and 'active')" t-attf-style="background-image: url(#{src})"></li>
</t>
<li class="fa fa-chevron-right o_indicators_right" style="overflow: hidden; padding: 12px; border: 0;"></li>
<li class="fa fa-chevron-right o_indicators_right" style="overflow: hidden; padding: 12px; border: 0; visibility: hidden;"></li>
</ul>
<a class="carousel-control left" t-attf-href="##{id}" data-slide="prev"><span class="fa fa-chevron-left" /></a>
+1 -1
View File
@@ -26,7 +26,7 @@ class Event(models.Model):
def _compute_is_participating(self):
# we don't allow public user to see participating label
if self.env.user != self.env.ref('base.public_user'):
if not self.env.user._is_public():
email = self.env.user.partner_id.email
for event in self:
domain = ['&', '|', ('email', '=', email), ('partner_id', '=', self.env.user.partner_id.id), ('event_id', '=', event.id)]
+7 -2
View File
@@ -17,8 +17,9 @@ class SaleOrder(models.Model):
_inherit = "sale.order"
website_order_line = fields.One2many(
'sale.order.line', 'order_id',
string='Order Lines displayed on Website', readonly=True,
'sale.order.line',
compute='_compute_website_order_line',
string='Order Lines displayed on Website',
help='Order Lines to be displayed on the website. They should not be used for computation purpose.',
)
cart_quantity = fields.Integer(compute='_compute_cart_info', string='Cart Quantity')
@@ -36,6 +37,10 @@ class SaleOrder(models.Model):
for order in self:
order.can_directly_mark_as_paid = order.state in ['sent', 'sale'] and order.payment_tx_id and order.payment_acquirer_id.provider in ['transfer', 'manual']
@api.one
def _compute_website_order_line(self):
self.website_order_line = self.order_line
@api.multi
@api.depends('website_order_line.product_uom_qty', 'website_order_line.product_id')
def _compute_cart_info(self):
+1 -1
View File
@@ -168,7 +168,7 @@ class Website(models.Model):
'partner_id': partner.id,
'pricelist_id': pricelist.id,
'payment_term_id': self.sale_get_payment_term(partner),
'team_id': self.salesteam_id.id,
'team_id': self.salesteam_id.id or partner.parent_id.team_id.id or partner.team_id.id,
'partner_invoice_id': addr['invoice'],
'partner_shipping_id': addr['delivery'],
'user_id': salesperson_id or self.salesperson_id.id or default_user_id,
@@ -17,11 +17,11 @@ class SaleOrder(models.Model):
has_delivery = fields.Boolean(
compute='_compute_has_delivery', string='Has delivery',
help="Has an order line set for delivery", store=True)
website_order_line = fields.One2many(
'sale.order.line', 'order_id',
string='Order Lines displayed on Website', readonly=True,
domain=[('is_delivery', '=', False)],
help='Order Lines to be displayed on the website. They should not be used for computation purpose.')
@api.one
def _compute_website_order_line(self):
super(SaleOrder, self)._compute_website_order_line()
self.website_order_line = self.website_order_line.filtered(lambda l: not l.is_delivery)
@api.depends('order_line.price_unit', 'order_line.tax_id', 'order_line.discount', 'order_line.product_uom_qty')
def _compute_amount_delivery(self):
+16
View File
@@ -0,0 +1,16 @@
India, 2018-09-18
Dreambits Technologies Pvt. Ltd. agrees to the terms of the Odoo Corporate Contributor License
Agreement v1.0.
I declare that I am authorized and able to make this agreement and sign this
declaration.
Signed,
Karan Shah admin@dreambits.in https://github.com/karan-dreambits
List of contributors:
Karan Shah admin@dreambits.in https://github.com/karan-dreambits
Devang Pipaliya devang@dreambits.in https://github.com/devang-dreambits
+2 -2
View File
@@ -56,7 +56,7 @@ Running the server
Maximum allowed virtual memory per worker. If the limit is exceeded,
the worker is killed and recycled at the end of the current request.
Defaults to 2048MB.
Defaults to 2048MiB.
.. option:: --limit-memory-hard <limit>
@@ -64,7 +64,7 @@ Running the server
immediately killed without waiting for the end of the current request
processing.
Defaults to 2560MB.
Defaults to 2560MiB.
.. option:: --limit-time-cpu <limit>
@@ -653,6 +653,7 @@
<field file="base/static/img/country_flags/gt.png" name="image" type="base64" />
<field name="currency_id" ref="GTQ" />
<field eval="502" name="phone_code" />
<field name="vat_label">NIT</field>
</record>
<record id="gu" model="res.country">
<field name="name">Guam</field>
@@ -1127,6 +1128,7 @@
<field name="currency_id" ref="MXN" />
<field eval="52" name="phone_code" />
<field name="vat_label">RFC</field>
<field eval="'%(street)s %(street2)s\n%(zip)s %(city)s, %(state_code)s\n%(country_name)s'" name="address_format" />
</record>
<record id="my" model="res.country">
<field name="name">Malaysia</field>
@@ -1732,6 +1734,7 @@
<field file="base/static/img/country_flags/vn.png" name="image" type="base64" />
<field name="currency_id" ref="VND" />
<field eval="84" name="phone_code" />
<field eval="'%(street)s\n%(street2)s\n%(city)s\n%(name)s %(zip)s %(country_name)s'" name="address_format" />
</record>
<record id="vu" model="res.country">
<field name="name">Vanuatu</field>
+3
View File
@@ -562,6 +562,9 @@ class IrActionsServer(models.Model):
active_id = self._context.get('active_id')
if not active_id and self._context.get('onchange_self'):
active_id = self._context['onchange_self']._origin.id
if not active_id: # onchange on new record
func = getattr(self, 'run_action_%s' % action.state)
res = func(action, eval_context=eval_context)
active_ids = self._context.get('active_ids', [active_id] if active_id else [])
for active_id in active_ids:
# run context dedicated to a particular active_id
+4 -2
View File
@@ -285,10 +285,12 @@ class configmanager(object):
help="Specify the number of workers, 0 disable prefork mode.",
type="int")
group.add_option("--limit-memory-soft", dest="limit_memory_soft", my_default=2048 * 1024 * 1024,
help="Maximum allowed virtual memory per worker, when reached the worker be reset after the current request (default 671088640 aka 640MB).",
help="Maximum allowed virtual memory per worker, when reached the worker be "
"reset after the current request (default 2048MiB).",
type="int")
group.add_option("--limit-memory-hard", dest="limit_memory_hard", my_default=2560 * 1024 * 1024,
help="Maximum allowed virtual memory per worker, when reached, any memory allocation will fail (default 805306368 aka 768MB).",
help="Maximum allowed virtual memory per worker, when reached, any memory "
"allocation will fail (default 2560MiB).",
type="int")
group.add_option("--limit-time-cpu", dest="limit_time_cpu", my_default=60,
help="Maximum allowed CPU time per request (default 60).",