diff --git a/addons/account/account_invoice_view.xml b/addons/account/account_invoice_view.xml index f081b7f0c26..4e2e041efea 100644 --- a/addons/account/account_invoice_view.xml +++ b/addons/account/account_invoice_view.xml @@ -182,8 +182,10 @@ + + on_change="onchange_journal_id(journal_id)" options="{'no_create': True}" + attrs="{'readonly':[('internal_number','!=',False)]}"/> @@ -328,8 +330,10 @@ + + on_change="onchange_journal_id(journal_id)" options="{'no_create': True}" + attrs="{'readonly':[('internal_number','!=',False)]}"/> diff --git a/addons/account/account_move_line.py b/addons/account/account_move_line.py index 820c29c5e58..56132b3ab46 100644 --- a/addons/account/account_move_line.py +++ b/addons/account/account_move_line.py @@ -451,7 +451,7 @@ class account_move_line(osv.osv): 'debit': fields.float('Debit', digits_compute=dp.get_precision('Account')), 'credit': fields.float('Credit', digits_compute=dp.get_precision('Account')), 'account_id': fields.many2one('account.account', 'Account', required=True, ondelete="cascade", domain=[('type','<>','view'), ('type', '<>', 'closed')], select=2), - 'move_id': fields.many2one('account.move', 'Journal Entry', ondelete="cascade", help="The move of this entry line.", select=2, required=True), + 'move_id': fields.many2one('account.move', 'Journal Entry', ondelete="cascade", help="The move of this entry line.", select=2, required=True, auto_join=True), 'narration': fields.related('move_id','narration', type='text', relation='account.move', string='Internal Note'), 'ref': fields.related('move_id', 'ref', string='Reference', type='char', store=True), 'statement_id': fields.many2one('account.bank.statement', 'Statement', help="The bank statement used for bank reconciliation", select=1, copy=False), diff --git a/addons/account_followup/account_followup.py b/addons/account_followup/account_followup.py index 23fbeb598b8..84a6a07332e 100644 --- a/addons/account_followup/account_followup.py +++ b/addons/account_followup/account_followup.py @@ -309,6 +309,7 @@ class res_partner(osv.osv): ('reconcile_id', '=', False), ('state', '!=', 'draft'), ('company_id', '=', company_id), + ('date_maturity', '<=', fields.date.context_today(self,cr,uid)), ], context=context): raise osv.except_osv(_('Error!'),_("The partner does not have any accounting entries to print in the overdue report for the current company.")) self.message_post(cr, uid, [ids[0]], body=_('Printed overdue payments report'), context=context) diff --git a/addons/account_followup/report/account_followup_print.py b/addons/account_followup/report/account_followup_print.py index 0c9d9b8c490..292b5e044ee 100644 --- a/addons/account_followup/report/account_followup_print.py +++ b/addons/account_followup/report/account_followup_print.py @@ -21,7 +21,7 @@ import time from collections import defaultdict -from openerp.osv import osv +from openerp.osv import osv, fields from openerp.report import report_sxw @@ -55,6 +55,7 @@ class report_rappel(report_sxw.rml_parse): ('reconcile_id', '=', False), ('state', '!=', 'draft'), ('company_id', '=', company_id), + ('date_maturity', '<=', fields.date.context_today(self,self.cr,self.uid)), ]) # lines_per_currency = {currency: [line data, ...], ...} diff --git a/addons/analytic/analytic_view.xml b/addons/analytic/analytic_view.xml index 7b1b16037cf..ba28acd7cd5 100644 --- a/addons/analytic/analytic_view.xml +++ b/addons/analytic/analytic_view.xml @@ -29,7 +29,7 @@ - + diff --git a/addons/board/board.py b/addons/board/board.py index 7f867fa1b11..b6b41bffe62 100644 --- a/addons/board/board.py +++ b/addons/board/board.py @@ -23,7 +23,7 @@ from operator import itemgetter from textwrap import dedent -from openerp import tools +from openerp import tools, SUPERUSER_ID from openerp.osv import fields, osv class board_board(osv.osv): @@ -143,7 +143,7 @@ class board_create(osv.osv_memory): ''') }, context=context) - menu_id = self.pool.get('ir.ui.menu').create(cr, uid, { + menu_id = self.pool.get('ir.ui.menu').create(cr, SUPERUSER_ID, { 'name': this.name, 'parent_id': this.menu_parent_id.id, 'action': 'ir.actions.act_window,%s' % (action_id,) diff --git a/addons/crm/crm_lead.py b/addons/crm/crm_lead.py index d142b4aa403..7d8f908e391 100644 --- a/addons/crm/crm_lead.py +++ b/addons/crm/crm_lead.py @@ -313,6 +313,7 @@ class crm_lead(format_address, osv.osv): values = { 'partner_name': partner.parent_id.name if partner.parent_id else partner.name, 'contact_name': partner.name if partner.parent_id else False, + 'title': partner.title and partner.title.id or False, 'street': partner.street, 'street2': partner.street2, 'city': partner.city, @@ -323,6 +324,7 @@ class crm_lead(format_address, osv.osv): 'mobile': partner.mobile, 'fax': partner.fax, 'zip': partner.zip, + 'function': partner.function, } return {'value': values} diff --git a/addons/hr_timesheet_sheet/hr_timesheet_sheet.py b/addons/hr_timesheet_sheet/hr_timesheet_sheet.py index d2d0d0a0533..71315c2fd90 100644 --- a/addons/hr_timesheet_sheet/hr_timesheet_sheet.py +++ b/addons/hr_timesheet_sheet/hr_timesheet_sheet.py @@ -559,12 +559,13 @@ class hr_timesheet_sheet_sheet_day(osv.osv): MAX(id) as id, name, sheet_id, + timezone, SUM(total_timesheet) as total_timesheet, - CASE WHEN SUM(total_attendance) < 0 + CASE WHEN SUM(orphan_attendances) != 0 THEN (SUM(total_attendance) + CASE WHEN current_date <> name THEN 1440 - ELSE (EXTRACT(hour FROM current_time AT TIME ZONE 'UTC') * 60) + EXTRACT(minute FROM current_time AT TIME ZONE 'UTC') + ELSE (EXTRACT(hour FROM current_time AT TIME ZONE 'UTC' AT TIME ZONE coalesce(timezone, 'UTC')) * 60) + EXTRACT(minute FROM current_time AT TIME ZONE 'UTC' AT TIME ZONE coalesce(timezone, 'UTC')) END ) ELSE SUM(total_attendance) @@ -573,21 +574,25 @@ class hr_timesheet_sheet_sheet_day(osv.osv): (( select min(hrt.id) as id, + 'UTC' as timezone, l.date::date as name, s.id as sheet_id, sum(l.unit_amount) as total_timesheet, + 0 as orphan_attendances, 0.0 as total_attendance from hr_analytic_timesheet hrt JOIN account_analytic_line l ON l.id = hrt.line_id LEFT JOIN hr_timesheet_sheet_sheet s ON s.id = hrt.sheet_id - group by l.date::date, s.id + group by l.date::date, s.id, timezone ) union ( select -min(a.id) as id, + p.tz as timezone, (a.name AT TIME ZONE 'UTC' AT TIME ZONE coalesce(p.tz, 'UTC'))::date as name, s.id as sheet_id, 0.0 as total_timesheet, + SUM(CASE WHEN a.action = 'sign_in' THEN -1 ELSE 1 END) as orphan_attendances, SUM(((EXTRACT(hour FROM (a.name AT TIME ZONE 'UTC' AT TIME ZONE coalesce(p.tz, 'UTC'))) * 60) + EXTRACT(minute FROM (a.name AT TIME ZONE 'UTC' AT TIME ZONE coalesce(p.tz, 'UTC')))) * (CASE WHEN a.action = 'sign_in' THEN -1 ELSE 1 END)) as total_attendance from hr_attendance a @@ -602,9 +607,9 @@ class hr_timesheet_sheet_sheet_day(osv.osv): LEFT JOIN res_partner p ON u.partner_id = p.id WHERE action in ('sign_in', 'sign_out') - group by (a.name AT TIME ZONE 'UTC' AT TIME ZONE coalesce(p.tz, 'UTC'))::date, s.id + group by (a.name AT TIME ZONE 'UTC' AT TIME ZONE coalesce(p.tz, 'UTC'))::date, s.id, timezone )) AS foo - GROUP BY name, sheet_id + GROUP BY name, sheet_id, timezone )) AS bar""") diff --git a/addons/project/project.py b/addons/project/project.py index 5e9f4b97200..fdf42ccf6be 100644 --- a/addons/project/project.py +++ b/addons/project/project.py @@ -313,7 +313,7 @@ class project(osv.osv): return True _constraints = [ - (_check_dates, 'Error! project start-date must be lower then project end-date.', ['date_start', 'date']) + (_check_dates, 'Error! project start-date must be lower than project end-date.', ['date_start', 'date']) ] def set_template(self, cr, uid, ids, context=None): @@ -836,7 +836,7 @@ class task(osv.osv): _constraints = [ (_check_recursion, 'Error ! You cannot create recursive tasks.', ['parent_ids']), - (_check_dates, 'Error ! Task end-date must be greater then task start-date', ['date_start','date_end']) + (_check_dates, 'Error ! Task end-date must be greater than task start-date', ['date_start','date_end']) ] # Override view according to the company definition diff --git a/addons/purchase/purchase.py b/addons/purchase/purchase.py index a3ae7197f12..87afb9bbc69 100644 --- a/addons/purchase/purchase.py +++ b/addons/purchase/purchase.py @@ -146,6 +146,19 @@ class purchase_order(osv.osv): limit=1) return res and res[0] or False + def _has_non_stockable_item(self, cr, uid, ids, *args): + res = dict.fromkeys(ids, False) + for order in self.browse(cr, uid, ids): + for order_line in order.order_line: + if ( + not order_line.product_id + or + order_line.product_id + and order_line.product_id.type not in ('product', 'consu') + ): + res[order.id] = True + return res + def _get_picking_in(self, cr, uid, context=None): obj_data = self.pool.get('ir.model.data') type_obj = self.pool.get('stock.picking.type') @@ -295,6 +308,7 @@ class purchase_order(osv.osv): 'create_uid': fields.many2one('res.users', 'Responsible'), 'company_id': fields.many2one('res.company', 'Company', required=True, select=1, states={'confirmed': [('readonly', True)], 'approved': [('readonly', True)]}), 'journal_id': fields.many2one('account.journal', 'Journal'), + 'has_non_stockable_item': fields.function(_has_non_stockable_item, method=True, type='boolean', string='Contains a non-stockable item'), 'bid_date': fields.date('Bid Received On', readonly=True, help="Date on which the bid was received"), 'bid_validity': fields.date('Bid Valid Until', help="Date on which the bid expired"), 'picking_type_id': fields.many2one('stock.picking.type', 'Deliver To', help="This will determine picking type of incoming shipment", required=True, @@ -484,6 +498,7 @@ class purchase_order(osv.osv): action['res_id'] = pick_ids and pick_ids[0] or False return action + def wkf_approve_order(self, cr, uid, ids, context=None): self.write(cr, uid, ids, {'state': 'approved', 'date_approve': fields.date.context_today(self,cr,uid,context=context)}) return True @@ -542,6 +557,10 @@ class purchase_order(osv.osv): for po in self.browse(cr, uid, ids, context=context): if not po.order_line: raise osv.except_osv(_('Error!'),_('You cannot confirm a purchase order without any purchase order line.')) + if po.invoice_method == 'picking' and po.has_non_stockable_item is True: + raise osv.except_osv( + _('Error!'), + _("You cannot confirm a purchase order with Invoice Control Method 'Based on incoming shipments' that contains non-stockable items.")) for line in po.order_line: if line.state=='draft': todo.append(line.id) diff --git a/addons/sale_stock/sale_stock.py b/addons/sale_stock/sale_stock.py index a7a28c1d334..dd592894ee6 100644 --- a/addons/sale_stock/sale_stock.py +++ b/addons/sale_stock/sale_stock.py @@ -299,6 +299,14 @@ class sale_order_line(osv.osv): res['value'].update({'product_packaging': False}) return res + # set product uom in context to get virtual stock in current uom + if res.get('value', {}).get('product_uom'): + # use the uom changed by super call + context.update({'uom': res['value']['product_uom']}) + elif uom: + # fallback on selected + context.update({'uom': uom}) + #update of result obtained in super function product_obj = product_obj.browse(cr, uid, product, context=context) res['value'].update({'product_tmpl_id': product_obj.product_tmpl_id.id, 'delay': (product_obj.sale_delay or 0.0)}) diff --git a/addons/web/static/src/js/view_form.js b/addons/web/static/src/js/view_form.js index 4de993bee3f..610c02b1426 100644 --- a/addons/web/static/src/js/view_form.js +++ b/addons/web/static/src/js/view_form.js @@ -2842,6 +2842,7 @@ instance.web.form.FieldText = instance.web.form.AbstractField.extend(instance.we this.$textarea = this.$el.find('textarea'); this.auto_sized = false; this.default_height = this.$textarea.css('height'); + if (this.default_height === '0px') this.default_height = '90px'; if (this.get("effective_readonly")) { this.$textarea.attr('disabled', 'disabled'); } diff --git a/addons/web/static/src/js/view_list.js b/addons/web/static/src/js/view_list.js index 646383a09b1..ee934d6a2c9 100644 --- a/addons/web/static/src/js/view_list.js +++ b/addons/web/static/src/js/view_list.js @@ -299,21 +299,24 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi this.$pager .on('click', 'a[data-pager-action]', function () { var $this = $(this); - var max_page = Math.floor(self.dataset.size() / self.limit()); + var max_page_index = Math.ceil(self.dataset.size() / self.limit()) - 1; switch ($this.data('pager-action')) { case 'first': - self.page = 0; break; + self.page = 0; + break; case 'last': - self.page = max_page - 1; + self.page = max_page_index; break; case 'next': - self.page += 1; break; + self.page += 1; + break; case 'previous': - self.page -= 1; break; + self.page -= 1; + break; } if (self.page < 0) { - self.page = max_page; - } else if (self.page > max_page) { + self.page = max_page_index; + } else if (self.page > max_page_index) { self.page = 0; } self.reload_content(); diff --git a/openerp/addons/base/ir/ir_attachment.py b/openerp/addons/base/ir/ir_attachment.py index 1c9703e8e60..77e4190f552 100644 --- a/openerp/addons/base/ir/ir_attachment.py +++ b/openerp/addons/base/ir/ir_attachment.py @@ -140,7 +140,9 @@ class ir_attachment(osv.osv): return fname def _file_delete(self, cr, uid, fname): - count = self.search_count(cr, 1, [('store_fname','=',fname)]) + # using SQL to include files hidden through unlink or due to record rules + cr.execute("SELECT COUNT(*) FROM ir_attachment WHERE store_fname = %s", (fname,)) + count = cr.fetchone()[0] full_path = self._full_path(cr, uid, fname) if not count and os.path.exists(full_path): try: diff --git a/openerp/addons/base/ir/ir_filters.py b/openerp/addons/base/ir/ir_filters.py index f179a8169f0..0aa673f9bc5 100644 --- a/openerp/addons/base/ir/ir_filters.py +++ b/openerp/addons/base/ir/ir_filters.py @@ -140,13 +140,14 @@ class ir_filters(osv.osv): ] def _auto_init(self, cr, context=None): - super(ir_filters, self)._auto_init(cr, context) + result = super(ir_filters, self)._auto_init(cr, context) # Use unique index to implement unique constraint on the lowercase name (not possible using a constraint) cr.execute("DROP INDEX IF EXISTS ir_filters_name_model_uid_unique_index") # drop old index w/o action cr.execute("SELECT indexname FROM pg_indexes WHERE indexname = 'ir_filters_name_model_uid_unique_action_index'") if not cr.fetchone(): cr.execute("""CREATE UNIQUE INDEX "ir_filters_name_model_uid_unique_action_index" ON ir_filters (lower(name), model_id, COALESCE(user_id,-1), COALESCE(action_id,-1))""") + return result _columns = { 'name': fields.char('Filter Name', translate=True, required=True),