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

This commit is contained in:
Christophe Simonis
2017-01-27 18:20:28 +01:00
20 changed files with 253 additions and 32 deletions
+1
View File
@@ -51,6 +51,7 @@
<field name="prefix">A</field>
<field eval="1" name="number_next"/>
<field eval="1" name="number_increment"/>
<field eval="False" name="company_id"/>
</record>
<!--
Invoice requests (deprecated)
+2 -1
View File
@@ -769,8 +769,9 @@ class AccountInvoice(models.Model):
will be grouped together if the journal has the 'group line' option. Of course a module
can add fields to invoice lines that would need to be tested too before merging lines
or not."""
return "%s-%s-%s-%s-%s-%s" % (
return "%s-%s-%s-%s-%s-%s-%s" % (
invoice_line['account_id'],
invoice_line.get('tax_ids', 'False'),
invoice_line.get('tax_line_id', 'False'),
invoice_line.get('product_id', 'False'),
invoice_line.get('analytic_account_id', 'False'),
@@ -61,7 +61,7 @@
<group>
<group>
<field name="product_id"
context="{'partner_id': parent.partner_id}"/>
context="parent and {'partner_id': parent.partner_id}"/>
<label for="quantity"/>
<div>
<field name="quantity" class="oe_inline"/>
+4
View File
@@ -193,6 +193,10 @@ class Employee(models.Model):
@api.multi
def write(self, vals):
if 'address_home_id' in vals:
account_id = vals.get('bank_account_id') or self.bank_account_id.id
if account_id:
self.env['res.partner.bank'].browse(account_id).partner_id = vals['address_home_id']
tools.image_resize_images(vals)
return super(Employee, self).write(vals)
+3 -2
View File
@@ -16,8 +16,9 @@ class Partner(models.Model):
try:
employee_group = self.env.ref('base.group_user')
for user in employee_group.users:
suggestions.append((user.partner_id.id, user.name, user.email))
hr_suggestions = [{'id': user.partner_id.id, 'name': user.name, 'email': user.email}
for user in employee_group.users]
suggestions.append(hr_suggestions)
return suggestions
except AccessError:
return suggestions
+1 -1
View File
@@ -80,7 +80,7 @@
<field name="country_id" options='{"no_open": True, "no_create": True}'/>
<field name="identification_id" groups="hr.group_hr_user"/>
<field name="passport_id" groups="hr.group_hr_user"/>
<field name="bank_account_id"/>
<field name="bank_account_id" context="{'default_partner_id': address_home_id}"/>
</group>
<group string="Contact Information">
<field name="address_home_id"
+1 -1
View File
@@ -82,7 +82,7 @@ class Usb(Escpos):
def _raw(self, msg):
""" Print any command sent in raw format """
if len(msg) != self.device.write(self.out_ep, msg, self.interface):
if len(msg) != self.device.write(self.out_ep, msg, self.interface, timeout=5000):
self.device.write(self.out_ep, self.errorText, self.interface)
raise TicketNotPrinted()
@@ -23,7 +23,7 @@ class AccountInvoice(models.Model):
def _get_reference_type(self):
"""Add BBA Structured Communication Type and change labels from 'reference' into 'communication' """
res = super(AccountInvoice, self)._get_reference_type()
res[[i for i, x in enumerate(res) if x[0] == 'none'][0]] = ('none', 'Free Communication')
res[[i for i, x in enumerate(res) if x[0] == 'none'][0]] = ('none', _('Free Communication'))
res.append(('bba', 'BBA Structured Communication'))
return res
+1 -1
View File
@@ -10,7 +10,7 @@ class TestMassMailing(models.TransientModel):
email_to = fields.Char(string='Recipients', required=True,
help='Comma-separated list of email addresses.', default=lambda self: self.env['mail.message']._get_default_from())
mass_mailing_id = fields.Many2one('mail.mass_mailing', string='Mailing', required=True)
mass_mailing_id = fields.Many2one('mail.mass_mailing', string='Mailing', required=True, ondelete='cascade')
@api.multi
def send_mail_test(self):
+9 -13
View File
@@ -316,22 +316,18 @@ class Picking(models.Model):
self.state = 'cancel'
elif all(move.state in ['cancel', 'done'] for move in self.move_lines):
self.state = 'done'
elif self.move_type == 'one':
ordered_moves = self.move_lines.filtered(
lambda move: move.state not in ['cancel', 'done']
).sorted(
key=lambda move: (move.state == 'assigned' and 2) or (move.state == 'waiting' and 1) or 0, reverse=False
)
self.state = ordered_moves[0].state
else:
filtered_moves = self.move_lines.filtered(lambda move: move.state not in ['cancel', 'done'])
if not all(move.state == 'assigned' for move in filtered_moves) and any(move.state == 'assigned' for move in filtered_moves):
self.state = 'partially_available'
elif any(move.partially_available for move in filtered_moves):
# We sort our moves by importance of state: "confirmed" should be first, then we'll have
# "waiting" and finally "assigned" at the end.
moves_todo = self.move_lines\
.filtered(lambda move: move.state not in ['cancel', 'done'])\
.sorted(key=lambda move: (move.state == 'assigned' and 2) or (move.state == 'waiting' and 1) or 0)
if self.move_type == 'one':
self.state = moves_todo[0].state
elif moves_todo[0].state != 'assigned' and any(x.partially_available or x.state == 'assigned' for x in moves_todo):
self.state = 'partially_available'
else:
ordered_moves = filtered_moves.sorted(key=lambda move: (move.state == 'assigned' and 2) or (move.state == 'waiting' and 1) or 0, reverse=True)
self.state = ordered_moves[0].state
self.state = moves_todo[-1].state
@api.one
@api.depends('move_lines.priority')
+191 -2
View File
@@ -1210,7 +1210,6 @@ class TestStockFlow(TestStockCommon):
inventory5.action_done()
self.assertEqual(inventory5_lines_count, inventory4_lines_count + 1, "The new product is not taken into account in the inventory valuation.")
self.assertTrue(product.id in inventory5.line_ids.mapped('product_id').ids, "The new product is not take into account in the inventory valuation.")
def test_30_check_with_no_incoming_lot(self):
""" Picking in without lots and picking out with"""
@@ -1278,7 +1277,6 @@ class TestStockFlow(TestStockCommon):
self.assertEqual(sum([x.qty for x in quants if x.lot_id.id == lot2.id]), 1.0, 'Wrong sum of quants with lot 2')
self.assertEqual(sum([x.qty for x in quants if x.lot_id.id == lot3.id]), 2.0, 'Wrong sum of quants with lot 3')
def test_40_pack_in_pack(self):
""" Put a pack in pack"""
picking_out = self.PickingObj.create({
@@ -1536,3 +1534,194 @@ class TestStockFlow(TestStockCommon):
# We should also make sure that when matching stock moves with pack operations, it takes the correct
self.assertEqual(len(picking_out.move_lines[0].linked_move_operation_ids), 2, 'We should only have 2 links beween the move and the 2 operations')
self.assertEqual(len(picking_out.move_lines[0].quant_ids), 2, 'We should have exactly 2 quants in the end')
def test_70_picking_state_all_at_once_reserve(self):
""" This test will check that the state of the picking is correctly computed according
to the state of its move lines and its move type.
"""
# move_type: direct == partial, one == all at once
# picking: confirmed == waiting availability, partially_available = partially available
# -----------------------------------------------------------
# "all at once" and "reserve" scenario
# -----------------------------------------------------------
# get one product in stock
inventory = self.env['stock.inventory'].create({
'name': 'Inventory Product Table',
'filter': 'partial',
'line_ids': [(0, 0, {
'product_id': self.productA.id,
'product_uom_id': self.productA.uom_id.id,
'product_qty': 1,
'location_id': self.stock_location
})]
})
inventory.action_done()
# create a "all at once" delivery order for two products
picking_out = self.PickingObj.create({
'partner_id': self.partner_agrolite_id,
'picking_type_id': self.picking_type_out,
'location_id': self.stock_location,
'location_dest_id': self.customer_location})
picking_out.move_type = 'one'
self.MoveObj.create({
'name': self.productA.name,
'product_id': self.productA.id,
'product_uom_qty': 2,
'product_uom': self.productA.uom_id.id,
'picking_id': picking_out.id,
'location_id': self.stock_location,
'location_dest_id': self.customer_location})
# validate this delivery order, it should be in the waiting state
picking_out.action_assign()
self.assertEquals(picking_out.state, "confirmed")
# receive one product in stock
inventory = self.env['stock.inventory'].create({
'name': 'Inventory Product Table',
'filter': 'partial',
'line_ids': [(0, 0, {
'product_id': self.productA.id,
'product_uom_id': self.productA.uom_id.id,
'product_qty': 2,
'location_id': self.stock_location
})]
})
inventory.action_done()
# recheck availability of the delivery order, it should be assigned
picking_out.action_assign()
self.assertEquals(picking_out.state, "assigned")
def test_71_picking_state_all_at_once_force_assign(self):
""" This test will check that the state of the picking is correctly computed according
to the state of its move lines and its move type.
"""
# move_type: direct == partial, one == all at once
# picking: confirmed == waiting availability, partially_available = partially available
# -----------------------------------------------------------
# "all at once" and "force assign" scenario
# -----------------------------------------------------------
# create a "all at once" delivery order for two products
picking_out = self.PickingObj.create({
'partner_id': self.partner_agrolite_id,
'picking_type_id': self.picking_type_out,
'location_id': self.stock_location,
'location_dest_id': self.customer_location})
picking_out.move_type = 'direct'
self.MoveObj.create({
'name': self.productA.name,
'product_id': self.productA.id,
'product_uom_qty': 2,
'product_uom': self.productA.uom_id.id,
'picking_id': picking_out.id,
'location_id': self.stock_location,
'location_dest_id': self.customer_location})
# validate this delivery order, it should be in the waiting state
picking_out.action_assign()
self.assertEquals(picking_out.state, "confirmed")
# force assign on the delivery order, it should be assigned
picking_out.force_assign()
self.assertEquals(picking_out.state, "assigned")
def test_72_picking_state_partial_reserve(self):
""" This test will check that the state of the picking is correctly computed according
to the state of its move lines and its move type.
"""
# move_type: direct == partial, one == all at once
# picking: confirmed == waiting availability, partially_available = partially available
# -----------------------------------------------------------
# "partial" and "reserve" scenario
# -----------------------------------------------------------
# get one product in stock
inventory = self.env['stock.inventory'].create({
'name': 'Inventory Product Table',
'filter': 'partial',
'line_ids': [(0, 0, {
'product_id': self.productA.id,
'product_uom_id': self.productA.uom_id.id,
'product_qty': 1,
'location_id': self.stock_location
})]
})
inventory.action_done()
# create a "partial" delivery order for two products
picking_out = self.PickingObj.create({
'partner_id': self.partner_agrolite_id,
'picking_type_id': self.picking_type_out,
'location_id': self.stock_location,
'location_dest_id': self.customer_location})
picking_out.move_type = 'direct'
self.MoveObj.create({
'name': self.productA.name,
'product_id': self.productA.id,
'product_uom_qty': 2,
'product_uom': self.productA.uom_id.id,
'picking_id': picking_out.id,
'location_id': self.stock_location,
'location_dest_id': self.customer_location})
# validate this delivery order, it should be in partially available
picking_out.action_assign()
self.assertEquals(picking_out.state, "partially_available")
# receive one product in stock
inventory = self.env['stock.inventory'].create({
'name': 'Inventory Product Table',
'filter': 'partial',
'line_ids': [(0, 0, {
'product_id': self.productA.id,
'product_uom_id': self.productA.uom_id.id,
'product_qty': 2,
'location_id': self.stock_location
})]
})
inventory.action_done()
# recheck availability of the delivery order, it should be assigned
picking_out.action_assign()
self.assertEquals(picking_out.state, "assigned")
def test_73_picking_state_partial_force_assign(self):
""" This test will check that the state of the picking is correctly computed according
to the state of its move lines and its move type.
"""
# move_type: direct == partial, one == all at once
# picking: confirmed == waiting availability, partially_available = partially available
# -----------------------------------------------------------
# "partial" and "force assign" scenario
# -----------------------------------------------------------
picking_out = self.PickingObj.create({
'partner_id': self.partner_agrolite_id,
'picking_type_id': self.picking_type_out,
'location_id': self.stock_location,
'location_dest_id': self.customer_location})
picking_out.move_type = 'direct'
self.MoveObj.create({
'name': self.productA.name,
'product_id': self.productA.id,
'product_uom_qty': 2,
'product_uom': self.productA.uom_id.id,
'picking_id': picking_out.id,
'location_id': self.stock_location,
'location_dest_id': self.customer_location})
# validate this delivery order, it should be in the waiting state
picking_out.action_assign()
self.assertEquals(picking_out.state, "confirmed")
# force assign on the delivery order, it should be assigned
picking_out.force_assign()
self.assertEquals(picking_out.state, "assigned")
+4 -3
View File
@@ -40,15 +40,15 @@
<table class="o_group o_inner_group o_label_nowrap">
<tr>
<td class="o_td_label"><label for="old_pwd" class="o_form_label">Old Password</label></td>
<td width="100%"><input type="password" class="o_form_field o_form_input" name="old_pwd" minlength="1" autofocus="autofocus"/></td>
<td width="100%"><input type="password" class="o_form_field o_form_input" name="old_pwd" minlength="1" autofocus="autofocus" autocomplete="current-password"/></td>
</tr>
<tr>
<td class="o_td_label"><label for="new_password" class="o_form_label">New Password</label></td>
<td width="100%"><input type="password" class="o_form_field o_form_input" name="new_password" minlength="1"/></td>
<td width="100%"><input type="password" class="o_form_field o_form_input" name="new_password" minlength="1" autocomplete="new-password"/></td>
</tr>
<tr>
<td class="o_td_label"><label for="confirm_pwd" class="o_form_label">Confirm New Password</label></td>
<td width="100%"><input type="password" class="o_form_field o_form_input" name="confirm_pwd" minlength="1"/></td>
<td width="100%"><input type="password" class="o_form_field o_form_input" name="confirm_pwd" minlength="1" autocomplete="new-password"/></td>
</tr>
</table>
@@ -625,6 +625,7 @@
t-att-tabindex="widget.node.attrs.tabindex"
t-att-autofocus="widget.node.attrs.autofocus"
t-att-placeholder="widget.node.attrs.placeholder"
t-att-autocomplete="widget.password ? 'new-password' : widget.node.attrs.autocomplete"
t-att-maxlength="widget.field.size"/>
</t>
<t t-name="FormSelection">
+1 -1
View File
@@ -262,7 +262,7 @@
<div class="form-group field-password">
<label for="password" class="control-label">Password</label>
<input type="password" name="password" id="password" class="form-control" required="required" t-att-autofocus="'autofocus' if login else None" maxlength="4096"/>
<input type="password" name="password" id="password" class="form-control" required="required" autocomplete="current-password" t-att-autofocus="'autofocus' if login else None" maxlength="4096"/>
</div>
<p class="alert alert-danger" t-if="error">
@@ -6,10 +6,6 @@ var website = require('website.website');
var base = require('web_editor.base');
require('website_sale.website_sale');
if(!$('.js_add_cart_variants[data-attribute_value_ids]').length) {
return $.Deferred().reject("DOM doesn't contain '.js_add_cart_variants[data-attribute_value_ids]'");
}
$('.oe_website_sale #add_to_cart, .oe_website_sale #products_grid .a-submit')
.off('click')
.removeClass('a-submit')
@@ -28,9 +24,14 @@ $('.oe_website_sale #add_to_cart, .oe_website_sale #products_grid .a-submit')
$modal.find('img:first').attr("src", "/web/image/product.product/" + product_id + "/image_medium");
// disable opacity on the <form> if currently active (in case the product is
// not published), as it interferes with bs modals
$form.addClass('css_options');
$modal.appendTo($form)
.modal()
.on('hidden.bs.modal', function () {
$form.removeClass('css_options'); // possibly reactivate opacity (see above)
$(this).remove();
});
+1
View File
@@ -31,3 +31,4 @@ Cyril Gaudin cyril.gaudin@camptocamp.com https://github.com/cyrilgdn
Denis Leemann denis.leemann@camptocamp.com https://github.com/leemannd
Akim Juillerat akim.juillerat@camptocamp.com https://github.com/grindtildeath
Damien Crier damien.crier@camptocamp.com https://github.com/damdam-s
Simone Orsi simahawk@gmail.com https://github.com/simahawk
+1
View File
@@ -11,3 +11,4 @@ Laurent Destailleur ldestailleur@teclib.com https://github.com/eldy
List of contributors:
Laurent Destailleur ldestailleur@teclib.com https://github.com/eldy
David Halgand dhalgand@teclib.com https://github.com/halgandd
+11
View File
@@ -0,0 +1,11 @@
Nantes, 2016-11-04
I hereby agree to the terms of the Odoo Individual Contributor License
Agreement v1.0.
I declare that I am authorized and able to make this agreement and sign this
declaration.
Signed,
David Halgand david.halgand@gmail.com https://github.com/halgandd
+1
View File
@@ -956,6 +956,7 @@ class IrActionsServer(models.Model):
:return: an action_id to be executed, or False is finished correctly without
return action
"""
res = False
for action in self:
eval_context = self._get_eval_context(action)
condition = action.condition
@@ -77,6 +77,12 @@ class test_inherits(common.TransactionCase):
self.assertIn(daughter, partner_demo.daughter_ids)
# search the partner from the daughter record
partners = self.env['res.partner'].search([('daughter_ids', 'like', 'not existing daugther')])
self.assertFalse(partners)
partners = self.env['res.partner'].search([('daughter_ids', 'not like', 'not existing daugther')])
self.assertIn(partner_demo, partners)
partners = self.env['res.partner'].search([('daughter_ids', '!=', False)])
self.assertIn(partner_demo, partners)
partners = self.env['res.partner'].search([('daughter_ids', 'in', daughter.ids)])
self.assertIn(partner_demo, partners)
+8 -1
View File
@@ -957,7 +957,14 @@ class expression(object):
if call_null:
o2m_op = 'in' if operator in NEGATIVE_TERM_OPERATORS else 'not in'
push(create_substitution_leaf(leaf, ('id', o2m_op, select_distinct_from_where_not_null(cr, field.inverse_name, comodel._table)), model))
# determine ids from field.inverse_name
if comodel._fields[field.inverse_name].store:
ids1 = select_distinct_from_where_not_null(cr, field.inverse_name, comodel._table)
else:
domain = [(field.inverse_name, '!=', False)]
recs = comodel.search(domain).sudo().with_context(prefetch_fields=False)
ids1 = recs.mapped(field.inverse_name).ids
push(create_substitution_leaf(leaf, ('id', o2m_op, ids1), model))
elif field.type == 'many2many':
rel_table, rel_id1, rel_id2 = field.relation, field.column1, field.column2