summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristophe Simonis <chs@odoo.com>2018-01-24 18:12:53 +0100
committerChristophe Simonis <chs@odoo.com>2018-01-24 18:12:53 +0100
commit0c97be858c9e4eb149dd8439da2fc9ce223d794b (patch)
treede369b87e08a349b5faf77d91768d0004b1cc1bf
parent50d87ae81b09fe9f621893eee8ccde4367e46c79 (diff)
parent4ea4f99df99e0f8d6e01b3116020cd1b525ec1fd (diff)
[MERGE] forward port branch saas-14 up to 4ea4f99df99
-rw-r--r--addons/account/models/account_invoice.py4
-rw-r--r--addons/account/models/account_move.py2
-rw-r--r--addons/account_accountant/models/res_config.py15
-rw-r--r--addons/point_of_sale/models/pos_order.py13
-rw-r--r--addons/point_of_sale/security/ir.model.access.csv2
-rw-r--r--addons/stock/models/stock_inventory.py4
-rw-r--r--addons/web/static/src/js/views/search_inputs.js6
-rw-r--r--addons/web_editor/static/src/js/ace.js3
-rw-r--r--addons/website_blog/models/website_blog.py11
-rw-r--r--addons/website_event_track/controllers/main.py4
-rw-r--r--addons/website_event_track/views/event_track_templates.xml4
-rw-r--r--addons/website_sale/static/src/js/website_sale_validate.js5
-rw-r--r--doc/cla/individual/sergeiruzkiicode.md11
-rw-r--r--odoo/addons/base/res/res_country_data.xml4
-rw-r--r--odoo/addons/base/res/res_currency_data.xml7
-rw-r--r--odoo/addons/base/res/res_currency_demo.xml6
-rw-r--r--odoo/addons/base/res/res_users.py8
-rw-r--r--odoo/http.py16
18 files changed, 96 insertions, 29 deletions
diff --git a/addons/account/models/account_invoice.py b/addons/account/models/account_invoice.py
index d1f339fe433..4b8e15cdb5f 100644
--- a/addons/account/models/account_invoice.py
+++ b/addons/account/models/account_invoice.py
@@ -1086,6 +1086,7 @@ class AccountInvoice(models.Model):
values['state'] = 'draft'
values['number'] = False
values['origin'] = invoice.number
+ values['payment_term_id'] = False
values['refund_invoice_id'] = invoice.id
if date:
@@ -1474,6 +1475,7 @@ class AccountPaymentTerm(models.Model):
def compute(self, value, date_ref=False):
date_ref = date_ref or fields.Date.today()
amount = value
+ sign = value < 0 and -1 or 1
result = []
if self.env.context.get('currency_id'):
currency = self.env['res.currency'].browse(self.env.context['currency_id'])
@@ -1482,7 +1484,7 @@ class AccountPaymentTerm(models.Model):
prec = currency.decimal_places
for line in self.line_ids:
if line.value == 'fixed':
- amt = round(line.value_amount, prec)
+ amt = sign * round(line.value_amount, prec)
elif line.value == 'percent':
amt = round(value * (line.value_amount / 100.0), prec)
elif line.value == 'balance':
diff --git a/addons/account/models/account_move.py b/addons/account/models/account_move.py
index 582d57b96c3..c13cac6c352 100644
--- a/addons/account/models/account_move.py
+++ b/addons/account/models/account_move.py
@@ -839,7 +839,7 @@ class AccountMoveLine(models.Model):
elif self._context.get('skip_full_reconcile_check') == 'amount_currency_only':
field = 'amount_residual_currency'
#target the pair of move in self that are the oldest
- sorted_moves = sorted(self, key=lambda a: a.date)
+ sorted_moves = sorted(self, key=lambda a: a.date_maturity or a.date)
debit = credit = False
for aml in sorted_moves:
if credit and debit:
diff --git a/addons/account_accountant/models/res_config.py b/addons/account_accountant/models/res_config.py
index 3059bbd2d93..d8cc8562f8e 100644
--- a/addons/account_accountant/models/res_config.py
+++ b/addons/account_accountant/models/res_config.py
@@ -7,7 +7,8 @@ from odoo import fields, models
class AccountConfigSettings(models.TransientModel):
_inherit = 'account.config.settings'
- fiscalyear_last_day = fields.Integer(related='company_id.fiscalyear_last_day', default=31)
+ fiscalyear_last_day = fields.Integer(related='company_id.fiscalyear_last_day',
+ default=lambda self: self._default_fiscalyear_last_day())
fiscalyear_last_month = fields.Selection([
(1, 'January'),
(2, 'February'),
@@ -20,8 +21,10 @@ class AccountConfigSettings(models.TransientModel):
(9, 'September'),
(10, 'October'),
(11, 'November'),
- (12, 'December')
- ], related='company_id.fiscalyear_last_month', default=12)
+ (12, 'December')],
+ related='company_id.fiscalyear_last_month',
+ default=lambda self: self._default_fiscalyear_last_month())
+
period_lock_date = fields.Date(related='company_id.period_lock_date')
fiscalyear_lock_date = fields.Date(related='company_id.fiscalyear_lock_date')
use_anglo_saxon = fields.Boolean(string='Anglo-Saxon Accounting', related='company_id.anglo_saxon_accounting')
@@ -33,3 +36,9 @@ class AccountConfigSettings(models.TransientModel):
'account.journal',
related='company_id.tax_cash_basis_journal_id',
string="Tax Cash Basis Journal",)
+
+ def _default_fiscalyear_last_day(self):
+ return self.env.user.company_id.fiscalyear_last_day or 31
+
+ def _default_fiscalyear_last_month(self):
+ return self.env.user.company_id.fiscalyear_last_month or 12
diff --git a/addons/point_of_sale/models/pos_order.py b/addons/point_of_sale/models/pos_order.py
index 27c1b8cfbcd..be3879bbbd7 100644
--- a/addons/point_of_sale/models/pos_order.py
+++ b/addons/point_of_sale/models/pos_order.py
@@ -339,15 +339,22 @@ class PosOrder(models.Model):
for order in self:
aml = order.statement_ids.mapped('journal_entry_ids') | order.account_move.line_ids | order.invoice_id.move_id.line_ids
aml = aml.filtered(lambda r: not r.reconciled and r.account_id.internal_type == 'receivable' and r.partner_id == order.partner_id.commercial_partner_id)
+
+ # Reconcile returns first
+ # to avoid mixing up the credit of a payment and the credit of a return
+ # in the receivable account
+ aml_returns = aml.filtered(lambda l: (l.journal_id.type == 'sale' and l.credit) or (l.journal_id.type != 'sale' and l.debit))
try:
- aml.reconcile()
+ aml_returns.reconcile()
+ (aml - aml_returns).reconcile()
except Exception:
# There might be unexpected situations where the automatic reconciliation won't
# work. We don't want the user to be blocked because of this, since the automatic
# reconciliation is introduced for convenience, not for mandatory accounting
# reasons.
- _logger.error('Reconciliation did not work for order %s', order.name)
- continue
+ # It may be interesting to have the Traceback logged anyway
+ # for debugging and support purposes
+ _logger.exception('Reconciliation did not work for order %s', order.name)
def _default_session(self):
return self.env['pos.session'].search([('state', '=', 'opened'), ('user_id', '=', self.env.uid)], limit=1)
diff --git a/addons/point_of_sale/security/ir.model.access.csv b/addons/point_of_sale/security/ir.model.access.csv
index 1e6f9b4e619..d9759520b4c 100644
--- a/addons/point_of_sale/security/ir.model.access.csv
+++ b/addons/point_of_sale/security/ir.model.access.csv
@@ -27,7 +27,7 @@ access_product_category_manager,product.category manager,product.model_product_c
access_product_pricelist_manager,product.pricelist manager,product.model_product_pricelist,group_pos_manager,1,0,0,0
access_pos_session_user,pos.session user,model_pos_session,group_pos_user,1,1,1,0
access_pos_config_user,pos.config user,model_pos_config,group_pos_user,1,1,0,0
-access_pos_config_manager,pos.config user,model_pos_config,group_pos_manager,1,1,1,0
+access_pos_config_manager,pos.config manager,model_pos_config,group_pos_manager,1,1,1,1
access_product_category_pos_manager,pos.category manager,model_pos_category,group_pos_manager,1,1,1,1
access_product_category_pos_user,pos.category user,model_pos_category,group_pos_user,1,0,0,0
access_barcode_nomenclature_pos_user,barcode.nomenclature.pos.user,barcodes.model_barcode_nomenclature,group_pos_user,1,0,0,0
diff --git a/addons/stock/models/stock_inventory.py b/addons/stock/models/stock_inventory.py
index fbc98d1c818..f2f2efd184b 100644
--- a/addons/stock/models/stock_inventory.py
+++ b/addons/stock/models/stock_inventory.py
@@ -455,9 +455,9 @@ class InventoryLine(models.Model):
continue
diff = line.theoretical_qty - line.product_qty
if diff < 0: # found more than expected
- vals = self._get_move_values(abs(diff), line.product_id.property_stock_inventory.id, line.location_id.id)
+ vals = line._get_move_values(abs(diff), line.product_id.property_stock_inventory.id, line.location_id.id)
else:
- vals = self._get_move_values(abs(diff), line.location_id.id, line.product_id.property_stock_inventory.id)
+ vals = line._get_move_values(abs(diff), line.location_id.id, line.product_id.property_stock_inventory.id)
move = moves.create(vals)
if diff > 0:
diff --git a/addons/web/static/src/js/views/search_inputs.js b/addons/web/static/src/js/views/search_inputs.js
index 6459fba77b8..91d85a1901a 100644
--- a/addons/web/static/src/js/views/search_inputs.js
+++ b/addons/web/static/src/js/views/search_inputs.js
@@ -354,7 +354,11 @@ var DateField = Field.extend(/** @lends instance.web.search.DateField# */{
}
// THIS SHOULD BE FORWARDPORTED UP TO SAAS-15, NOT LATER
- var m = moment.utc(v, t === 'datetime' ? 'YYYY-MM-DD HH:mm:ss' : 'YYYY-MM-DD');
+ if (t === 'datetime') {
+ var m = moment.utc(v, 'YYYY-MM-DD HH:mm:ss');
+ } else {
+ var m = moment(v, 'YYYY-MM-DD');
+ }
if (!m.isValid()) { return $.when(null); }
var d = m.toDate();
var date_string = formats.format_value(d, this.attrs);
diff --git a/addons/web_editor/static/src/js/ace.js b/addons/web_editor/static/src/js/ace.js
index 45a8214c255..b8544774fdb 100644
--- a/addons/web_editor/static/src/js/ace.js
+++ b/addons/web_editor/static/src/js/ace.js
@@ -167,7 +167,6 @@ var ViewEditor = Widget.extend({
*/
start: function () {
this.$viewEditor = this.$("#ace-view-editor");
- this.$editor = this.$(".ace_editor");
this.$typeSwitcherChoices = this.$(".o_ace_type_switcher_choice");
this.$typeSwitcherBtn = this.$(".o_ace_type_switcher > .dropdown-toggle");
@@ -182,9 +181,9 @@ var ViewEditor = Widget.extend({
this.$formatButton = this.$("button[data-action=\"format\"]");
this.$resetButton = this.$("button[data-action=\"reset\"]");
-
this.aceEditor = window.ace.edit(this.$viewEditor[0]);
this.aceEditor.setTheme("ace/theme/monokai");
+ this.$editor = this.$(".ace_editor");
var refX = 0;
var resizing = false;
diff --git a/addons/website_blog/models/website_blog.py b/addons/website_blog/models/website_blog.py
index 65bd0c472fe..429f19d9c74 100644
--- a/addons/website_blog/models/website_blog.py
+++ b/addons/website_blog/models/website_blog.py
@@ -207,11 +207,12 @@ class BlogPost(models.Model):
@api.multi
def write(self, vals):
- self.ensure_one()
- if 'website_published' in vals and 'published_date' not in vals:
- if self.published_date <= fields.Datetime.now():
- vals['published_date'] = vals['website_published'] and fields.Datetime.now()
- result = super(BlogPost, self).write(vals)
+ result = True
+ for post in self:
+ copy_vals = vals
+ if 'website_published' in vals and 'published_date' not in vals and post.published_date <= fields.Datetime.now():
+ copy_vals['published_date'] = vals['website_published'] and fields.Datetime.now() or False
+ result &= super(BlogPost, self).write(copy_vals)
self._check_for_publication(vals)
return result
diff --git a/addons/website_event_track/controllers/main.py b/addons/website_event_track/controllers/main.py
index 0a592053453..4443242a77d 100644
--- a/addons/website_event_track/controllers/main.py
+++ b/addons/website_event_track/controllers/main.py
@@ -15,7 +15,7 @@ class WebsiteEventTrackController(http.Controller):
@http.route(['''/event/<model("event.event"):event>/track/<model("event.track", "[('event_id','=',event[0])]"):track>'''], type='http', auth="public", website=True)
def event_track_view(self, event, track, **post):
- track = track.sudo()
+ track = track.sudo().with_context(tz=event.date_tz or 'UTC')
values = {'track': track, 'event': track.event_id, 'main_object': track}
return request.render("website_event_track.track_view", values)
@@ -67,6 +67,7 @@ class WebsiteEventTrackController(http.Controller):
@http.route(['''/event/<model("event.event", "[('website_track','=',1)]"):event>/agenda'''], type='http', auth="public", website=True)
def event_agenda(self, event, tag=None, **post):
+ event = event.with_context(tz=event.date_tz or 'UTC')
local_tz = pytz.timezone(event.date_tz or 'UTC')
days_tracks = collections.defaultdict(lambda: [])
for track in event.track_ids.sorted(lambda track: (track.date, bool(track.location_id))):
@@ -93,6 +94,7 @@ class WebsiteEventTrackController(http.Controller):
'''/event/<model("event.event", "[('website_track','=',1)]"):event>/track/tag/<model("event.track.tag"):tag>'''
], type='http', auth="public", website=True)
def event_tracks(self, event, tag=None, **post):
+ event = event.with_context(tz=event.date_tz or 'UTC')
searches = {}
if tag:
searches.update(tag=tag.id)
diff --git a/addons/website_event_track/views/event_track_templates.xml b/addons/website_event_track/views/event_track_templates.xml
index ecd617a98d5..0830f13d4e3 100644
--- a/addons/website_event_track/views/event_track_templates.xml
+++ b/addons/website_event_track/views/event_track_templates.xml
@@ -256,7 +256,9 @@
</div>
<div class="panel-body">
<b>Date</b><br/>
- <span t-field="track.date" t-options='{"hide_seconds":"True"}'/><br/>
+ <span t-field="track.date" t-options='{"hide_seconds":"True"}'/>
+ <t t-if="event.date_tz">(<span t-field="event.date_tz"/>)</t>
+ <br/>
<b>Duration</b><br/>
<span t-field="track.duration" t-options='{"widget": "duration", "unit": "hour", "round": "minute"}'/><br/>
<b>Location</b><br/>
diff --git a/addons/website_sale/static/src/js/website_sale_validate.js b/addons/website_sale/static/src/js/website_sale_validate.js
index 614e8901484..3bef4bc4f9a 100644
--- a/addons/website_sale/static/src/js/website_sale_validate.js
+++ b/addons/website_sale/static/src/js/website_sale_validate.js
@@ -21,7 +21,10 @@ $(document).ready(function () {
setTimeout(function () { payment_transaction_poll_status(); }, Math.ceil(_poll_nbr / 3) * 1000);
}
else {
- result.message = "<i class='fa fa-warning' />";
+ var $message = $(result.message);
+ $message.find('span:first').prepend($(
+ "<i title='We are waiting the confirmation of the bank or payment provider' class='fa fa-warning' style='margin-right:10px;'>"));
+ result.message = $message.html();
}
}
$('div.oe_website_sale_tx_status').html(result.message);
diff --git a/doc/cla/individual/sergeiruzkiicode.md b/doc/cla/individual/sergeiruzkiicode.md
new file mode 100644
index 00000000000..44af0359a97
--- /dev/null
+++ b/doc/cla/individual/sergeiruzkiicode.md
@@ -0,0 +1,11 @@
+Belarus, 2017-01-17
+
+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,
+
+Sergei Ruzki sergei.ruzki@icode.by https://github.com/sergeiruzkiicode
diff --git a/odoo/addons/base/res/res_country_data.xml b/odoo/addons/base/res/res_country_data.xml
index 2755b114d34..abbc0bc46f2 100644
--- a/odoo/addons/base/res/res_country_data.xml
+++ b/odoo/addons/base/res/res_country_data.xml
@@ -258,8 +258,8 @@
<field name="name">Belarus</field>
<field name="code">by</field>
<field file="base/static/img/country_flags/by.png" name="image" type="base64" />
- <field name="currency_id" ref="BYR" />
- <field eval="0" name="phone_code" />
+ <field name="currency_id" ref="BYN" />
+ <field eval="375" name="phone_code" />
</record>
<record id="bz" model="res.country">
<field name="name">Belize</field>
diff --git a/odoo/addons/base/res/res_currency_data.xml b/odoo/addons/base/res/res_currency_data.xml
index 8b108fa978d..8746f4d0b1b 100644
--- a/odoo/addons/base/res/res_currency_data.xml
+++ b/odoo/addons/base/res/res_currency_data.xml
@@ -408,6 +408,13 @@
<field name="active" eval="False"/>
</record>
+ <record id="BYN" model="res.currency">
+ <field name="name">BYN</field>
+ <field name="symbol">Br</field>
+ <field name="rounding">0.01</field>
+ <field name="active" eval="False"/>
+ </record>
+
<record id="BZD" model="res.currency">
<field name="name">BZD</field>
<field name="symbol">BZ$</field>
diff --git a/odoo/addons/base/res/res_currency_demo.xml b/odoo/addons/base/res/res_currency_demo.xml
index a6e406e783d..ba316d0a7db 100644
--- a/odoo/addons/base/res/res_currency_demo.xml
+++ b/odoo/addons/base/res/res_currency_demo.xml
@@ -349,6 +349,12 @@
<field name="rate">10228.19</field>
</record>
+ <record forcecreate="0" id="rateBYN" model="res.currency.rate">
+ <field name="currency_id" ref="BYN" />
+ <field name="name">2016-07-01</field>
+ <field name="rate">2.23151</field>
+ </record>
+
<record forcecreate="0" id="rateBZD" model="res.currency.rate">
<field name="currency_id" ref="BZD" />
<field name="name">2010-01-01</field>
diff --git a/odoo/addons/base/res/res_users.py b/odoo/addons/base/res/res_users.py
index 596620a9e52..657bebf58a7 100644
--- a/odoo/addons/base/res/res_users.py
+++ b/odoo/addons/base/res/res_users.py
@@ -14,6 +14,7 @@ from odoo.exceptions import AccessDenied, AccessError, UserError, ValidationErro
from odoo.osv import expression
from odoo.service.db import check_super
from odoo.tools import partition
+from odoo.http import root
_logger = logging.getLogger(__name__)
@@ -372,6 +373,8 @@ class Users(models.Model):
if any(key.startswith('context_') or key in ('lang', 'tz') for key in values):
self.context_get.clear_cache(self)
if any(key in values for key in ['active'] + USER_PRIVATE_FIELDS):
+ # force deletion of all sessions for these users
+ root.session_store.delete_sessions_for_uids(self.ids)
db = self._cr.dbname
for id in self.ids:
self.__uid_cache[db].pop(id, None)
@@ -385,7 +388,10 @@ class Users(models.Model):
db = self._cr.dbname
for id in self.ids:
self.__uid_cache[db].pop(id, None)
- return super(Users, self).unlink()
+ res = super(Users, self).unlink()
+ # force deletion of all sessions for these users
+ root.session_store.delete_sessions_for_uids(self.ids)
+ return res
@api.model
def name_search(self, name='', args=None, operator='ilike', limit=100):
diff --git a/odoo/http.py b/odoo/http.py
index 56b1f8e6930..46d744f2f36 100644
--- a/odoo/http.py
+++ b/odoo/http.py
@@ -1040,7 +1040,6 @@ class OpenERPSession(werkzeug.contrib.sessions.Session):
self.db = db
self.uid = uid
self.login = login
- self.password = password
request.uid = uid
request.disable_db = False
@@ -1055,7 +1054,6 @@ class OpenERPSession(werkzeug.contrib.sessions.Session):
"""
if not self.db or not self.uid:
raise SessionExpiredException("Session expired")
- security.check(self.db, self.uid, self.password)
def logout(self, keep_db=False):
for k in self.keys():
@@ -1068,7 +1066,6 @@ class OpenERPSession(werkzeug.contrib.sessions.Session):
self.setdefault("db", None)
self.setdefault("uid", None)
self.setdefault("login", None)
- self.setdefault("password", None)
self.setdefault("context", {})
def get_context(self):
@@ -1281,6 +1278,17 @@ class DisableCacheMiddleware(object):
start_response(status, new_headers)
return self.app(environ, start_wrapped)
+class OdooSessionStore(werkzeug.contrib.sessions.FilesystemSessionStore):
+ def delete_sessions_for_uids(self, uids):
+ # pretty expensive on large session stores, especially non-local!
+ uids = set(uids)
+ _logger.info('Deleting all HTTP sessions for UIDs %s', uids)
+ for sid in self.list():
+ s = self.get(sid)
+ if s.uid and s.uid in uids:
+ _logger.debug('Deleting session %s', sid)
+ self.delete(s)
+
class Root(object):
"""Root WSGI application for the OpenERP Web Client.
"""
@@ -1292,7 +1300,7 @@ class Root(object):
# Setup http sessions
path = odoo.tools.config.session_dir
_logger.debug('HTTP sessions stored in: %s', path)
- return werkzeug.contrib.sessions.FilesystemSessionStore(path, session_class=OpenERPSession)
+ return OdooSessionStore(path, session_class=OpenERPSession)
@lazy_property
def nodb_routing_map(self):