diff --git a/addons/account/account_invoice_view.xml b/addons/account/account_invoice_view.xml index b6776556d53..7b764fb4374 100644 --- a/addons/account/account_invoice_view.xml +++ b/addons/account/account_invoice_view.xml @@ -266,7 +266,7 @@ - + @@ -516,6 +516,19 @@ + + + tree + + + + + + form + + + + Pending Invoice account.invoice diff --git a/addons/account/account_move_line.py b/addons/account/account_move_line.py index 6f92464e036..9bed6f7120c 100644 --- a/addons/account/account_move_line.py +++ b/addons/account/account_move_line.py @@ -851,7 +851,12 @@ class account_move_line(osv.osv): ret.append(ret_line) return ret - def list_partners_to_reconcile(self, cr, uid, context=None): + + def list_partners_to_reconcile(self, cr, uid, context=None, filter_domain=False): + line_ids = [] + if filter_domain: + line_ids = self.search(cr, uid, filter_domain, context=context) + where_clause = filter_domain and "AND l.id = ANY(%s)" or "" cr.execute( """SELECT partner_id FROM ( SELECT l.partner_id, p.last_reconciliation_date, SUM(l.debit) AS debit, SUM(l.credit) AS credit, MAX(l.create_date) AS max_date @@ -861,10 +866,12 @@ class account_move_line(osv.osv): WHERE a.reconcile IS TRUE AND l.reconcile_id IS NULL AND l.state <> 'draft' + %s GROUP BY l.partner_id, p.last_reconciliation_date ) AS s WHERE debit > 0 AND credit > 0 AND (last_reconciliation_date IS NULL OR max_date > last_reconciliation_date) - ORDER BY last_reconciliation_date""") + ORDER BY last_reconciliation_date""" + % where_clause, (line_ids,)) ids = [x[0] for x in cr.fetchall()] if not ids: return [] diff --git a/addons/account/static/src/js/account_widgets.js b/addons/account/static/src/js/account_widgets.js index a60b550e7cc..e61c8d72760 100644 --- a/addons/account/static/src/js/account_widgets.js +++ b/addons/account/static/src/js/account_widgets.js @@ -1791,7 +1791,7 @@ openerp.account = function (instance) { this.last_group_by = group_by; this.old_search = _.bind(this._super, this); var mod = new instance.web.Model("account.move.line", context, domain); - return mod.call("list_partners_to_reconcile", []).then(function(result) { + return mod.call("list_partners_to_reconcile", [context, domain]).then(function(result) { var current = self.current_partner !== null ? self.partners[self.current_partner][0] : null; self.partners = result; var index = _.find(_.range(self.partners.length), function(el) { diff --git a/addons/crm/crm_lead.py b/addons/crm/crm_lead.py index 3d6ae546d29..138a9d952ce 100644 --- a/addons/crm/crm_lead.py +++ b/addons/crm/crm_lead.py @@ -396,7 +396,7 @@ class crm_lead(format_address, osv.osv): """ stages_leads = {} for lead in self.browse(cr, uid, ids, context=context): - stage_id = self.stage_find(cr, uid, [lead], lead.team_id.id or False, [('probability', '=', 0.0), ('fold', '=', True), ('sequence', '>', 1)], context=context) + stage_id = self.stage_find(cr, uid, [lead], lead.team_id.id or False, [('probability', '=', 0.0), ('on_change', '=', True), ('sequence', '>', 1)], context=context) if stage_id: if stages_leads.get(stage_id): stages_leads[stage_id].append(lead.id) @@ -415,7 +415,7 @@ class crm_lead(format_address, osv.osv): """ stages_leads = {} for lead in self.browse(cr, uid, ids, context=context): - stage_id = self.stage_find(cr, uid, [lead], lead.team_id.id or False, [('probability', '=', 100.0), ('fold', '=', True)], context=context) + stage_id = self.stage_find(cr, uid, [lead], lead.team_id.id or False, [('probability', '=', 100.0), ('on_change', '=', True)], context=context) if stage_id: if stages_leads.get(stage_id): stages_leads[stage_id].append(lead.id) @@ -887,6 +887,7 @@ class crm_lead(format_address, osv.osv): (tree_view or False, 'tree'), (False, 'kanban'), (False, 'calendar'), (False, 'graph')], 'type': 'ir.actions.act_window', + 'context': {'default_type': 'opportunity'} } def redirect_lead_view(self, cr, uid, lead_id, context=None): diff --git a/addons/hr_timesheet_invoice/hr_timesheet_invoice.py b/addons/hr_timesheet_invoice/hr_timesheet_invoice.py index f34e03b69a7..9a4c4f00dbf 100644 --- a/addons/hr_timesheet_invoice/hr_timesheet_invoice.py +++ b/addons/hr_timesheet_invoice/hr_timesheet_invoice.py @@ -154,14 +154,106 @@ class account_analytic_line(osv.osv): price = 0.0 return price + def _prepare_cost_invoice(self, cr, uid, partner, company_id, currency_id, analytic_lines, context=None): + """ returns values used to create main invoice from analytic lines""" + account_payment_term_obj = self.pool['account.payment.term'] + invoice_name = analytic_lines[0].account_id.name + + date_due = False + if partner.property_payment_term: + pterm_list = account_payment_term_obj.compute(cr, uid, + partner.property_payment_term.id, value=1, + date_ref=time.strftime('%Y-%m-%d')) + if pterm_list: + pterm_list = [line[0] for line in pterm_list] + pterm_list.sort() + date_due = pterm_list[-1] + return { + 'name': "%s - %s" % (time.strftime('%d/%m/%Y'), invoice_name), + 'partner_id': partner.id, + 'company_id': company_id, + 'payment_term': partner.property_payment_term.id or False, + 'account_id': partner.property_account_receivable.id, + 'currency_id': currency_id, + 'date_due': date_due, + 'fiscal_position': partner.property_account_position.id + } + + def _prepare_cost_invoice_line(self, cr, uid, invoice_id, product_id, uom, user_id, + factor_id, account, analytic_lines, journal_type, data, context=None): + product_obj = self.pool['product.product'] + + uom_context = dict(context or {}, uom=uom) + + total_price = sum(l.amount for l in analytic_lines) + total_qty = sum(l.unit_amount for l in analytic_lines) + + if data.get('product'): + # force product, use its public price + if isinstance(data['product'], (tuple, list)): + product_id = data['product'][0] + else: + product_id = data['product'] + unit_price = self._get_invoice_price(cr, uid, account, product_id, user_id, total_qty, uom_context) + elif journal_type == 'general' and product_id: + # timesheets, use sale price + unit_price = self._get_invoice_price(cr, uid, account, product_id, user_id, total_qty, uom_context) + else: + # expenses, using price from amount field + unit_price = total_price*-1.0 / total_qty + + factor = self.pool['hr_timesheet_invoice.factor'].browse(cr, uid, factor_id, context=uom_context) + factor_name = factor.customer_name + curr_invoice_line = { + 'price_unit': unit_price, + 'quantity': total_qty, + 'product_id': product_id, + 'discount': factor.factor, + 'invoice_id': invoice_id, + 'name': factor_name, + 'uos_id': uom, + 'account_analytic_id': account.id, + } + + if product_id: + product = product_obj.browse(cr, uid, product_id, context=uom_context) + factor_name = product_obj.name_get(cr, uid, [product_id], context=uom_context)[0][1] + if factor.customer_name: + factor_name += ' - ' + factor.customer_name + + general_account = product.property_account_income or product.categ_id.property_account_income_categ + if not general_account: + raise UserError(_("Configuration Error!") + '\n' + _("Please define income account for product '%s'.") % product.name) + taxes = product.taxes_id or general_account.tax_ids + tax = self.pool['account.fiscal.position'].map_tax(cr, uid, account.partner_id.property_account_position, taxes) + curr_invoice_line.update({ + 'invoice_line_tax_id': [(6, 0, tax)], + 'name': factor_name, + 'invoice_line_tax_id': [(6, 0, tax)], + 'account_id': general_account.id, + }) + + note = [] + for line in analytic_lines: + # set invoice_line_note + details = [] + if data.get('date', False): + details.append(line['date']) + if data.get('time', False): + if line['product_uom_id']: + details.append("%s %s" % (line.unit_amount, line.product_uom_id.name)) + else: + details.append("%s" % (line['unit_amount'], )) + if data.get('name', False): + details.append(line['name']) + if details: + note.append(u' - '.join(map(lambda x: unicode(x) or '', details))) + if note: + curr_invoice_line['name'] += "\n" + ("\n".join(map(lambda x: unicode(x) or '', note))) + return curr_invoice_line + def invoice_cost_create(self, cr, uid, ids, data=None, context=None): - analytic_account_obj = self.pool.get('account.analytic.account') - account_payment_term_obj = self.pool.get('account.payment.term') invoice_obj = self.pool.get('account.invoice') - product_obj = self.pool.get('product.product') - invoice_factor_obj = self.pool.get('hr_timesheet_invoice.factor') - fiscal_pos_obj = self.pool.get('account.fiscal.position') - product_uom_obj = self.pool.get('product.uom') invoice_line_obj = self.pool.get('account.invoice.line') invoices = [] if context is None: @@ -169,131 +261,62 @@ class account_analytic_line(osv.osv): if data is None: data = {} - journal_types = {} + # use key (partner/account, company, currency) + # creates one invoice per key + invoice_grouping = {} + currency_id = False # prepare for iteration on journal and accounts - for line in self.pool.get('account.analytic.line').browse(cr, uid, ids, context=context): - if line.journal_id.type not in journal_types: - journal_types[line.journal_id.type] = set() - journal_types[line.journal_id.type].add(line.account_id.id) - for journal_type, account_ids in journal_types.items(): - for account in analytic_account_obj.browse(cr, uid, list(account_ids), context=context): - partner = account.partner_id + for line in self.browse(cr, uid, ids, context=context): + + key = (line.account_id.id, + line.account_id.company_id.id, + line.account_id.pricelist_id.currency_id.id) + invoice_grouping.setdefault(key, []).append(line) + + for (key_id, company_id, currency_id), analytic_lines in invoice_grouping.items(): + # key_id is an account.analytic.account + partner = analytic_lines[0].account_id.partner_id # will be the same for every line + + curr_invoice = self._prepare_cost_invoice(cr, uid, partner, company_id, currency_id, analytic_lines, context=context) + invoice_context = dict(context, + lang=partner.lang, + force_company=company_id, # set force_company in context so the correct product properties are selected (eg. income account) + company_id=company_id) # set company_id in context, so the correct default journal will be selected + last_invoice = invoice_obj.create(cr, uid, curr_invoice, context=invoice_context) + invoices.append(last_invoice) + + # use key (product, uom, user, invoiceable, analytic account, journal type) + # creates one invoice line per key + invoice_lines_grouping = {} + for analytic_line in analytic_lines: + account = analytic_line.account_id if (not partner) or not (account.pricelist_id): - raise UserError(_('Contract incomplete. Please fill in the Customer and Pricelist fields.')) + raise UserError(_('Contract incomplete. Please fill in the Customer and Pricelist fields for %s.') % (account.name)) - date_due = False - if partner.property_payment_term: - pterm_list= account_payment_term_obj.compute(cr, uid, - partner.property_payment_term.id, value=1, - date_ref=time.strftime('%Y-%m-%d')) - if pterm_list: - pterm_list = [line[0] for line in pterm_list] - pterm_list.sort() - date_due = pterm_list[-1] + if not analytic_line.to_invoice: + raise UserError(_('Trying to invoice non invoiceable line for %s.') % (analytic_line.product_id.name)) - curr_invoice = { - 'name': time.strftime('%d/%m/%Y') + ' - '+account.name, - 'partner_id': account.partner_id.id, - 'company_id': account.company_id.id, - 'payment_term': partner.property_payment_term.id or False, - 'account_id': partner.property_account_receivable.id, - 'currency_id': account.pricelist_id.currency_id.id, - 'date_due': date_due, - 'fiscal_position': account.partner_id.property_account_position.id - } - context2 = context.copy() - context2['lang'] = partner.lang - # set company_id in context, so the correct default journal will be selected - context2['force_company'] = curr_invoice['company_id'] - # set force_company in context so the correct product properties are selected (eg. income account) - context2['company_id'] = curr_invoice['company_id'] + key = (analytic_line.product_id.id, + analytic_line.product_uom_id.id, + analytic_line.user_id.id, + analytic_line.to_invoice.id, + analytic_line.account_id, + analytic_line.journal_id.type) + invoice_lines_grouping.setdefault(key, []).append(analytic_line) - last_invoice = invoice_obj.create(cr, uid, curr_invoice, context=context2) - invoices.append(last_invoice) + # finally creates the invoice line + for (product_id, uom, user_id, factor_id, account, journal_type), lines_to_invoice in invoice_lines_grouping.items(): + curr_invoice_line = self._prepare_cost_invoice_line(cr, uid, last_invoice, + product_id, uom, user_id, factor_id, account, lines_to_invoice, + journal_type, data, context=context) - cr.execute("""SELECT product_id, user_id, to_invoice, sum(amount), sum(unit_amount), product_uom_id - FROM account_analytic_line as line LEFT JOIN account_analytic_journal journal ON (line.journal_id = journal.id) - WHERE account_id = %s - AND line.id IN %s AND journal.type = %s AND to_invoice IS NOT NULL - GROUP BY product_id, user_id, to_invoice, product_uom_id""", (account.id, tuple(ids), journal_type)) - - for product_id, user_id, factor_id, total_price, qty, uom in cr.fetchall(): - context2.update({'uom': uom}) - - if data.get('product'): - # force product, use its public price - product_id = data['product'][0] - unit_price = self._get_invoice_price(cr, uid, account, product_id, user_id, qty, context2) - elif journal_type == 'general' and product_id: - # timesheets, use sale price - unit_price = self._get_invoice_price(cr, uid, account, product_id, user_id, qty, context2) - else: - # expenses, using price from amount field - unit_price = total_price*-1.0 / qty - - factor = invoice_factor_obj.browse(cr, uid, factor_id, context=context2) - # factor_name = factor.customer_name and line_name + ' - ' + factor.customer_name or line_name - factor_name = factor.customer_name - curr_line = { - 'price_unit': unit_price, - 'quantity': qty, - 'product_id': product_id or False, - 'discount': factor.factor, - 'invoice_id': last_invoice, - 'name': factor_name, - 'uos_id': uom, - 'account_analytic_id': account.id, - } - product = product_obj.browse(cr, uid, product_id, context=context2) - if product: - factor_name = product_obj.name_get(cr, uid, [product_id], context=context2)[0][1] - if factor.customer_name: - factor_name += ' - ' + factor.customer_name - - general_account = product.property_account_income or product.categ_id.property_account_income_categ - if not general_account: - raise UserError(_("Configuration Error!") + '\n' + _("Please define income account for product '%s'.") % product.name) - taxes = product.taxes_id or general_account.tax_ids - tax = fiscal_pos_obj.map_tax(cr, uid, account.partner_id.property_account_position, taxes) - curr_line.update({ - 'invoice_line_tax_id': [(6,0,tax )], - 'name': factor_name, - 'invoice_line_tax_id': [(6,0,tax)], - 'account_id': general_account.id, - }) - # - # Compute for lines - # - cr.execute("SELECT * FROM account_analytic_line WHERE account_id = %s and id IN %s AND product_id=%s and to_invoice=%s ORDER BY account_analytic_line.date", (account.id, tuple(ids), product_id, factor_id)) - - line_ids = cr.dictfetchall() - note = [] - for line in line_ids: - # set invoice_line_note - details = [] - if data.get('date', False): - details.append(line['date']) - if data.get('time', False): - if line['product_uom_id']: - details.append("%s %s" % (line['unit_amount'], product_uom_obj.browse(cr, uid, [line['product_uom_id']],context2)[0].name)) - else: - details.append("%s" % (line['unit_amount'], )) - if data.get('name', False): - details.append(line['name']) - if details: - note.append(u' - '.join(map(lambda x: unicode(x) or '',details))) - if note: - curr_line['name'] += "\n" + ("\n".join(map(lambda x: unicode(x) or '',note))) - invoice_line_obj.create(cr, uid, curr_line, context=context) - cr.execute("update account_analytic_line set invoice_id=%s WHERE account_id = %s and id IN %s", (last_invoice, account.id, tuple(ids))) - self.invalidate_cache(cr, uid, ['invoice_id'], ids, context=context) - - invoice_obj.button_reset_taxes(cr, uid, [last_invoice], context) + invoice_line_obj.create(cr, uid, curr_invoice_line, context=context) + self.write(cr, uid, [l.id for l in analytic_lines], {'invoice_id': last_invoice}, context=context) + invoice_obj.button_reset_taxes(cr, uid, [last_invoice], context) return invoices - class hr_analytic_timesheet(osv.osv): _inherit = "hr.analytic.timesheet" def on_change_account_id(self, cr, uid, ids, account_id, user_id=False): diff --git a/addons/mrp_repair/mrp_repair_view.xml b/addons/mrp_repair/mrp_repair_view.xml index 8d97b3aa7dc..fb556af4d59 100644 --- a/addons/mrp_repair/mrp_repair_view.xml +++ b/addons/mrp_repair/mrp_repair_view.xml @@ -167,6 +167,7 @@ + diff --git a/addons/point_of_sale/static/src/js/models.js b/addons/point_of_sale/static/src/js/models.js index de51cc10169..2ab32e322b6 100644 --- a/addons/point_of_sale/static/src/js/models.js +++ b/addons/point_of_sale/static/src/js/models.js @@ -183,15 +183,20 @@ openerp.point_of_sale.load_models = function load_models(instance, module){ //mo }, },{ model: 'account.tax', - fields: ['name','amount', 'price_include', 'include_base_amount', 'type'], + fields: ['name','amount', 'price_include', 'include_base_amount', 'type', 'child_ids', 'child_depend', 'include_base_amount'], domain: null, - loaded: function(self,taxes){ - self.taxes = taxes; + loaded: function(self, taxes){ + self.taxes = taxes; self.taxes_by_id = {}; - - for (var i = 0; i < taxes.length; i++) { - self.taxes_by_id[taxes[i].id] = taxes[i]; - } + _.each(taxes, function(tax){ + self.taxes_by_id[tax.id] = tax; + }); + _.each(self.taxes_by_id, function(tax) { + tax.child_taxes = {}; + _.each(tax.child_ids, function(child_tax_id) { + tax.child_taxes[child_tax_id] = self.taxes_by_id[child_tax_id]; + }); + }); }, },{ model: 'pos.session', @@ -1089,52 +1094,84 @@ openerp.point_of_sale.load_models = function load_models(instance, module){ //mo } return taxes; }, - get_all_prices: function(){ + compute_all: function(taxes, price_unit) { var self = this; + var res = []; var currency_rounding = this.pos.currency.rounding; - var base = this.get_base_price(); - var totalTax = base; - var totalNoTax = base; - - var product = this.get_product(); - var taxes = this.get_applicable_taxes(); - var taxtotal = 0; - var taxdetail = {}; - _.each(taxes, function(tax) { + var base = price_unit; + _(taxes).each(function(tax) { if (tax.price_include) { - var tmp; if (tax.type === "percent") { - tmp = base - round_pr(base / (1 + tax.amount),currency_rounding); + tmp = round_pr(base - round_pr(base / (1 + tax.amount),currency_rounding),currency_rounding); + data = {amount:tmp, price_include:true, id: tax.id}; + res.push(data); } else if (tax.type === "fixed") { tmp = round_pr(tax.amount * self.get_quantity(),currency_rounding); + data = {amount:tmp, price_include:true, id: tax.id}; + res.push(data); } else { throw "This type of tax is not supported by the point of sale: " + tax.type; } - tmp = round_pr(tmp,currency_rounding); - taxtotal += tmp; - totalNoTax -= tmp; - taxdetail[tax.id] = tmp; } else { - var tmp; if (tax.type === "percent") { - tmp = tax.amount * base; + tmp = round_pr(tax.amount * base, currency_rounding); + data = {amount:tmp, price_include:false, id: tax.id}; + res.push(data); } else if (tax.type === "fixed") { - tmp = tax.amount * self.get_quantity(); + tmp = round_pr(tax.amount * self.get_quantity(), currency_rounding); + data = {amount:tmp, price_include:false, id: tax.id}; + res.push(data); } else { throw "This type of tax is not supported by the point of sale: " + tax.type; } - tmp = round_pr(tmp,currency_rounding); - - if (tax.include_base_amount) { - base += tmp; + var base_amount = data.amount; + var child_amount = 0.0; + if (tax.child_depend) { + res.pop(); // do not use parent tax + child_tax = self.compute_all(tax.child_taxes, base_amount); + res.push(child_tax); + _(child_tax).each(function(child) { + child_amount += child.amount; + }); + } + if (tax.include_base_amount) { + base += base_amount + child_amount; } - - taxtotal += tmp; - totalTax += tmp; - taxdetail[tax.id] = tmp; } }); + return res; + }, + get_all_prices: function(){ + var base = round_pr(this.get_quantity() * this.get_unit_price() * (1.0 - (this.get_discount() / 100.0)), this.pos.currency.rounding); + var totalTax = base; + var totalNoTax = base; + var taxtotal = 0; + + var product = this.get_product(); + var taxes_ids = product.taxes_id; + var taxes = this.pos.taxes; + var taxdetail = {}; + var product_taxes = []; + + _(taxes_ids).each(function(el){ + product_taxes.push(_.detect(taxes, function(t){ + return t.id === el; + })); + }); + + var all_taxes = _(this.compute_all(product_taxes, base)).flatten(); + + _(all_taxes).each(function(tax) { + if (tax.price_include) { + totalNoTax -= tax.amount; + } else { + totalTax += tax.amount; + } + taxtotal += tax.amount; + taxdetail[tax.id] = tax.amount; + }); + return { "priceWithTax": totalTax, "priceWithoutTax": totalNoTax, diff --git a/addons/project/project.py b/addons/project/project.py index c071cae1bb8..84a6a7e4064 100644 --- a/addons/project/project.py +++ b/addons/project/project.py @@ -1031,7 +1031,7 @@ class task(osv.osv): def set_remaining_time(self, cr, uid, ids, remaining_time=1.0, context=None): for task in self.browse(cr, uid, ids, context=context): if (task.stage_id and task.stage_id.sequence <= 1) or (task.planned_hours == 0.0): - self.write(cr, uid, [task.id], {'planned_hours': remaining_time}, context=context) + self.write(cr, uid, [task.id], {'planned_hours': remaining_time + task.effective_hours}, context=context) self.write(cr, uid, ids, {'remaining_hours': remaining_time}, context=context) return True diff --git a/addons/purchase/purchase.py b/addons/purchase/purchase.py index 111852f723e..86bb9ed2b83 100644 --- a/addons/purchase/purchase.py +++ b/addons/purchase/purchase.py @@ -861,7 +861,7 @@ class purchase_order(osv.osv): Orders will only be merged if: * Purchase Orders are in draft * Purchase Orders belong to the same partner - * Purchase Orders are have same stock location, same pricelist + * Purchase Orders are have same stock location, same pricelist, same currency Lines will only be merged if: * Order lines are exactly the same except for the quantity and unit @@ -897,12 +897,13 @@ class purchase_order(osv.osv): # Compute what the new orders should contain new_orders = {} - order_lines_to_move = [] + order_lines_to_move = {} for porder in [order for order in self.browse(cr, uid, ids, context=context) if order.state == 'draft']: - order_key = make_key(porder, ('partner_id', 'location_id', 'pricelist_id')) + order_key = make_key(porder, ('partner_id', 'location_id', 'pricelist_id', 'currency_id')) new_order = new_orders.setdefault(order_key, ({}, [])) new_order[1].append(porder.id) order_infos = new_order[0] + order_lines_to_move.setdefault(order_key, []) if not order_infos: order_infos.update({ @@ -913,6 +914,7 @@ class purchase_order(osv.osv): 'picking_type_id': porder.picking_type_id.id, 'location_id': porder.location_id.id, 'pricelist_id': porder.pricelist_id.id, + 'currency_id': porder.currency_id.id, 'state': 'draft', 'order_line': {}, 'notes': '%s' % (porder.notes or '',), @@ -926,8 +928,7 @@ class purchase_order(osv.osv): if porder.origin: order_infos['origin'] = (order_infos['origin'] or '') + ' ' + porder.origin - for order_line in porder.order_line: - order_lines_to_move += [order_line.id] + order_lines_to_move[order_key] += [order_line.id for order_line in porder.order_line] allorders = [] orders_info = {} @@ -941,7 +942,7 @@ class purchase_order(osv.osv): for key, value in order_data['order_line'].iteritems(): del value['uom_factor'] value.update(dict(key)) - order_data['order_line'] = [(6, 0, order_lines_to_move)] + order_data['order_line'] = [(6, 0, order_lines_to_move[order_key])] # create the new order context.update({'mail_create_nolog': True}) diff --git a/addons/purchase/wizard/purchase_order_group_view.xml b/addons/purchase/wizard/purchase_order_group_view.xml index 83503380c61..56efe485ae5 100644 --- a/addons/purchase/wizard/purchase_order_group_view.xml +++ b/addons/purchase/wizard/purchase_order_group_view.xml @@ -13,7 +13,7 @@ Please note that: Orders will only be merged if: * Purchase Orders are in draft * Purchase Orders belong to the same supplier -* Purchase Orders are have same stock location, same pricelist +* Purchase Orders are have same stock location, same pricelist, same currency Lines will only be merged if: * Order lines are exactly the same except for the product,quantity and unit " colspan="4"/> diff --git a/addons/sale/sale.py b/addons/sale/sale.py index 8d7084abe5c..b032304faa8 100644 --- a/addons/sale/sale.py +++ b/addons/sale/sale.py @@ -330,7 +330,8 @@ class sale_order(osv.osv): part = self.pool.get('res.partner').browse(cr, uid, part, context=context) addr = self.pool.get('res.partner').address_get(cr, uid, [part.id], ['delivery', 'invoice', 'contact']) pricelist = part.property_product_pricelist and part.property_product_pricelist.id or False - payment_term = part.property_payment_term and part.property_payment_term.id or False + invoice_part = self.pool.get('res.partner').browse(cr, uid, addr['invoice'], context=context) + payment_term = invoice_part.property_payment_term and invoice_part.property_payment_term.id or False dedicated_salesman = part.user_id and part.user_id.id or uid val = { 'partner_invoice_id': addr['invoice'], @@ -393,14 +394,14 @@ class sale_order(osv.osv): 'origin': order.name, 'type': 'out_invoice', 'reference': order.client_order_ref or order.name, - 'account_id': order.partner_id.property_account_receivable.id, + 'account_id': order.partner_invoice_id.property_account_receivable.id, 'partner_id': order.partner_invoice_id.id, 'journal_id': journal_ids[0], 'invoice_line': [(6, 0, lines)], 'currency_id': order.pricelist_id.currency_id.id, 'comment': order.note, 'payment_term': order.payment_term and order.payment_term.id or False, - 'fiscal_position': order.fiscal_position.id or order.partner_id.property_account_position.id, + 'fiscal_position': order.fiscal_position.id or order.partner_invoice_id.property_account_position.id, 'date_invoice': context.get('date_invoice', False), 'company_id': order.company_id.id, 'user_id': order.user_id and order.user_id.id or False, diff --git a/addons/stock/stock_view.xml b/addons/stock/stock_view.xml index 23633f9a233..c914a8761cc 100644 --- a/addons/stock/stock_view.xml +++ b/addons/stock/stock_view.xml @@ -264,12 +264,7 @@ Click to add a serial number.

This is the list of all the production lots you recorded. When - you select a lot, you can get the - traceability of the products contained in lot. By default, the - list is filtered on the serial numbers that are available in - your warehouse but you can uncheck the 'Available' button to - get all the lots you produced, received or delivered to - customers. + you select a lot, you can get the traceability of the products contained in lot.

diff --git a/addons/web/static/src/js/formats.js b/addons/web/static/src/js/formats.js index 9fd9ff86b02..b716f0f0ef3 100644 --- a/addons/web/static/src/js/formats.js +++ b/addons/web/static/src/js/formats.js @@ -227,10 +227,6 @@ instance.web.parse_value = function (value, descriptor, value_if_empty) { throw new Error(_.str.sprintf(_t("'%s' is not a correct integer"), value)); return tmp; case 'float': - tmp = Number(value); - if (!isNaN(tmp)) - return tmp; - var tmp2 = value; do { tmp = tmp2; diff --git a/addons/web_kanban_sparkline/static/src/js/kanban_sparkline.js b/addons/web_kanban_sparkline/static/src/js/kanban_sparkline.js index b8c7761d103..f5be348a8e7 100644 --- a/addons/web_kanban_sparkline/static/src/js/kanban_sparkline.js +++ b/addons/web_kanban_sparkline/static/src/js/kanban_sparkline.js @@ -11,6 +11,15 @@ instance.web_kanban.SparklineBarWidget = instance.web_kanban.AbstractField.exten var self = this; var title = this.$node.html() || this.field.string; setTimeout(function () { + var tooltipFormatter = function(sp, options, fields) { + var format = $.spformat('
{{offset:offset}} {{formatted_value}}
'); + var result = ''; + $.each(fields, function(i, field) { + field.formatted_value = instance.web.format_value(field.value, { type : 'float' }); + result += format.render(field, options.get('tooltipValueLookups'), options); + }) + return result; + } var field_value = JSON.parse(self.field.value); var value = _.pluck(field_value, 'value'); var tooltips = _.pluck(field_value, 'tooltip'); @@ -24,6 +33,7 @@ instance.web_kanban.SparklineBarWidget = instance.web_kanban.AbstractField.exten barSpacing: 1, barColor: '#96d854', tooltipFormat: tooltipFormat, + tooltipFormatter: self.options.type == 'tristate' ? undefined : tooltipFormatter, chartRangeMin: 0, tooltipValueLookups: { 'offset': tooltips diff --git a/addons/website_quote/controllers/main.py b/addons/website_quote/controllers/main.py index c15431b9b2b..0c295d20b92 100644 --- a/addons/website_quote/controllers/main.py +++ b/addons/website_quote/controllers/main.py @@ -36,7 +36,7 @@ class sale_quote(http.Controller): def view(self, order_id, pdf=None, token=None, message=False, **post): # use SUPERUSER_ID allow to access/view order for public user # only if he knows the private token - order = request.registry.get('sale.order').browse(request.cr, token and SUPERUSER_ID or request.uid, order_id) + order = request.registry.get('sale.order').browse(request.cr, token and SUPERUSER_ID or request.uid, order_id, request.context) now = time.strftime('%Y-%m-%d') dummy, action = request.registry.get('ir.model.data').get_object_reference(request.cr, request.uid, 'sale', 'action_quotations') if token: diff --git a/doc/business.rst b/doc/business.rst index 26d4dfdf97a..def762578f6 100644 --- a/doc/business.rst +++ b/doc/business.rst @@ -4,4 +4,4 @@ Business Mementoes .. toctree:: - Accounting Memento + Accounting Memento (US GAAP) diff --git a/doc/cla/corporate/akretion.md b/doc/cla/corporate/akretion.md new file mode 100644 index 00000000000..61932b3fe8b --- /dev/null +++ b/doc/cla/corporate/akretion.md @@ -0,0 +1,43 @@ +Brazil, France, 2015-04-14 + +Akretion France 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, + +Sébastien BEAU sebastien.beau@akretion.com https://github.com/akretion + + +Akretion LTDA 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, +Raphaël Valyi raphael.valyi@akretion.com https://github.com/akretion + + +List of contributors: + +Abdessamad Hilali abdessamad.hilali@akretion.com https://github.com/ahilali +Adrien Chaussende adrien.chaussende@akretion.com https://github.com/achaussende +Alexis de Lattre alexis.delattre@akretion.com https://github.com/alexis-via +Arthur Vuillard arthur.vuillard@akretion.com https://github.com/arthru +Benoît Guillot benoit.guillot@akretion.com https://github.com/bguillot +Chafique delli chafique.delli@akretion.com https://github.com/chafique-delli +Cilene Oliveira cilene.oliveira@akretion.com https://github.com/cileneoliveira +David Beal david.beal@akretion.com https://github.com/bealdav +Florian da Costa florian.dacosta@akretion.com https://github.com/florian-dacosta +Magno Barcelo da Costa magno.costa@akretion.com.br https://github.com/mbcosta +Raphaël Valyi raphael.valyi@akretion.com https://github.com/rvalyi +Renato Lima renato.lima@akretion.com https://github.com/renatonlima +Sebastien Beau sebastien.beau@akretion.com https://github.com/sebastienbeau +Sylvain Calador sylvain.calador@akretion.com https://github.com/sylvainc +Sylvain Legal sylvain.legal@akretion.com https://github.com/legalsylvain +Thiago Moreira de Souza Arrais thiago.moreira@akretion.com.br https://github.com/thiagomds +Valentin Chemiere valentin.chemiere@akretion.com https://github.com/viggor +Vianney da Costa vianney.dacosta@akretion.com.br https://launchpad.net/~vianneydc diff --git a/doc/cla/individual/jnbrunet.md b/doc/cla/individual/jnbrunet.md new file mode 100644 index 00000000000..9c48a753205 --- /dev/null +++ b/doc/cla/individual/jnbrunet.md @@ -0,0 +1,11 @@ +Canada, March 21 2015 + +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, + +Jean-Nicolas Brunet jnbrunet2000@gmail.com https://github.com/jnbrunet