[MERGE] forward port branch 9.0 up to 3805eb620a
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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"/>
|
||||
|
||||
@@ -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})
|
||||
|
||||
@@ -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'
|
||||
|
||||
|
||||
@@ -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'))
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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:
|
||||
|
||||
+401
-398
File diff suppressed because it is too large
Load Diff
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"/>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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'),
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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€
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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,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
|
||||
|
||||
@@ -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,)
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
},
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user