[MERGE] forward port branch saas-14 up to c43c540172
This commit is contained in:
@@ -441,9 +441,14 @@ class EventRegistration(models.Model):
|
||||
@api.multi
|
||||
def message_get_suggested_recipients(self):
|
||||
recipients = super(EventRegistration, self).message_get_suggested_recipients()
|
||||
public_users = self.env['res.users'].sudo()
|
||||
public_groups = self.env.ref("base.group_public", raise_if_not_found=False)
|
||||
if public_groups:
|
||||
public_users = public_groups.sudo().with_context(active_test=False).mapped("users")
|
||||
try:
|
||||
for attendee in self:
|
||||
if attendee.partner_id:
|
||||
is_public = attendee.sudo().with_context(active_test=False).partner_id.user_ids in public_users if public_users else False
|
||||
if attendee.partner_id and not is_public:
|
||||
attendee._message_add_suggested_recipient(recipients, partner=attendee.partner_id, reason=_('Customer'))
|
||||
elif attendee.email:
|
||||
attendee._message_add_suggested_recipient(recipients, email=attendee.email, reason=_('Customer Email'))
|
||||
|
||||
@@ -549,7 +549,7 @@
|
||||
<field name="refund_account_id" ref="vat_refund_h_non_eu"/>
|
||||
<field name="account_id" ref="vat_refund_h_non_eu"/>
|
||||
<field name="type_tax_use">none</field>
|
||||
<field name="tag_ids" eval="[(6,0,[ref('tag_nl_34')])]"/>
|
||||
<field name="tag_ids" eval="[(6,0,[ref('tag_nl_33')])]"/>
|
||||
</record>
|
||||
<record id="btw_E2" model="account.tax.template">
|
||||
<field name="sequence">20</field>
|
||||
@@ -680,7 +680,7 @@
|
||||
<field name="refund_account_id" ref="vat_refund_h_d_non_eu"/>
|
||||
<field name="account_id" ref="vat_refund_h_d_non_eu"/>
|
||||
<field name="type_tax_use">none</field>
|
||||
<field name="tag_ids" eval="[(6,0,[ref('tag_nl_34')])]"/>
|
||||
<field name="tag_ids" eval="[(6,0,[ref('tag_nl_33')])]"/>
|
||||
</record>
|
||||
<record id="btw_E2_d" model="account.tax.template">
|
||||
<field name="sequence">20</field>
|
||||
|
||||
@@ -248,19 +248,20 @@ class link_tracker_click(models.Model):
|
||||
|
||||
vals = {
|
||||
'link_id': code_rec.link_id.id,
|
||||
'create_date': datetime.date.today(),
|
||||
'ip': ip,
|
||||
'country_id': country_record.id,
|
||||
'mail_stat_id': stat_id
|
||||
}
|
||||
|
||||
if stat_id:
|
||||
mail_stat = self.env['mail.mail.statistics'].search([('id', '=', stat_id)])
|
||||
# It could happen that the related ID is no longer available, but we still want the link to work
|
||||
if mail_stat.exists():
|
||||
vals['mail_stat_id'] = stat_id
|
||||
|
||||
if mail_stat.mass_mailing_campaign_id:
|
||||
vals['mass_mailing_campaign_id'] = mail_stat.mass_mailing_campaign_id.id
|
||||
if mail_stat.mass_mailing_campaign_id:
|
||||
vals['mass_mailing_campaign_id'] = mail_stat.mass_mailing_campaign_id.id
|
||||
|
||||
if mail_stat.mass_mailing_id:
|
||||
vals['mass_mailing_id'] = mail_stat.mass_mailing_id.id
|
||||
if mail_stat.mass_mailing_id:
|
||||
vals['mass_mailing_id'] = mail_stat.mass_mailing_id.id
|
||||
|
||||
self.create(vals)
|
||||
|
||||
@@ -48,7 +48,7 @@ class OgoneController(http.Controller):
|
||||
def feedback(self, **kwargs):
|
||||
try:
|
||||
tx = request.env['payment.transaction'].sudo()._ogone_form_get_tx_from_data(kwargs)
|
||||
tx._ogone_s2s_validate()
|
||||
tx._ogone_s2s_validate_tree(kwargs)
|
||||
except ValidationError:
|
||||
return 'ko'
|
||||
return 'ok'
|
||||
|
||||
@@ -667,7 +667,7 @@ class SaleOrderLine(models.Model):
|
||||
if line.state != 'sale' or not line.product_id._need_procurement():
|
||||
continue
|
||||
qty = 0.0
|
||||
for proc in line.procurement_ids:
|
||||
for proc in line.procurement_ids.filtered(lambda r: r.state != 'cancel'):
|
||||
qty += proc.product_qty
|
||||
if float_compare(qty, line.product_uom_qty, precision_digits=precision) >= 0:
|
||||
continue
|
||||
|
||||
@@ -80,16 +80,17 @@ class StockScrap(models.Model):
|
||||
for scrap in self:
|
||||
moves = scrap._get_origin_moves() or self.env['stock.move']
|
||||
move = self.env['stock.move'].create(scrap._prepare_move_values())
|
||||
quants = self.env['stock.quant'].quants_get_preferred_domain(
|
||||
move.product_qty, move,
|
||||
domain=[
|
||||
('qty', '>', 0),
|
||||
('lot_id', '=', self.lot_id.id),
|
||||
('package_id', '=', self.package_id.id)],
|
||||
preferred_domain_list=scrap._get_preferred_domain())
|
||||
if any([not x[0] for x in quants]):
|
||||
raise UserError(_('You cannot scrap a move without having available stock for %s. You can correct it with an inventory adjustment.') % move.product_id.name)
|
||||
self.env['stock.quant'].quants_reserve(quants, move)
|
||||
if move.product_id.type == 'product':
|
||||
quants = self.env['stock.quant'].quants_get_preferred_domain(
|
||||
move.product_qty, move,
|
||||
domain=[
|
||||
('qty', '>', 0),
|
||||
('lot_id', '=', self.lot_id.id),
|
||||
('package_id', '=', self.package_id.id)],
|
||||
preferred_domain_list=scrap._get_preferred_domain())
|
||||
if any([not x[0] for x in quants]):
|
||||
raise UserError(_('You cannot scrap a move without having available stock for %s. You can correct it with an inventory adjustment.') % move.product_id.name)
|
||||
self.env['stock.quant'].quants_reserve(quants, move)
|
||||
move.action_done()
|
||||
scrap.write({'move_id': move.id, 'state': 'done'})
|
||||
moves.recalculate_move_state()
|
||||
|
||||
@@ -4,3 +4,4 @@ from . import test_stock_flow
|
||||
import test_product
|
||||
import test_warehouse
|
||||
import test_shipment
|
||||
import test_stock_scrap
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from openerp.tests.common import TransactionCase
|
||||
from openerp.exceptions import UserError
|
||||
|
||||
|
||||
class TestStockScrap(TransactionCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestStockScrap, self).setUp()
|
||||
self.user_demo = self.env.ref('base.user_demo')
|
||||
self.scrap_obj = self.env['stock.scrap']
|
||||
self.picking_obj = self.env['stock.picking']
|
||||
self.stock_loc = self.browse_ref('stock.stock_location_stock')
|
||||
self.customer_loc = self.browse_ref('stock.stock_location_customers')
|
||||
|
||||
# Stockable product
|
||||
self.product_stockable = self.env['product.product'].create({
|
||||
'type': 'product',
|
||||
'name': 'Stockable Product',
|
||||
'uom_id': self.env.ref('product.product_uom_unit').id,
|
||||
'uom_po_id': self.env.ref('product.product_uom_unit').id,
|
||||
})
|
||||
|
||||
# Consumable product
|
||||
self.product_consumable = self.env['product.product'].create({
|
||||
'type': 'consu',
|
||||
'name': 'Consumable Product',
|
||||
'uom_id': self.env.ref('product.product_uom_unit').id,
|
||||
'uom_po_id': self.env.ref('product.product_uom_unit').id,
|
||||
})
|
||||
|
||||
self._update_product_qty(self.product_stockable)
|
||||
|
||||
def _update_product_qty(self, product):
|
||||
product_qty = self.env['stock.change.product.qty'].create({
|
||||
'location_id': self.stock_loc.id,
|
||||
'product_id': product.id,
|
||||
'new_quantity': 100.0,
|
||||
})
|
||||
product_qty.change_product_qty()
|
||||
return product_qty
|
||||
|
||||
def _prepare_picking(self, product):
|
||||
picking = self.picking_obj.create({
|
||||
'name': 'picking - test',
|
||||
'location_id': self.stock_loc.id,
|
||||
'location_dest_id': self.customer_loc.id,
|
||||
'picking_type_id': self.ref('stock.picking_type_out'),
|
||||
'move_lines': [(0, 0, {
|
||||
'name': product.name,
|
||||
'product_id': product.id,
|
||||
'product_uom_qty': 20.0,
|
||||
'product_uom': product.uom_id.id,
|
||||
})]
|
||||
})
|
||||
return picking
|
||||
|
||||
def test_stock_scrap_01(self):
|
||||
""" Standalone scrap a stockable product """
|
||||
with self.assertRaises(UserError):
|
||||
self.scrap_obj.create({
|
||||
'name': 'scrap - grand test',
|
||||
'product_id': self.product_stockable.id,
|
||||
'product_uom_id': self.ref('product.product_uom_unit'),
|
||||
'scrap_qty': 120,
|
||||
})
|
||||
self.assertEquals(self.product_stockable.qty_available, 100.0)
|
||||
|
||||
def test_stock_scrap_02(self):
|
||||
""" Scrap a stockable product from a picking """
|
||||
picking = self._prepare_picking(self.product_stockable)
|
||||
scrap = self.scrap_obj.create({
|
||||
'name': 'scrap - grand test',
|
||||
'product_id': self.product_stockable.id,
|
||||
'product_uom_id': self.ref('product.product_uom_unit'),
|
||||
'picking_id': picking.id,
|
||||
'scrap_qty': 80,
|
||||
})
|
||||
self.assertEquals(scrap.state, 'done')
|
||||
self.assertEquals(self.product_stockable.qty_available, 20.0)
|
||||
|
||||
def test_stock_scrap_03(self):
|
||||
""" Standalone scrap a consumable product """
|
||||
scrap = self.scrap_obj.create({
|
||||
'name': 'scrap - grand test',
|
||||
'product_id': self.product_consumable.id,
|
||||
'product_uom_id': self.ref('product.product_uom_unit'),
|
||||
'scrap_qty': 100,
|
||||
})
|
||||
self.assertEquals(scrap.state, 'done')
|
||||
self.assertEquals(self.product_consumable.qty_available, -100.0)
|
||||
with self.assertRaises(UserError):
|
||||
scrap.sudo(self.user_demo).unlink()
|
||||
|
||||
def test_stock_scrap_04(self):
|
||||
""" Scrap a consumable product from a picking """
|
||||
picking = self._prepare_picking(self.product_consumable)
|
||||
scrap = self.scrap_obj.create({
|
||||
'name': 'scrap - grand test',
|
||||
'product_id': self.product_consumable.id,
|
||||
'product_uom_id': self.ref('product.product_uom_unit'),
|
||||
'picking_id': picking.id,
|
||||
'scrap_qty': 100,
|
||||
})
|
||||
self.assertEquals(scrap.state, 'done')
|
||||
self.assertEquals(self.product_consumable.qty_available, -100.0)
|
||||
@@ -0,0 +1,26 @@
|
||||
<templates>
|
||||
<t t-name="date-simple"><t t-esc='value' /></t>
|
||||
<params id="date-simple">{"value": "1988-09-16"}</params>
|
||||
<result id="date-simple">1988-09-16</result>
|
||||
|
||||
<t t-name="datetime-simple"><t t-esc='value' /></t>
|
||||
<params id="datetime-simple">{"value": "1988-09-16 14:00:00"}</params>
|
||||
<result id="datetime-simple">1988-09-16 14:00:00</result>
|
||||
|
||||
<t t-name="datetime-widget-datetime"><t t-esc='value' t-options="{'widget': 'datetime'}" /></t>
|
||||
<params id="datetime-widget-datetime">{"value": "1988-09-16 14:00:00"}</params>
|
||||
<result id="datetime-widget-datetime">09/16/1988 16:00:00</result>
|
||||
|
||||
<t t-name="datetime-widget-date"><t t-esc='value' t-options="{'widget': 'date'}" /></t>
|
||||
<params id="datetime-widget-date">{"value": "1988-09-16 14:00:00"}</params>
|
||||
<result id="datetime-widget-date">09/16/1988</result>
|
||||
|
||||
<t t-name="datetime-widget-date-tz2"><t t-esc='value' t-options="{'widget': 'date'}" /></t>
|
||||
<params id="datetime-widget-date-tz2">{"value": "1988-09-16 01:00:00"}</params>
|
||||
<result id="datetime-widget-date-tz2">09/16/1988</result>
|
||||
|
||||
<t t-name="datetime-widget-date-tz"><t t-esc='value' t-options="{'widget': 'date'}" /></t>
|
||||
<params id="datetime-widget-date-tz">{"value": "1988-09-16 23:00:00"}</params>
|
||||
<result id="datetime-widget-date-tz">09/17/1988</result>
|
||||
|
||||
</templates>
|
||||
@@ -14,6 +14,7 @@ List of contributors:
|
||||
|
||||
Aarón Henríquez ahenriquez@eficent.com https://github.com/aheficent
|
||||
Lois Rilo lois.rilo@eficent.com https://github.com/lreficent
|
||||
Gisela Mora gisela.mora@eficent.com https://github.com/gmeficent
|
||||
Miquel Raich miquel.raich@eficent.com https://github.com/mreficent
|
||||
Jordi Ballester jordi.ballester@eficent.com https://github.com/jbeficent
|
||||
Hector Villarreal hector.villarreal@eficent.com https://github.com/hveficent
|
||||
Roser Garcia roser.garcia@eficent.com https://github.com/rgarnau (up to 2018-05-01)
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
Nepal, 2018-02-09
|
||||
|
||||
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,
|
||||
|
||||
Subodh Dahal contact.subodhdahal@gmail.com https://github.com/SubodhDahal
|
||||
@@ -614,7 +614,7 @@ The previous code will create the snippet’s content, but we still need to plac
|
||||
.. code-block:: xml
|
||||
|
||||
<template id="place_into_bar" inherit_id="website.snippets" name="Place into bar">
|
||||
<xpath expr="//div[@id='snippet_content']/div[@class='o_panel_body']" position="inside">
|
||||
<xpath expr="//div[@id='snippet_structure']/div[@class='o_panel_body']" position="inside">
|
||||
<t t-snippet="theme_tutorial.snippet_testimonial"
|
||||
t-thumbnail="/theme_tutorial/static/src/img/ui/snippet_thumb.jpg"/>
|
||||
</xpath>
|
||||
|
||||
@@ -32,7 +32,7 @@ SAFE_EVAL_BASE = {
|
||||
def make_compute(text, deps):
|
||||
""" Return a compute function from its code body and dependencies. """
|
||||
func = lambda self: safe_eval(text, SAFE_EVAL_BASE, {'self': self}, mode="exec")
|
||||
deps = [arg.strip() for arg in (deps or "").split(",")]
|
||||
deps = [arg.strip() for arg in deps.split(",")] if deps else []
|
||||
return api.depends(*deps)(func)
|
||||
|
||||
|
||||
|
||||
@@ -61,6 +61,19 @@ class TestFields(common.TransactionCase):
|
||||
self.assertTrue(field.store)
|
||||
self.assertTrue(field.readonly)
|
||||
|
||||
def test_10_computed_custom(self):
|
||||
""" check definition of custom computed fields """
|
||||
self.env['ir.model.fields'].create({
|
||||
'name': 'x_bool_false_computed',
|
||||
'model_id': self.env.ref('test_new_api.model_test_new_api_message').id,
|
||||
'field_description': 'A boolean computed to false',
|
||||
'compute': "for r in self: r['x_bool_false_computed'] = False",
|
||||
'store': False,
|
||||
'ttype': 'boolean'
|
||||
})
|
||||
field = self.env['test_new_api.message']._fields['x_bool_false_computed']
|
||||
self.assertFalse(field.depends)
|
||||
|
||||
def test_10_non_stored(self):
|
||||
""" test non-stored fields """
|
||||
# a field declared with store=False should not have a column
|
||||
|
||||
+6
-4
@@ -1047,7 +1047,7 @@ class OpenERPSession(werkzeug.contrib.sessions.Session):
|
||||
self.db = db
|
||||
self.uid = uid
|
||||
self.login = login
|
||||
self.session_token = uid and security.compute_session_token(self)
|
||||
self.session_token = uid and security.compute_session_token(self, request.env)
|
||||
request.uid = uid
|
||||
request.disable_db = False
|
||||
|
||||
@@ -1062,15 +1062,17 @@ class OpenERPSession(werkzeug.contrib.sessions.Session):
|
||||
"""
|
||||
if not self.db or not self.uid:
|
||||
raise SessionExpiredException("Session expired")
|
||||
|
||||
# We create our own environment instead of the request's one.
|
||||
# to avoid creating it without the uid since request.uid isn't set yet
|
||||
env = odoo.api.Environment(request.cr, self.uid, self.context)
|
||||
# == BACKWARD COMPATIBILITY TO CONVERT OLD SESSION TYPE TO THE NEW ONES ! REMOVE ME AFTER 11.0 ==
|
||||
if self.get('password'):
|
||||
security.check(self.db, self.uid, self.password)
|
||||
self.session_token = security.compute_session_token(self)
|
||||
self.session_token = security.compute_session_token(self, env)
|
||||
self.pop('password')
|
||||
# =================================================================================================
|
||||
# here we check if the session is still valid
|
||||
if not security.check_session(self):
|
||||
if not security.check_session(self, env):
|
||||
raise SessionExpiredException("Session expired")
|
||||
|
||||
def logout(self, keep_db=False):
|
||||
|
||||
@@ -12,15 +12,13 @@ def check(db, uid, passwd):
|
||||
res_users = odoo.registry(db)['res.users']
|
||||
return res_users.check(db, uid, passwd)
|
||||
|
||||
def compute_session_token(session):
|
||||
with odoo.registry(session.db).cursor() as cr:
|
||||
self = odoo.api.Environment(cr, session.uid, {})['res.users'].browse(session.uid)
|
||||
return self._compute_session_token(session.sid)
|
||||
def compute_session_token(session, env):
|
||||
self = env['res.users'].browse(session.uid)
|
||||
return self._compute_session_token(session.sid)
|
||||
|
||||
def check_session(session):
|
||||
with odoo.registry(session.db).cursor() as cr:
|
||||
self = odoo.api.Environment(cr, session.uid, {})['res.users'].browse(session.uid)
|
||||
if odoo.tools.misc.consteq(self._compute_session_token(session.sid), session.session_token):
|
||||
return True
|
||||
self._invalidate_session_cache()
|
||||
return False
|
||||
def check_session(session, env):
|
||||
self = env['res.users'].browse(session.uid)
|
||||
if odoo.tools.misc.consteq(self._compute_session_token(session.sid), session.session_token):
|
||||
return True
|
||||
self._invalidate_session_cache()
|
||||
return False
|
||||
|
||||
@@ -287,7 +287,7 @@ class HttpCase(TransactionCase):
|
||||
session.db = db
|
||||
session.uid = uid
|
||||
session.login = user
|
||||
session.session_token = uid and security.compute_session_token(session)
|
||||
session.session_token = uid and security.compute_session_token(session, env)
|
||||
session.context = env['res.users'].context_get() or {}
|
||||
session.context['uid'] = uid
|
||||
session._fix_lang(session.context)
|
||||
|
||||
+7
-5
@@ -1209,13 +1209,15 @@ def format_date(env, value, lang_code=False, date_format=False):
|
||||
'''
|
||||
if not value:
|
||||
return ''
|
||||
if isinstance(value, datetime.datetime):
|
||||
value = value.date()
|
||||
elif isinstance(value, basestring):
|
||||
if isinstance(value, basestring):
|
||||
if len(value) < DATE_LENGTH:
|
||||
return ''
|
||||
value = value[:DATE_LENGTH]
|
||||
value = datetime.datetime.strptime(value, DEFAULT_SERVER_DATE_FORMAT).date()
|
||||
if len(value) > DATE_LENGTH:
|
||||
# a datetime, convert to correct timezone
|
||||
value = odoo.fields.Datetime.from_string(value)
|
||||
value = odoo.fields.Datetime.context_timestamp(env['res.lang'], value)
|
||||
else:
|
||||
value = odoo.fields.Datetime.from_string(value)
|
||||
|
||||
lang = env['res.lang']._lang_get(lang_code or env.context.get('lang') or 'en_US')
|
||||
locale = babel.Locale.parse(lang.code)
|
||||
|
||||
Reference in New Issue
Block a user