[MERGE] forward port branch saas-14 up to b857fd4c90

This commit is contained in:
Christophe Simonis
2017-07-19 17:04:28 +02:00
28 changed files with 130 additions and 65 deletions
@@ -892,7 +892,7 @@ class AccountBankStatementLine(models.Model):
counterpart_moves = (counterpart_moves | move)
# Create The payment
payment = False
payment = self.env['account.payment']
if abs(total)>0.00001:
partner_id = self.partner_id and self.partner_id.id or False
partner_type = False
+10 -5
View File
@@ -707,7 +707,7 @@ class AccountInvoice(models.Model):
total_currency = 0
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))
currency = self.currency_id.with_context(date=self.date or self.date_invoice or fields.Date.context_today(self))
if not (line.get('currency_id') and line.get('amount_currency')):
line['currency_id'] = currency.id
line['amount_currency'] = currency.round(line['price'])
@@ -841,7 +841,6 @@ class AccountInvoice(models.Model):
if not inv.date_invoice:
inv.with_context(ctx).write({'date_invoice': fields.Date.context_today(self)})
date_invoice = inv.date_invoice
company_currency = inv.company_id.currency_id
# create move lines (one per invoice line + eventual taxes and analytic lines)
@@ -854,9 +853,9 @@ class AccountInvoice(models.Model):
name = inv.name or '/'
if inv.payment_term_id:
totlines = inv.with_context(ctx).payment_term_id.with_context(currency_id=company_currency.id).compute(total, date_invoice)[0]
totlines = inv.with_context(ctx).payment_term_id.with_context(currency_id=company_currency.id).compute(total, inv.date_invoice)[0]
res_amount_currency = total_currency
ctx['date'] = date_invoice
ctx['date'] = inv.date or inv.date_invoice
for i, t in enumerate(totlines):
if inv.currency_id != company_currency:
amount_currency = company_currency.with_context(ctx).compute(t[1], inv.currency_id)
@@ -896,7 +895,7 @@ class AccountInvoice(models.Model):
journal = inv.journal_id.with_context(ctx)
line = inv.finalize_invoice_move_lines(line)
date = inv.date or date_invoice
date = inv.date or inv.date_invoice
move_vals = {
'ref': inv.reference,
'line_ids': line,
@@ -1494,6 +1493,12 @@ class AccountPaymentTerm(models.Model):
result.append((last_date, dist))
return result
@api.multi
def unlink(self):
property_recs = self.env['ir.property'].search([('value_reference', 'in', ['account.payment.term,%s'%payment_term.id for payment_term in self])])
property_recs.unlink()
return super(AccountPaymentTerm, self).unlink()
class AccountPaymentTermLine(models.Model):
_name = "account.payment.term.line"
+8 -2
View File
@@ -57,10 +57,16 @@ Best Regards,''')
if (date.month < last_month or (date.month == last_month and date.day <= last_day)):
date = date.replace(month=last_month, day=last_day)
else:
date = date.replace(month=last_month, day=last_day, year=date.year + 1)
if last_month == 2 and last_day == 29 and (date.year + 1) % 4 != 0:
date = date.replace(month=last_month, day=28, year=date.year + 1)
else:
date = date.replace(month=last_month, day=last_day, year=date.year + 1)
date_to = date
date_from = date + timedelta(days=1)
date_from = date_from.replace(year=date_from.year - 1)
if date_from.month == 2 and date_from.day == 29:
date_from = date_from.replace(day=28, year=date_from.year - 1)
else:
date_from = date_from.replace(year=date_from.year - 1)
return {'date_from': date_from, 'date_to': date_to}
def get_new_account_code(self, current_code, old_prefix, new_prefix, digits):
+1 -1
View File
@@ -200,7 +200,7 @@
</p>
<div class="alert alert-info info_icon" role="alert">
<span class="fa fa-lightbulb-o fa-lg"/>
<p>Exchange rates can be automatically updated once a day from <strong>Yahoo Finance</strong> or the <strong>European Central Bank</strong>. You can activate this feature in the bottom of the <a t-att-href="prepare_backend_url('account.action_account_config')">Accounting Settings</a><a t-att-href="prepare_backend_url('account.action_account_config')">accounting settings</a>.</p>
<p>Exchange rates can be automatically updated once a day from <strong>Yahoo Finance</strong> or the <strong>European Central Bank</strong>. You can activate this feature in the bottom of the <a t-att-href="prepare_backend_url('account.action_account_config')">Accounting Settings</a>.</p>
</div>
</t>
<t t-call="web_planner.page">
@@ -223,7 +223,7 @@ class AccountBankStatementImport(models.TransientModel):
else:
ignored_statement_lines_import_ids.append(line_vals['unique_import_id'])
if 'balance_start' in st_vals:
st_vals['balance_start'] += line_vals['amount']
st_vals['balance_start'] += float(line_vals['amount'])
if len(filtered_st_lines) > 0:
# Remove values that won't be used to create records
+1 -1
View File
@@ -44,7 +44,7 @@
<field name="name">Submitted</field>
<field name="res_model">hr.expense.sheet</field>
<field name="description">Expense report submitted, waiting approval</field>
<field name="default" eval="False"/>
<field name="default" eval="True"/>
</record>
<record id="mt_expense_paid" model="mail.message.subtype">
<field name="name">Paid</field>
+20 -10
View File
@@ -30,7 +30,8 @@ class HrExpense(models.Model):
company_id = fields.Many2one('res.company', string='Company', readonly=True, states={'draft': [('readonly', False)], 'refused': [('readonly', False)]}, default=lambda self: self.env.user.company_id)
currency_id = fields.Many2one('res.currency', string='Currency', readonly=True, states={'draft': [('readonly', False)], 'refused': [('readonly', False)]}, default=lambda self: self.env.user.company_id.currency_id)
analytic_account_id = fields.Many2one('account.analytic.account', string='Analytic Account', states={'post': [('readonly', True)], 'done': [('readonly', True)]}, oldname='analytic_account')
account_id = fields.Many2one('account.account', string='Account', states={'post': [('readonly', True)], 'done': [('readonly', True)]}, default=lambda self: self.env['ir.property'].get('property_account_expense_categ_id', 'product.category'))
account_id = fields.Many2one('account.account', string='Account', states={'post': [('readonly', True)], 'done': [('readonly', True)]}, default=lambda self: self.env['ir.property'].get('property_account_expense_categ_id', 'product.category'),
help="An expense account is expected")
description = fields.Text()
payment_mode = fields.Selection([("own_account", "Employee (to reimburse)"), ("company_account", "Company")], default='own_account', states={'done': [('readonly', True)], 'post': [('readonly', True)]}, string="Payment By")
attachment_number = fields.Integer(compute='_compute_attachment_number', string='Number of Attachments')
@@ -418,10 +419,15 @@ class HrExpenseSheet(models.Model):
@api.model
def create(self, vals):
# Add the followers at creation, so they can be notified
if vals.get('employee_id'):
employee = self.env['hr.employee'].browse(vals['employee_id'])
users = self._get_users_to_subscribe(employee=employee) - self.env.user
vals['message_follower_ids'] = []
for partner in users.mapped('partner_id'):
vals['message_follower_ids'] += self.env['mail.followers']._add_follower_command(self._name, [], {partner.id: None}, {})[0]
sheet = super(HrExpenseSheet, self).create(vals)
self.check_consistency()
if vals.get('employee_id'):
sheet._add_followers()
return sheet
@api.multi
@@ -456,16 +462,20 @@ class HrExpenseSheet(models.Model):
return 'hr_expense.mt_expense_paid'
return super(HrExpenseSheet, self)._track_subtype(init_values)
def _add_followers(self):
user_ids = []
employee = self.employee_id
def _get_users_to_subscribe(self, employee=False):
users = self.env['res.users']
employee = employee or self.employee_id
if employee.user_id:
user_ids.append(employee.user_id.id)
users |= employee.user_id
if employee.parent_id:
user_ids.append(employee.parent_id.user_id.id)
users |= employee.parent_id.user_id
if employee.department_id and employee.department_id.manager_id and employee.parent_id != employee.department_id.manager_id:
user_ids.append(employee.department_id.manager_id.user_id.id)
self.message_subscribe_users(user_ids=user_ids)
users |= employee.department_id.manager_id.user_id
return users
def _add_followers(self):
users = self._get_users_to_subscribe()
self.message_subscribe_users(user_ids=users.ids)
@api.onchange('employee_id')
def _onchange_employee_id(self):
+1 -1
View File
@@ -60,7 +60,7 @@
</group><group>
<field name="reference"/>
<field name="date"/>
<field name="account_id"/>
<field name="account_id" domain="[('internal_type', '=', 'other')]"/>
<field name="employee_id"/>
<field name="sheet_id" invisible="1"/>
<field name="currency_id" groups="base.group_multi_currency"/>
@@ -2906,6 +2906,7 @@
<field name="name">Coupons échus à l'encaissement</field>
<field name="code">5111</field>
<field name="user_type_id" ref="account.data_account_type_liquidity"/>
<field name="reconcile" eval="True"/>
<field name="chart_template_id" ref="l10n_fr_pcg_chart_template"/>
</record>
@@ -2913,6 +2914,7 @@
<field name="name">Chèques à encaisser</field>
<field name="code">5112</field>
<field name="user_type_id" ref="account.data_account_type_liquidity"/>
<field name="reconcile" eval="True"/>
<field name="chart_template_id" ref="l10n_fr_pcg_chart_template"/>
</record>
@@ -2920,6 +2922,7 @@
<field name="name">Effets à l'encaissement</field>
<field name="code">5113</field>
<field name="user_type_id" ref="account.data_account_type_liquidity"/>
<field name="reconcile" eval="True"/>
<field name="chart_template_id" ref="l10n_fr_pcg_chart_template"/>
</record>
@@ -2927,6 +2930,7 @@
<field name="name">Effets à l'escompte</field>
<field name="code">5114</field>
<field name="user_type_id" ref="account.data_account_type_liquidity"/>
<field name="reconcile" eval="True"/>
<field name="chart_template_id" ref="l10n_fr_pcg_chart_template"/>
</record>
@@ -217,7 +217,7 @@
<field name="name">equipment.request.calendar</field>
<field name="model">maintenance.request</field>
<field name="arch" type="xml">
<calendar date_start="schedule_date" color="technician_user_id">
<calendar date_start="schedule_date" color="technician_user_id" date_delay="duration">
<field name="name"/>
<field name="technician_user_id"/>
</calendar>
+3 -4
View File
@@ -61,15 +61,14 @@ class MailMail(models.Model):
self.ensure_one()
body = super(MailMail, self).send_get_mail_body(partner=partner)
links_blacklist = ['/unsubscribe_from_list']
if self.mailing_id and body and self.statistics_ids:
for match in re.findall(URL_REGEX, self.body_html):
href = match[0]
url = match[1]
if not [s for s in links_blacklist if s in href]:
parsed = urlparse.urlparse(url, scheme='http')
if parsed.scheme.startswith('http') and parsed.path.startswith('/r/'):
new_href = href.replace(url, url + '/m/' + str(self.statistics_ids[0].id))
body = body.replace(href, new_href)
+10
View File
@@ -39,3 +39,13 @@ class MailThread(models.AbstractModel):
message_ids = [x.strip() for x in decode_smtp_header(message['References']).split()]
self.env['mail.mail.statistics'].set_replied(mail_message_ids=message_ids)
return super(MailThread, self).message_route_process(message, message_dict, routes)
@api.multi
def message_post_with_template(self, template_id, **kwargs):
# avoid having message send through `message_post*` methods being implicitly considered as
# mass-mailing
no_massmail = self.with_context(
default_mass_mailing_name=False,
default_mass_mailing_id=False,
)
return super(MailThread, no_massmail).message_post_with_template(template_id, **kwargs)
+6 -5
View File
@@ -226,11 +226,12 @@ class Repair(models.Model):
@api.multi
def action_repair_invoice_create(self):
self.action_invoice_create()
if self.invoice_method == 'b4repair':
self.action_repair_ready()
elif self.invoice_method == 'after_repair':
self.write({'state': 'done'})
for repair in self:
repair.action_invoice_create()
if repair.invoice_method == 'b4repair':
repair.action_repair_ready()
elif repair.invoice_method == 'after_repair':
repair.write({'state': 'done'})
return True
@api.multi
+2 -2
View File
@@ -256,14 +256,14 @@ class TxAuthorize(models.Model):
def authorize_s2s_capture_transaction(self):
self.ensure_one()
transaction = AuthorizeAPI(self.acquirer_id)
tree = transaction.capture(self.acquirer_reference, self.amount)
tree = transaction.capture(self.acquirer_reference or '', self.amount)
return self._authorize_s2s_validate_tree(tree)
@api.multi
def authorize_s2s_void_transaction(self):
self.ensure_one()
transaction = AuthorizeAPI(self.acquirer_id)
tree = transaction.void(self.acquirer_reference)
tree = transaction.void(self.acquirer_reference or '')
return self._authorize_s2s_validate_tree(tree)
@api.multi
+1 -1
View File
@@ -625,7 +625,7 @@ class PosOrder(models.Model):
return_picking = Picking.create(return_vals)
return_picking.message_post(body=message)
for line in order.lines.filtered(lambda l: l.product_id.type in ['product', 'consu']):
for line in order.lines.filtered(lambda l: l.product_id.type in ['product', 'consu'] and not float_is_zero(l.qty, precision_digits=l.product_id.uom_id.rounding)):
moves |= Move.create({
'name': line.name,
'product_uom': line.product_id.uom_id.id,
+4 -11
View File
@@ -10,27 +10,20 @@ class SaleOrderLine(models.Model):
@api.multi
def _compute_analytic(self, domain=None):
lines = {}
force_so_lines = self.env.context.get("force_so_lines")
if not domain:
if not self.ids and not force_so_lines:
return True
# To filter on analyic lines linked to an expense
expense_type_id = self.env.ref('account.data_account_type_expenses', raise_if_not_found=False)
expense_type_id = expense_type_id and expense_type_id.id
domain = [
('so_line', 'in', self.ids),
'|',
('amount', '<', 0),
'&',
('amount', '=', 0),
'|',
('move_id', '=', False),
('move_id.account_id.user_type_id', '=', expense_type_id)
]
domain = [('so_line', 'in', self.ids), ('amount', '<=', 0.0)]
data = self.env['account.analytic.line'].read_group(
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)
@@ -113,6 +113,7 @@ class SaleAdvancePaymentInv(models.TransientModel):
'payment_term_id': order.payment_term_id.id,
'fiscal_position_id': order.fiscal_position_id.id or order.partner_id.property_account_position_id.id,
'team_id': order.team_id.id,
'user_id': order.user_id.id,
'comment': order.note,
})
invoice.compute_taxes()
+2 -13
View File
@@ -140,20 +140,9 @@ class SaleOrderLine(models.Model):
@api.multi
def _compute_analytic(self, domain=None):
if not domain:
if not domain and self.ids:
# To filter on analyic lines linked to an expense
expense_type_id = self.env.ref('account.data_account_type_expenses', raise_if_not_found=False)
expense_type_id = expense_type_id and expense_type_id.id
domain = [
('so_line', 'in', self.ids),
'|',
'|',
('amount', '<', 0.0),
('project_id', '!=', False),
'&',
('amount', '=', 0),
'|',
('move_id', '=', False),
('move_id.account_id.user_type_id', '=', expense_type_id)
]
domain = [('so_line', 'in', self.ids), '|', ('amount', '<=', 0.0), ('project_id', '!=', False)]
return super(SaleOrderLine, self)._compute_analytic(domain=domain)
+6
View File
@@ -364,6 +364,12 @@ class Product(models.Model):
def action_view_routes(self):
return self.mapped('product_tmpl_id').action_view_routes()
@api.multi
def write(self, values):
res = super(Product, self).write(values)
if 'active' in values and not values['active'] and self.mapped('orderpoint_ids').filtered(lambda r: r.active):
raise UserError(_('You still have some active reordering rules on this product. Please archive or delete them first.'))
return res
class ProductTemplate(models.Model):
_inherit = 'product.template'
+1 -1
View File
@@ -24,7 +24,7 @@ class SaleQuoteTemplate(models.Model):
], 'Payment', help="Require immediate payment by the customer when validating the order from the website quote")
mail_template_id = fields.Many2one('mail.template', 'Confirmation Mail',
help="This e-mail template will be sent on confirmation. Leave empty to send nothing.",
domain=[('model', '=', 'sale.quote.template')])
domain=[('model', '=', 'sale.order')])
@api.multi
def open_template(self):
@@ -135,12 +135,18 @@ if(!$('.o_website_quote').length) {
case "h1":
var id = self.setElementId('quote_header_', el);
var text = self.extractText($(el));
if (!text) {
break;
}
last_li = $("<li>").append($('<a href="#'+id+'"/>').text(text)).appendTo(self.$el);
last_ul = false;
break;
case "h2":
var id = self.setElementId('quote_', el);
var text = self.extractText($(el));
if (!text) {
break;
}
if (last_li) {
if (!last_ul) {
last_ul = $("<ul class='nav'>").appendTo(last_li);
+1 -1
View File
@@ -170,7 +170,7 @@
<div class="products_pager">
<div class="row" style="width: 100%;">
<t t-call="website_sale.search">
<t t-set="search_class">pagination form-inline o_website_sale_search</t>
<t t-set="search_class" t-value="'pagination form-inline o_website_sale_search'"/>
</t>
<t t-call="website_sale.pricelist_list" />
<t t-call="website.pager" />
+14
View File
@@ -0,0 +1,14 @@
Finland, 2017-07-17
Oy Tawasta OS Technologies Ltd. agrees to the terms of the Odoo Corporate Contributor License Agreement v1.0.
We declare that we are authorized and able to make this agreement and sign this declaration.
Signed,
Jarmo Kortetjärvi jarmo.kortetjarvi@tawasta.fi https://github.com/jarmokortetjarvi
List of contributors:
Aleksi Savijoki aleksi.savijoki@tawasta.fi https://github.com/savijoki
Jarmo Kortetjärvi jarmo.kortetjarvi@tawasta.fi https://github.com/jarmokortetjarvi
+10
View File
@@ -0,0 +1,10 @@
India, 2017-07-04
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,
Rakesh Sindhav sindhav21591@gmail.com https://github.com/sindhav
+11
View File
@@ -0,0 +1,11 @@
Finland, 2017-07-19
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,
Timo Talvitie timo.talvitie@vizucom.com https://github.com/timotalvitie
+1 -1
View File
@@ -11,7 +11,7 @@ diff --git a/openacademy/models.py b/openacademy/models.py
+from datetime import timedelta
from odoo import models, fields, api, exceptions
class Session(models.Model):
class Course(models.Model):
@@ -55,6 +56,8 @@ class Session(models.Model):
attendee_ids = fields.Many2many('res.partner', string="Attendees")
+1 -1
View File
@@ -17,7 +17,7 @@ Index: addons/openacademy/views/openacademy.xml
===================================================================
--- addons.orig/openacademy/views/openacademy.xml 2014-08-28 14:21:58.627015606 +0200
+++ addons/openacademy/views/openacademy.xml 2014-08-28 14:21:58.623015606 +0200
@@ -169,11 +169,57 @@
@@ -168,11 +168,57 @@
</field>
</record>
+2 -2
View File
@@ -30,11 +30,11 @@ dictionary, where each key specifies module metadatum.
""",
# data files always loaded at installation
'data': [
'mymodule_view.xml',
'views/mymodule_view.xml',
],
# data files containing optionally loaded demonstration data
'demo': [
'demo_data.xml',
'demo/demo_data.xml',
],
}