[MERGE] forward port branch 9.0 up to 3805eb620a

This commit is contained in:
Christophe Simonis
2017-01-02 12:49:55 +01:00
47 changed files with 667 additions and 458 deletions
+4 -3
View File
@@ -636,9 +636,10 @@ class AccountInvoice(models.Model):
for line in invoice_move_lines:
if self.currency_id != company_currency:
currency = self.currency_id.with_context(date=self.date_invoice or fields.Date.context_today(self))
line['currency_id'] = currency.id
line['amount_currency'] = currency.round(line['price'])
line['price'] = currency.compute(line['price'], company_currency)
if not (line.get('currency_id') and line.get('amount_currency')):
line['currency_id'] = currency.id
line['amount_currency'] = currency.round(line['price'])
line['price'] = currency.compute(line['price'], company_currency)
else:
line['currency_id'] = False
line['amount_currency'] = False
+8
View File
@@ -929,6 +929,14 @@ class AccountMoveLine(models.Model):
if 'analytic_account_id' in first_line_dict:
del first_line_dict['analytic_account_id']
if 'tax_ids' in first_line_dict:
tax_ids = []
#vals['tax_ids'] is a list of commands [[4, tax_id, None], ...]
for tax_id in vals['tax_ids']:
tax_ids.append(tax_id[1])
amount = first_line_dict['credit'] - first_line_dict['debit']
amount_tax = self.env['account.tax'].browse(tax_ids).compute_all(amount)['total_included']
first_line_dict['credit'] = amount_tax > 0 and amount_tax or 0.0
first_line_dict['debit'] = amount_tax < 0 and abs(amount_tax) or 0.0
del first_line_dict['tax_ids']
# Writeoff line in specified writeoff account
+8 -6
View File
@@ -234,15 +234,17 @@ class ResPartner(models.Model):
def _credit_debit_get(self):
tables, where_clause, where_params = self.env['account.move.line']._query_get()
where_params = [tuple(self.ids)] + where_params
self._cr.execute("""SELECT l.partner_id, act.type, SUM(l.amount_residual)
FROM account_move_line l
LEFT JOIN account_account a ON (l.account_id=a.id)
if where_clause:
where_clause = 'AND ' + where_clause
self._cr.execute("""SELECT account_move_line.partner_id, act.type, SUM(account_move_line.amount_residual)
FROM account_move_line
LEFT JOIN account_account a ON (account_move_line.account_id=a.id)
LEFT JOIN account_account_type act ON (a.user_type_id=act.id)
WHERE act.type IN ('receivable','payable')
AND l.partner_id IN %s
AND l.reconciled IS FALSE
AND account_move_line.partner_id IN %s
AND account_move_line.reconciled IS FALSE
""" + where_clause + """
GROUP BY l.partner_id, act.type
GROUP BY account_move_line.partner_id, act.type
""", where_params)
for pid, type, val in self._cr.fetchall():
partner = self.browse(pid)
+1
View File
@@ -1328,6 +1328,7 @@
<field name="currency_id" options="{'no_create': True}" groups="base.group_multi_currency"/>
<field name="debit" sum="Total Debit"/>
<field name="credit" sum="Total Credit"/>
<field name="date_maturity" required="0"/>
</tree>
</field>
<field name="narration" colspan="4" placeholder="Add an internal note..." nolabel="1" height="50"/>
+2
View File
@@ -10,5 +10,7 @@ class ir_configparameter(osv.Model):
if force:
IMD = self.pool['ir.model.data']
oauth_oe = IMD.xmlid_to_object(cr, SUPERUSER_ID, 'auth_oauth.provider_openerp')
if not oauth_oe:
return
dbuuid = self.get_param(cr, SUPERUSER_ID, 'database.uuid')
oauth_oe.write({'client_id': dbuuid})
+6 -6
View File
@@ -685,15 +685,15 @@ class calendar_event(osv.Model):
return [d.astimezone(pytz.UTC) for d in rset1]
def _get_recurrency_end_date(self, cr, uid, id, context=None):
data = self.read(cr, uid, id, ['final_date', 'recurrency', 'rrule_type', 'count', 'end_type', 'stop'], context=context)
data = self.read(cr, uid, id, ['final_date', 'recurrency', 'rrule_type', 'count', 'end_type', 'stop', 'interval'], context=context)
if not data.get('recurrency'):
return False
end_type = data.get('end_type')
final_date = data.get('final_date')
if end_type == 'count' and all(data.get(key) for key in ['count', 'rrule_type', 'stop']):
count = data['count'] + 1
if end_type == 'count' and all(data.get(key) for key in ['count', 'rrule_type', 'stop', 'interval']):
count = (data['count'] + 1) * data['interval']
delay, mult = {
'daily': ('days', 1),
'weekly': ('days', 7),
@@ -973,12 +973,12 @@ class calendar_event(osv.Model):
startdatetime = startdatetime or start
if startdatetime:
start = datetime.strptime(startdatetime, DEFAULT_SERVER_DATETIME_FORMAT)
value['start_date'] = datetime.strftime(start, DEFAULT_SERVER_DATE_FORMAT)
value['start_date'] = fields.date.context_today(self, cr, uid, context=context, timestamp=start)
enddatetime = enddatetime or end
if enddatetime:
end = datetime.strptime(enddatetime, DEFAULT_SERVER_DATETIME_FORMAT)
value['stop_date'] = datetime.strftime(end, DEFAULT_SERVER_DATE_FORMAT)
value['stop_date'] = fields.date.context_today(self, cr, uid, context=context, timestamp=end)
else: # from date to datetime
user = self.pool['res.users'].browse(cr, uid, uid, context)
tz = pytz.timezone(user.tz) if user.tz else pytz.utc
@@ -1312,7 +1312,7 @@ class calendar_event(osv.Model):
data['rrule_type'] = 'monthly'
if r._bymonthday:
data['day'] = r._bymonthday[0]
data['day'] = list(r._bymonthday)[0]
data['month_by'] = 'date'
data['rrule_type'] = 'monthly'
+1 -1
View File
@@ -36,7 +36,7 @@ class EventTicket(models.Model):
name = fields.Char(string='Name', required=True, translate=True)
event_id = fields.Many2one('event.event', string="Event", required=True, ondelete='cascade')
product_id = fields.Many2one('product.product', string='Product',
required=True, domain=[("event_type_id", "!=", False)],
required=True, domain=["|", ("event_type_id", "!=", False), ("event_ok", "=", True)],
default=_default_product_id)
registration_ids = fields.One2many('event.registration', 'event_ticket_id', string='Registrations')
price = fields.Float(string='Price', digits=dp.get_precision('Product Price'))
+2 -2
View File
@@ -201,8 +201,8 @@ class google_calendar(osv.AbstractModel):
if not context:
context = {}
if event.allday:
start_date = fields.datetime.context_timestamp(cr, uid, datetime.strptime(event.start, tools.DEFAULT_SERVER_DATETIME_FORMAT), context=context).isoformat('T').split('T')[0]
final_date = fields.datetime.context_timestamp(cr, uid, datetime.strptime(event.stop, tools.DEFAULT_SERVER_DATETIME_FORMAT) + timedelta(days=1), context=context).isoformat('T').split('T')[0]
start_date = event.start_date
final_date = (datetime.strptime(event.stop_date, tools.DEFAULT_SERVER_DATE_FORMAT) + timedelta(days=1)).strftime(tools.DEFAULT_SERVER_DATE_FORMAT)
type = 'date'
vstype = 'dateTime'
else:
+3 -1
View File
@@ -18,6 +18,7 @@ from dateutil.relativedelta import relativedelta
from openerp.exceptions import UserError, AccessError
from openerp import tools, SUPERUSER_ID
from openerp.osv import fields, osv
from openerp.tools import float_compare
from openerp.tools.translate import _
_logger = logging.getLogger(__name__)
@@ -500,7 +501,8 @@ class hr_holidays(osv.osv):
if record.holiday_type != 'employee' or record.type != 'remove' or not record.employee_id or record.holiday_status_id.limit:
continue
leave_days = self.pool.get('hr.holidays.status').get_days(cr, uid, [record.holiday_status_id.id], record.employee_id.id, context=context)[record.holiday_status_id.id]
if leave_days['remaining_leaves'] < 0 or leave_days['virtual_remaining_leaves'] < 0:
if float_compare(leave_days['remaining_leaves'], 0, precision_digits=2) == -1 or \
float_compare(leave_days['virtual_remaining_leaves'], 0, precision_digits=2) == -1:
return False
return True
@@ -3,7 +3,7 @@
from datetime import datetime, timedelta
from dateutil.relativedelta import relativedelta
from openerp.osv import osv
from openerp.osv import osv, fields
from openerp.tools.translate import _
from openerp.tools import DEFAULT_SERVER_DATE_FORMAT, DEFAULT_SERVER_DATETIME_FORMAT
@@ -44,6 +44,7 @@ class HrHolidaySummaryReport(osv.AbstractModel):
res = []
count = 0
start_date = datetime.strptime(start_date, DEFAULT_SERVER_DATE_FORMAT)
start_date = fields.datetime.context_timestamp(cr, uid, start_date, context=context).date()
end_date = start_date + relativedelta(days=59)
for index in range(0, 60):
current = start_date + timedelta(index)
@@ -55,8 +56,12 @@ class HrHolidaySummaryReport(osv.AbstractModel):
holiday_type = ['confirm','validate'] if holiday_type == 'both' else ['confirm'] if holiday_type == 'Confirmed' else ['validate']
holidays_ids = holidays_obj.search(cr, uid, [('employee_id', '=', empid), ('state', 'in', holiday_type), ('type', '=', 'remove'), ('date_from', '<=', str(end_date)), ('date_to', '>=', str(start_date))], context=context)
for holiday in holidays_obj.browse(cr, uid, holidays_ids, context=context):
# Convert date to user timezone, otherwise the report will not be consistent with the
# value displayed in the interface.
date_from = datetime.strptime(holiday.date_from, DEFAULT_SERVER_DATETIME_FORMAT)
date_from = fields.datetime.context_timestamp(cr, uid, date_from, context=context).date()
date_to = datetime.strptime(holiday.date_to, DEFAULT_SERVER_DATETIME_FORMAT)
date_to = fields.datetime.context_timestamp(cr, uid, date_to, context=context).date()
for index in range(0, ((date_to - date_from).days + 1)):
if date_from >= start_date and date_from <= end_date:
res[(date_from-start_date).days]['color'] = holiday.holiday_status_id.color_name
@@ -114,4 +119,4 @@ class HrHolidaySummaryReport(osv.AbstractModel):
'get_data_from_report': self._get_data_from_report(cr, uid, ids, data['form'], context=context),
'get_holidays_status': self._get_holidays_status(cr, uid, ids, context=context),
}
return report_obj.render(cr, uid, ids, 'hr_holidays.report_holidayssummary', docargs, context=context)
return report_obj.render(cr, uid, ids, 'hr_holidays.report_holidayssummary', docargs, context=context)
+3 -2
View File
@@ -1,6 +1,7 @@
#-*- coding:utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import babel
import time
from datetime import date
from datetime import datetime
@@ -637,7 +638,7 @@ class hr_payslip(osv.osv):
ttyme = datetime.fromtimestamp(time.mktime(time.strptime(date_from, "%Y-%m-%d")))
employee_id = empolyee_obj.browse(cr, uid, employee_id, context=context)
res['value'].update({
'name': _('Salary Slip of %s for %s') % (employee_id.name, tools.ustr(ttyme.strftime('%B-%Y'))),
'name': _('Salary Slip of %s for %s') % (employee_id.name, tools.ustr(babel.dates.format_date(date=ttyme, format='MMMM-y', locale=context.get('lang', 'en_US')))),
'company_id': employee_id.company_id.id
})
@@ -696,7 +697,7 @@ class hr_payslip(osv.osv):
date_to = self.date_to
ttyme = datetime.fromtimestamp(time.mktime(time.strptime(date_from, "%Y-%m-%d")))
self.name = _('Salary Slip of %s for %s') % (employee_id.name, tools.ustr(ttyme.strftime('%B-%Y')))
self.name = _('Salary Slip of %s for %s') % (employee_id.name, tools.ustr(babel.dates.format_date(date=ttyme, format='MMMM-y', locale=self.env.context.get('lang', 'en_US'))))
self.company_id = employee_id.company_id
if not self.env.context.get('contract') or not self.contract_id:
File diff suppressed because it is too large Load Diff
+2
View File
@@ -24,6 +24,7 @@ class account_tax(osv.osv):
_inherit = 'account.tax'
_columns = {
'name': fields.char('Tax Name', required=True, select=True, translate=True),
'description': fields.char('Display on Invoices', translate=True),
}
@@ -31,6 +32,7 @@ class account_tax_template(osv.osv):
_inherit = 'account.tax.template'
_columns = {
'name': fields.char('Tax Name', required=True, select=True, translate=True),
'description': fields.char('Display on Invoices', translate=True)
}
+6 -2
View File
@@ -60,8 +60,10 @@ class AccountChartTemplate(models.Model):
for company in company_ids:
# write account.account translations in the real COA
chart_template_id._process_accounts_translations(company.id, langs, 'name')
# copy account.tax translations
# copy account.tax name translations
chart_template_id._process_taxes_translations(company.id, langs, 'name')
# copy account.tax description translations
chart_template_id._process_taxes_translations(company.id, langs, 'description')
# copy account.fiscal.position translations
chart_template_id._process_fiscal_pos_translations(company.id, langs, 'name')
return True
@@ -107,8 +109,10 @@ class base_language_install(models.TransientModel):
for company in self.env['res.company'].search([('chart_template_id', '=', coa.id)]):
# write account.account translations in the real COA
coa._process_accounts_translations(company.id, [self.lang], 'name')
# copy account.tax translations
# copy account.tax name translations
coa._process_taxes_translations(company.id, [self.lang], 'name')
# copy account.tax description translations
coa._process_taxes_translations(company.id, [self.lang], 'description')
# copy account.fiscal.position translations
coa._process_fiscal_pos_translations(company.id, [self.lang], 'name')
return res
+8 -8
View File
@@ -12,7 +12,7 @@
<field name="applicability">taxes</field>
</record>
<record id="tax_tag_ut8" model="account.account.tag">
<field name="name">Utgående 8% mva</field>
<field name="name">Utgående 10% mva</field>
<field name="applicability">taxes</field>
</record>
<record id="tax_tag_in25" model="account.account.tag">
@@ -24,7 +24,7 @@
<field name="applicability">taxes</field>
</record>
<record id="tax_tag_in8" model="account.account.tag">
<field name="name">Inngående 8% mva</field>
<field name="name">Inngående 10% mva</field>
<field name="applicability">taxes</field>
</record>
@@ -63,9 +63,9 @@
</record>
<record id="tax3" model="account.tax.template">
<field name="chart_template_id" ref="no_chart_template"/>
<field name="name">Utgående 8% mva</field>
<field name="description">Utgående 8% mva</field>
<field name="amount">8</field>
<field name="name">Utgående 10% mva</field>
<field name="description">Utgående 10% mva</field>
<field name="amount">10</field>
<field name="amount_type">percent</field>
<field name="type_tax_use">sale</field>
<field name="account_id" ref="chart2704"/>
@@ -96,9 +96,9 @@
</record>
<record id="tax6" model="account.tax.template">
<field name="chart_template_id" ref="no_chart_template"/>
<field name="name">Inngående 8% mva</field>
<field name="description">Inngående 8% mva</field>
<field name="amount">8</field>
<field name="name">Inngående 10% mva</field>
<field name="description">Inngående 10% mva</field>
<field name="amount">10</field>
<field name="amount_type">percent</field>
<field name="type_tax_use">purchase</field>
<field name="account_id" ref="chart2714"/>
+6 -1
View File
@@ -60,11 +60,16 @@ class Followers(models.Model):
default_subtypes = self.env['mail.message.subtype'].search([
('default', '=', True),
'|', ('res_model', '=', res_model), ('res_model', '=', False)])
external_default_subtypes = default_subtypes.filtered(lambda subtype: not subtype.internal)
if force_mode:
employee_pids = self.env['res.users'].sudo().search([('partner_id', 'in', partner_data.keys()), ('share', '=', False)]).mapped('partner_id').ids
for pid, data in partner_data.iteritems():
if not data:
partner_data[pid] = default_subtypes.ids
if pid not in employee_pids:
partner_data[pid] = external_default_subtypes.ids
else:
partner_data[pid] = default_subtypes.ids
for cid, data in channel_data.iteritems():
if not data:
channel_data[cid] = default_subtypes.ids
+16
View File
@@ -75,6 +75,22 @@ class TestMailFollowers(TestMail):
self.assertEqual(len(follower), 1)
self.assertEqual(follower.subtype_ids, self.default_group_subtypes)
def test_followers_subtypes_default_internal(self):
mt_mg_def_int = self.env['mail.message.subtype'].create({'name': 'mt_mg_def', 'default': True, 'res_model': 'mail.channel', 'internal': True})
self.group_pigs.message_subscribe_users(user_ids=[self.user_employee.id])
follower = self.env['mail.followers'].search([
('res_model', '=', 'mail.channel'),
('res_id', '=', self.group_pigs.id),
('partner_id', '=', self.user_employee.partner_id.id)])
self.assertEqual(follower.subtype_ids, self.default_group_subtypes | mt_mg_def_int)
self.group_pigs.message_subscribe_users(user_ids=[self.user_portal.id])
follower = self.env['mail.followers'].search([
('res_model', '=', 'mail.channel'),
('res_id', '=', self.group_pigs.id),
('partner_id', '=', self.user_portal.partner_id.id)])
self.assertEqual(follower.subtype_ids, self.default_group_subtypes)
def test_followers_subtypes_specified(self):
self.group_pigs.sudo(self.user_employee).message_subscribe_users(subtype_ids=[self.mt_mg_nodef.id])
self.assertEqual(self.group_pigs.message_follower_ids.mapped('partner_id'), self.user_employee.partner_id)
@@ -235,7 +235,7 @@ snippets_editor.Class.include({
var _set_value = window.top.odoo[callback+"_updown"];
var odoo_top = window.top.odoo;
window.top.odoo[callback+"_updown"] = function (value, fields_values, field_name) {
if (!window) {
if (!window || window.closed) {
delete odoo_top[callback+"_updown"];
return;
}
+5 -1
View File
@@ -3,6 +3,7 @@
import json
import logging
import pprint
import urllib
import urllib2
import werkzeug
@@ -26,7 +27,7 @@ class PaypalController(http.Controller):
return return_url
def _parse_pdt_response(self, response):
""" Parse a text reponse for a PDT verification .
""" Parse a text response for a PDT verification .
:param response str: text response, structured in the following way:
STATUS\nkey1=value1\nkey2=value2...\n
@@ -37,6 +38,9 @@ class PaypalController(http.Controller):
lines = filter(None, response.split('\n'))
status = lines.pop(0)
pdt_post = dict(line.split('=', 1) for line in lines)
# html unescape
for post in pdt_post:
pdt_post[post] = urllib.unquote_plus(pdt_post[post]).decode('utf8')
return status, pdt_post
def paypal_validate_data(self, **post):
@@ -15,7 +15,7 @@
<field name="paypal_api_enabled" invisible="1"/>
<field name="paypal_api_username" invisible="1"/>
<field name="paypal_api_password" invisible="1"/>
<a colspan="2" href="https://www.paypal.com/in/cgi-bin/webscr?cmd=xpt/Marketing/general/how-to-set-up-a-paypal-account-outside" target="_blank">How to configure your paypal account?</a>
<a colspan="2" href="https://www.odoo.com/documentation/user/online/ecommerce/shopper_experience/paypal.html" target="_blank">How to configure your paypal account?</a>
</group>
</xpath>
</field>
@@ -46,7 +46,7 @@ class PosOrderReport(models.Model):
MIN(l.id) AS id,
COUNT(*) AS nbr_lines,
s.date_order AS date,
SUM(l.qty * u.factor) AS product_qty,
SUM(l.qty) AS product_qty,
SUM(l.qty * l.price_unit) AS price_sub_total,
SUM((l.qty * l.price_unit) * (100 - l.discount) / 100) AS price_total,
SUM((l.qty * l.price_unit) * (l.discount / 100)) AS total_discount,
+3 -1
View File
@@ -81,7 +81,9 @@ class wizard_user(osv.osv_memory):
_columns = {
'wizard_id': fields.many2one('portal.wizard', string='Wizard', required=True, ondelete='cascade'),
'partner_id': fields.many2one('res.partner', string='Contact', required=True, readonly=True),
'partner_id': fields.many2one(
'res.partner', string='Contact', required=True, readonly=True,
ondelete='cascade'),
'email': fields.char(string='Email', size=240),
'in_portal': fields.boolean('In Portal'),
'user_id': fields.many2one('res.users', string='Login User'),
+4 -2
View File
@@ -566,7 +566,7 @@ class product_template(osv.osv):
else:
company_id = context.get('force_company') or product.env.user.company_id.id
product = product.with_context(force_company=company_id)
res[product.id] = res[product.id] = product.sudo()[ptype]
res[product.id] = product.sudo()[ptype]
if ptype == 'list_price':
res[product.id] += product._name == "product.product" and product.price_extra or 0.0
if 'uom' in context:
@@ -922,9 +922,11 @@ class product_product(osv.osv):
result[product.id] = price_extra
return result
def _select_seller(self, cr, uid, product_id, partner_id=False, quantity=0.0, date=time.strftime(DEFAULT_SERVER_DATE_FORMAT), uom_id=False, context=None):
def _select_seller(self, cr, uid, product_id, partner_id=False, quantity=0.0, date=None, uom_id=False, context=None):
if context is None:
context = {}
if date is None:
date = time.strftime(DEFAULT_SERVER_DATE_FORMAT)
res = self.pool.get('product.supplierinfo').browse(cr, uid, [])
for seller in product_id.seller_ids:
# Set quantity in UoM of seller
+1
View File
@@ -12,6 +12,7 @@
cost_method: average
property_stock_account_input: o_expense
property_stock_account_output: o_income
supplier_taxes_id: []
description: Average Ice Cream can be mass-produced and thus is widely available in developed parts of the world. Ice cream can be purchased in large cartons (vats and squrounds) from supermarkets and grocery stores, in smaller quantities from ice cream shops, convenience stores, and milk bars, and in individual servings from small carts or vans at public events.
-
I create a draft Purchase Order for first incoming shipment for 10 pieces at 60€
+5 -3
View File
@@ -1,8 +1,9 @@
-
Set the company currency as EURO for the sake of repeatibility
-
!record {model: res.company, id: base.main_company}:
currency_id: base.EUR
!python {model: res.company}: |
company = self.browse(cr, uid, ref('base.main_company'))
company.currency_id = company.env['res.currency'].browse(ref('base.EUR'))
-
Set a product as using fifo price
-
@@ -19,13 +20,13 @@
valuation: real_time
property_stock_account_input: o_expense
property_stock_account_output: o_income
supplier_taxes_id: []
description: FIFO Ice Cream can be mass-produced and thus is widely available in developed parts of the world. Ice cream can be purchased in large cartons (vats and squrounds) from supermarkets and grocery stores, in smaller quantities from ice cream shops, convenience stores, and milk bars, and in individual servings from small carts or vans at public events.
-
I create a draft Purchase Order for first in move for 10 kg at 50 euro
-
!record {model: purchase.order, id: purchase_order_fifo1}:
partner_id: base.res_partner_3
currency_id: base.EUR
order_line:
- product_id: product_fifo_icecream
product_qty: 10.0
@@ -268,6 +269,7 @@
valuation: real_time
property_stock_account_input: o_expense
property_stock_account_output: o_income
supplier_taxes_id: []
description:
-
Create outpicking. We create delivery order of 100 kg.
+1
View File
@@ -13,6 +13,7 @@
valuation: real_time
property_stock_account_input: o_expense
property_stock_account_output: o_income
supplier_taxes_id: []
description: FIFO Ice Cream can be mass-produced and thus is widely available in developed parts of the world. Ice cream can be purchased in large cartons (vats and squrounds) from supermarkets and grocery stores, in smaller quantities from ice cream shops, convenience stores, and milk bars, and in individual servings from small carts or vans at public events.
-
I create a draft Purchase Order for first in move for 10 kg at 50 euro
+18
View File
@@ -18,6 +18,11 @@ class SaleOrderLine(models.Model):
domain,
['so_line', 'unit_amount', 'product_uom_id'], ['product_uom_id', 'so_line'], lazy=False
)
# If the unlinked analytic line was the last one on the SO line, the qty was not updated.
force_so_lines = self.env.context.get("force_so_lines")
if force_so_lines:
for line in force_so_lines:
lines.setdefault(line, 0.0)
for d in data:
if not d['product_uom_id']:
continue
@@ -42,6 +47,12 @@ class AccountAnalyticLine(models.Model):
def _get_invoice_price(self, order):
if self.unit_amount == 0.0:
return 0.0
# Prevent unnecessary currency conversion that could be impacted by exchange rate
# fluctuations
if self.currency_id and self.amount_currency and self.currency_id == order.currency_id:
return abs(self.amount_currency / self.unit_amount)
price_unit = abs(self.amount / self.unit_amount)
currency_id = self.company_id.currency_id
if currency_id and currency_id != order.currency_id:
@@ -124,3 +135,10 @@ class AccountAnalyticLine(models.Model):
line.with_context(create=True).write(res)
line.mapped('so_line').sudo()._compute_analytic()
return line
@api.multi
def unlink(self):
so_lines = self.mapped('so_line')
res = super(AccountAnalyticLine, self).unlink()
so_lines.with_context(force_so_lines=so_lines).sudo()._compute_analytic()
return res
+8
View File
@@ -105,6 +105,11 @@ class Inventory(models.Model):
if self.filter != 'pack':
self.package_id = False
@api.onchange('location_id')
def onchange_location_id(self):
if self.location_id.company_id:
self.company_id = self.location_id.company_id
@api.one
@api.constrains('filter', 'product_id', 'lot_id', 'partner_id', 'package_id')
def _check_filter_product(self):
@@ -177,6 +182,9 @@ class Inventory(models.Model):
locations = self.env['stock.location'].search([('id', 'child_of', [self.location_id.id])])
domain = ' location_id in %s'
args = (tuple(locations.ids),)
if self.company_id:
domain += ' AND company_id = %s'
args += (self.company_id.id,)
if self.partner_id:
domain += ' AND owner_id = %s'
args += (self.partner_id.id,)
+2 -2
View File
@@ -79,7 +79,7 @@
<field name="arch" type="xml">
<tree string="Stock Location" decoration-info="usage=='view'" decoration-danger="usage=='internal'">
<field name="active" invisible="1"/>
<field name="complete_name"/>
<field name="display_name"/>
<field name="usage"/>
<field name="company_id" groups="base.group_multi_company"/>
</tree>
@@ -287,4 +287,4 @@
<menuitem action="action_routes_form" id="menu_stock_routes"
parent="stock.menu_routes_config" sequence="1" />
</odoo>
</odoo>
+12 -2
View File
@@ -82,13 +82,21 @@ class account_invoice(osv.osv):
cacc = accounts['expense'].id
if dacc and cacc:
price_unit = i_line._get_anglo_saxon_price_unit()
if inv.currency_id.id != company_currency:
currency_id = inv.currency_id.id
amount_currency = self.env['account.invoice.line']._get_price(inv, company_currency, i_line, price_unit)
else:
currency_id = False
amount_currency = False
return [
{
'type':'src',
'name': i_line.name[:64],
'price_unit': price_unit,
'quantity': i_line.quantity,
'price': self.env['account.invoice.line']._get_price(inv, company_currency, i_line, price_unit),
'price': price_unit * i_line.quantity,
'currency_id': currency_id,
'amount_currency': amount_currency,
'account_id':dacc,
'product_id':i_line.product_id.id,
'uom_id':i_line.uom_id.id,
@@ -101,7 +109,9 @@ class account_invoice(osv.osv):
'name': i_line.name[:64],
'price_unit': price_unit,
'quantity': i_line.quantity,
'price': -1 * self.env['account.invoice.line']._get_price(inv, company_currency, i_line, price_unit),
'price': -1 * price_unit * i_line.quantity,
'currency_id': currency_id,
'amount_currency': -1 * amount_currency,
'account_id':cacc,
'product_id':i_line.product_id.id,
'uom_id':i_line.uom_id.id,
+3 -1
View File
@@ -299,7 +299,9 @@ var ImageDialog = Widget.extend({
self.display_attachments();
});
this.fetch_existing().then(function () {
self.set_image(_.find(self.records, function (record) { return record.url === o.url;}) || o);
if (o.url) {
self.set_image(_.find(self.records, function (record) { return record.url === o.url;}) || o);
}
});
return res;
},
+2 -1
View File
@@ -345,7 +345,8 @@ class PageConverter(werkzeug.routing.PathConverter):
query = query and query.startswith('website.') and query[8:] or query
if query:
domain += [('key', 'like', query)]
domain += ['|', ('website_id', '=', request.website.id), ('website_id', '=', False)]
website_id = request.context.get('website_id') or request.registry['website'].search(cr, uid, [], limit=1)[0]
domain += ['|', ('website_id', '=', website_id), ('website_id', '=', False)]
views = View.search_read(cr, uid, domain, fields=['key', 'priority', 'write_date'], order='name', context=context)
for view in views:
+1
View File
@@ -522,6 +522,7 @@ class website(osv.osv):
of the same.
:rtype: list({name: str, url: str})
"""
request.context = dict(request.context, **context)
router = request.httprequest.app.get_db_router(request.db)
# Force enumeration to be performed as public user
url_set = set()
@@ -27,6 +27,7 @@
</div>
<group>
<field name="name"/>
<field name="subtitle"/>
</group>
</sheet>
<div class="oe_chatter">
+7 -2
View File
@@ -295,7 +295,7 @@ class Website(models.Model):
else:
salesperson_id = request.website.salesperson_id.id
addr = partner.address_get(['delivery', 'invoice'])
sale_order = self.env['sale.order'].sudo().create({
so_data = {
'partner_id': partner.id,
'pricelist_id': pricelist_id,
'payment_term_id': partner.property_payment_term_id.id,
@@ -303,7 +303,12 @@ class Website(models.Model):
'partner_invoice_id': addr['invoice'],
'partner_shipping_id': addr['delivery'],
'user_id': salesperson_id or self.salesperson_id.id,
})
}
company = self.company_id or self.env['product.pricelist'].browse(pricelist_id).sudo().company_id
if company:
so_data['company_id'] = company.id
sale_order = self.env['sale.order'].sudo().create(so_data)
# set fiscal position
if request.website.partner_id.id != partner.id:
@@ -263,9 +263,12 @@ $('.oe_website_sale').each(function () {
}
$(oe_website_sale).on('change', 'input.js_product_change', function () {
var self = this;
var $parent = $(this).closest('.js_product');
$parent.find(".oe_default_price:first .oe_currency_value").html( price_to_str(+$(this).data('lst_price')) );
$parent.find(".oe_price:first .oe_currency_value").html(price_to_str(+$(this).data('price')) );
$.when(base.ready()).then(function() {
$parent.find(".oe_default_price:first .oe_currency_value").html( price_to_str(+$(self).data('lst_price')) );
$parent.find(".oe_price:first .oe_currency_value").html(price_to_str(+$(self).data('price')) );
});
update_product_image(this, +$(this).val());
});
@@ -287,8 +290,10 @@ $('.oe_website_sale').each(function () {
var product_id = false;
for (var k in variant_ids) {
if (_.isEmpty(_.difference(variant_ids[k][1], values))) {
$price.html(price_to_str(variant_ids[k][2]));
$default_price.html(price_to_str(variant_ids[k][3]));
$.when(base.ready()).then(function() {
$price.html(price_to_str(variant_ids[k][2]));
$default_price.html(price_to_str(variant_ids[k][3]));
});
if (variant_ids[k][3]-variant_ids[k][2]>0.2) {
$default_price.closest('.oe_website_sale').addClass("discount");
$optional_price.closest('.oe_optional').show().css('text-decoration', 'line-through');
@@ -342,9 +347,12 @@ $('.oe_website_sale').each(function () {
$('input.js_variant_change, select.js_variant_change', this).first().trigger('change');
});
var state_options = $("select[name='state_id']:visible option:not(:first)");
$("select[name='state_id']").each(function(){
$(this).data('options', $(this).find('option:not(:first)'));
});
$(oe_website_sale).on('change', "select[name='country_id']", function () {
var select = $("select[name='state_id']:visible");
var select = $("select[name='state_id']:enabled");
var state_options = select.data('options');
var selected_state = select.val();
state_options.detach();
var displayed_state = state_options.filter("[data-country_id="+($(this).val() || 0)+"]");
@@ -41,7 +41,7 @@ $('.oe_website_sale #add_to_cart, .oe_website_sale #products_grid .a-submit')
data: {lang: base.get_context().lang},
success: function (quantity) {
if (!$a.hasClass('js_goto_shop')) {
window.location.href = window.location.href.replace(/shop([\/?].*)?$/, "shop/cart");
window.location.pathname = window.location.pathname.replace(/shop([\/?].*)?$/, "shop/cart");
}
var $q = $(".my_cart_quantity");
$q.parent().parent().removeClass("hidden", !quantity);
+2
View File
@@ -29,3 +29,5 @@ Ferdinand Gassauer ferdinand.gassauer@camptocamp.com https://github.com/ferdiga
Jean-Baptiste Aubort jean-baptiste.aubort@camptocamp.com
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
+2 -1
View File
@@ -16,5 +16,6 @@ List of contributors:
- Stéphane Le Cornec stephane.lecornec@gmail.com https://github.com/coleste
- Eric Lemire elemire@users.noreply.github.com https://github.com/elemire
- Martin Malorni mmalorni@gmail.com https://github.com/mmalorni
- Gregory Savoie gs.microcom@gmail.com https://github.com/gregsavoie
- Samuel Chamberland sc.microcom@gmail.com https://github.com/sc-microcom
- Noreddine Ben Jillali nbj.microcom@gmail.com https://github.com/nbj-microcom
- Rim Ben Dhaou rbd.microcom@gmail.com https://github.com/rbd-microcom
+11
View File
@@ -0,0 +1,11 @@
United States, 2016-03-28
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,
César Castillo ceaucari@gmail.com https://github.com/ceaucari
+11
View File
@@ -0,0 +1,11 @@
Ecuador, 2016-10-02
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,
Daniel A. Mendieta P. damendieta@gmail.com https://github.com/damendieta
+11
View File
@@ -0,0 +1,11 @@
Taiwan, 2016-08-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,
JasonWu Tingyuan Wu jaronemo@msn.com https://github.com/jaronemo
+11
View File
@@ -0,0 +1,11 @@
Spain, 2016-04-15
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,
Javier Colmenero Fernández javierjcf@gmail.com https://github.com/javierjcf
+11
View File
@@ -0,0 +1,11 @@
Ecuador, 2016-12-13
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,
Juan Cristobal Lopez Arrieta juancristobal@gmail.com https://github.com/jclopezar
+11
View File
@@ -0,0 +1,11 @@
Algeria, 2016-08-11
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,
Noureddine LOUAHEDJ n.louahedj@gmail.com https://github.com/noureddineme
+11
View File
@@ -0,0 +1,11 @@
Cuba, 2016-12-01
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,
Rigoberto Martínez rigo1985@gmail.com https://github.com/rigo1985
+11
View File
@@ -0,0 +1,11 @@
China, 20160118
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,
LIN Yu yu_lin@me.com https://github.com/uynil