Skip to content

Commit

Permalink
[MERGE] forward port branch saas-12.1 up to f00c490
Browse files Browse the repository at this point in the history
  • Loading branch information
KangOl committed Mar 26, 2019
2 parents 02f5b65 + f00c490 commit 5dbf7bf
Show file tree
Hide file tree
Showing 49 changed files with 571 additions and 203 deletions.
3 changes: 1 addition & 2 deletions addons/account/models/account.py
Expand Up @@ -581,8 +581,7 @@ def _get_alias_values(self, alias_name=None):
if self.company_id != self.env.ref('base.main_company'): if self.company_id != self.env.ref('base.main_company'):
alias_name += '-' + str(self.company_id.name) alias_name += '-' + str(self.company_id.name)
return { return {
'alias_defaults': {'type': 'in_invoice'}, 'alias_defaults': {'type': 'in_invoice', 'company_id': self.company_id.id},
'alias_user_id': self.env.user.id,
'alias_parent_thread_id': self.id, 'alias_parent_thread_id': self.id,
'alias_name': re.sub(r'[^\w]+', '-', alias_name) 'alias_name': re.sub(r'[^\w]+', '-', alias_name)
} }
Expand Down
12 changes: 6 additions & 6 deletions addons/account/models/account_invoice.py
Expand Up @@ -1928,12 +1928,12 @@ def _prepare_invoice_line(self):
} }
return data return data


@api.model @api.model_create_multi
def create(self, vals): def create(self, vals_list):
if vals.get('display_type', self.default_get(['display_type'])['display_type']): for vals in vals_list:
vals.update(price_unit=0, account_id=False, quantity=0) if vals.get('display_type', self.default_get(['display_type'])['display_type']):

vals.update(price_unit=0, account_id=False, quantity=0)
return super(AccountInvoiceLine, self).create(vals) return super(AccountInvoiceLine, self).create(vals_list)


@api.multi @api.multi
def write(self, values): def write(self, values):
Expand Down
3 changes: 2 additions & 1 deletion addons/account/report/account_invoice_report.py
Expand Up @@ -114,7 +114,8 @@ def _sub_select(self):
ai.partner_bank_id, ai.partner_bank_id,
SUM ((invoice_type.sign_qty * ail.quantity) / u.factor * u2.factor) AS product_qty, SUM ((invoice_type.sign_qty * ail.quantity) / u.factor * u2.factor) AS product_qty,
SUM(ail.price_subtotal_signed * invoice_type.sign) AS price_total, SUM(ail.price_subtotal_signed * invoice_type.sign) AS price_total,
SUM(ai.amount_total * invoice_type.sign) AS amount_total, (ai.amount_total * invoice_type.sign) / (SELECT count(*) FROM account_invoice_line l where invoice_id = ai.id) *
count(*) * invoice_type.sign AS amount_total,
SUM(ABS(ail.price_subtotal_signed)) / CASE SUM(ABS(ail.price_subtotal_signed)) / CASE
WHEN SUM(ail.quantity / u.factor * u2.factor) <> 0::numeric WHEN SUM(ail.quantity / u.factor * u2.factor) <> 0::numeric
THEN SUM(ail.quantity / u.factor * u2.factor) THEN SUM(ail.quantity / u.factor * u2.factor)
Expand Down
2 changes: 1 addition & 1 deletion addons/auth_signup/data/auth_signup_data.xml
Expand Up @@ -142,7 +142,7 @@
% set website_url = object.env['ir.config_parameter'].sudo().get_param('web.base.url') % set website_url = object.env['ir.config_parameter'].sudo().get_param('web.base.url')
Your Odoo domain is: <b><a href='${website_url}'>${website_url}</a></b><br /> Your Odoo domain is: <b><a href='${website_url}'>${website_url}</a></b><br />
Your sign in email is: <b><a href="/web/login?login=${object.email}" target="_blank">${object.email}</a></b><br /><br /> Your sign in email is: <b><a href="/web/login?login=${object.email}" target="_blank">${object.email}</a></b><br /><br />
Never heard of Odoo? It’s a all-in-one business software loved by 3+ million users. It will considerably improve your experience at work and increase your productivity. Never heard of Odoo? It’s an all-in-one business software loved by 3+ million users. It will considerably improve your experience at work and increase your productivity.
<br /><br /> <br /><br />
Have a look at the <a href="https://www.odoo.com/page/tour?utm_source=db&amp;utm_medium=auth" style="color: #875A7B;">Odoo Tour</a> to discover the tool. Have a look at the <a href="https://www.odoo.com/page/tour?utm_source=db&amp;utm_medium=auth" style="color: #875A7B;">Odoo Tour</a> to discover the tool.
<br /><br /> <br /><br />
Expand Down
12 changes: 10 additions & 2 deletions addons/base_iban/models/res_partner_bank.py
Expand Up @@ -70,13 +70,21 @@ def get_bban(self):
@api.model @api.model
def create(self, vals): def create(self, vals):
if vals.get('acc_number'): if vals.get('acc_number'):
vals['acc_number'] = pretty_iban(normalize_iban(vals['acc_number'])) try:
validate_iban(vals['acc_number'])
vals['acc_number'] = pretty_iban(normalize_iban(vals['acc_number']))
except ValidationError:
pass
return super(ResPartnerBank, self).create(vals) return super(ResPartnerBank, self).create(vals)


@api.multi @api.multi
def write(self, vals): def write(self, vals):
if vals.get('acc_number'): if vals.get('acc_number'):
vals['acc_number'] = pretty_iban(normalize_iban(vals['acc_number'])) try:
validate_iban(vals['acc_number'])
vals['acc_number'] = pretty_iban(normalize_iban(vals['acc_number']))
except ValidationError:
pass
return super(ResPartnerBank, self).write(vals) return super(ResPartnerBank, self).write(vals)


@api.one @api.one
Expand Down
7 changes: 7 additions & 0 deletions addons/crm/models/crm_lead.py
Expand Up @@ -277,6 +277,13 @@ def _onchange_state(self):
if self.state_id: if self.state_id:
self.country_id = self.state_id.country_id.id self.country_id = self.state_id.country_id.id


@api.onchange('country_id')
def _onchange_country_id(self):
res = {'domain': {'state_id': []}}
if self.country_id:
res['domain']['state_id'] = [('country_id', '=', self.country_id.id)]
return res

# ---------------------------------------- # ----------------------------------------
# ORM override (CRUD, fields_view_get, ...) # ORM override (CRUD, fields_view_get, ...)
# ---------------------------------------- # ----------------------------------------
Expand Down
13 changes: 9 additions & 4 deletions addons/crm/models/crm_team.py
Expand Up @@ -48,13 +48,18 @@ def _compute_unassigned_leads_count(self):
team.unassigned_leads_count = counts.get(team.id, 0) team.unassigned_leads_count = counts.get(team.id, 0)


def _compute_opportunities(self): def _compute_opportunities(self):
opportunity_data = self.env['crm.lead'].read_group([ opportunity_data = self.env['crm.lead'].search([
('team_id', 'in', self.ids), ('team_id', 'in', self.ids),
('probability', '<', 100), ('probability', '<', 100),
('type', '=', 'opportunity'), ('type', '=', 'opportunity'),
], ['planned_revenue', 'team_id'], ['team_id']) ]).read(['planned_revenue', 'team_id'])
counts = {datum['team_id'][0]: datum['team_id_count'] for datum in opportunity_data} counts = {}
amounts = {datum['team_id'][0]: (datum['planned_revenue']) for datum in opportunity_data} amounts = {}
for datum in opportunity_data:
counts.setdefault(datum['team_id'][0], 0)
amounts.setdefault(datum['team_id'][0], 0)
counts[datum['team_id'][0]] += 1
amounts[datum['team_id'][0]] += (datum.get('planned_revenue', 0))
for team in self: for team in self:
team.opportunities_count = counts.get(team.id, 0) team.opportunities_count = counts.get(team.id, 0)
team.opportunities_amount = amounts.get(team.id, 0) team.opportunities_amount = amounts.get(team.id, 0)
Expand Down
2 changes: 1 addition & 1 deletion addons/fleet/models/fleet_vehicle_cost.py
Expand Up @@ -279,7 +279,7 @@ def scheduler_manage_contract_expiration(self):
'fleet.mail_act_fleet_contract_to_renew', contract.expiration_date, 'fleet.mail_act_fleet_contract_to_renew', contract.expiration_date,
user_id=contract.user_id.id) user_id=contract.user_id.id)


expired_contracts = self.search([('state', '!=', 'expired'), ('expiration_date', '<',fields.Date.today() )]) expired_contracts = self.search([('state', 'not in', ['expired', 'closed']), ('expiration_date', '<',fields.Date.today() )])
expired_contracts.write({'state': 'expired'}) expired_contracts.write({'state': 'expired'})


futur_contracts = self.search([('state', 'not in', ['futur', 'closed']), ('start_date', '>', fields.Date.today())]) futur_contracts = self.search([('state', 'not in', ['futur', 'closed']), ('start_date', '>', fields.Date.today())])
Expand Down
6 changes: 6 additions & 0 deletions addons/hr_holidays/static/scss/hr_leave_mobile.scss
@@ -0,0 +1,6 @@
@include media-breakpoint-down(sm) {
.o_hr_holidays_dates {
display: flex;
flex-flow: column;
}
}
13 changes: 8 additions & 5 deletions addons/hr_holidays/views/hr_leave_template.xml
@@ -1,8 +1,11 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<odoo> <odoo>
<template id="assets_backend" inherit_id="web.assets_backend"> <template id="assets_backend" inherit_id="web.assets_backend">
<xpath expr="script[last()]" position="after"> <xpath expr="script[last()]" position="after">
<script type="text/javascript" src="/hr_holidays/static/src/js/time_off_calendar.js"></script> <script type="text/javascript" src="/hr_holidays/static/src/js/time_off_calendar.js"></script>
</xpath> </xpath>
</template> <xpath expr="link[last()]" position="after">
<link rel="stylesheet" type="text/scss" href="/hr_holidays/static/scss/hr_leave_mobile.scss"/>
</xpath>
</template>
</odoo> </odoo>
2 changes: 1 addition & 1 deletion addons/hr_holidays/views/hr_leave_views.xml
Expand Up @@ -168,7 +168,7 @@
<div> <div>
<field name="date_from" invisible="1"/> <field name="date_from" invisible="1"/>
<field name="date_to" invisible="1"/> <field name="date_to" invisible="1"/>
<div class="o_row o_row_readonly"> <div class="o_row o_row_readonly o_hr_holidays_dates">
<field name="request_date_from" class="oe_inline" <field name="request_date_from" class="oe_inline"
attrs="{'readonly': [('state', 'not in', ('draft', 'confirm'))]}"/> attrs="{'readonly': [('state', 'not in', ('draft', 'confirm'))]}"/>
<span class="oe_inline" <span class="oe_inline"
Expand Down
2 changes: 1 addition & 1 deletion addons/hr_timesheet/models/hr_timesheet.py
Expand Up @@ -148,7 +148,7 @@ def _timesheet_postprocess_values(self, values):
:return: a dictionary mapping each record id to its corresponding :return: a dictionary mapping each record id to its corresponding
dictionnary values to write (may be empty). dictionnary values to write (may be empty).
""" """
result = dict.fromkeys(self.ids, dict()) result = {id_: {} for id_ in self.ids}
sudo_self = self.sudo() # this creates only one env for all operation that required sudo() sudo_self = self.sudo() # this creates only one env for all operation that required sudo()
# (re)compute the amount (depending on unit_amount, employee_id for the cost, and account_id for currency) # (re)compute the amount (depending on unit_amount, employee_id for the cost, and account_id for currency)
if any([field_name in values for field_name in ['unit_amount', 'employee_id', 'account_id']]): if any([field_name in values for field_name in ['unit_amount', 'employee_id', 'account_id']]):
Expand Down
32 changes: 32 additions & 0 deletions addons/hr_timesheet/tests/test_timesheet.py
Expand Up @@ -232,3 +232,35 @@ def test_transfert_project(self):
self.task1.write({ self.task1.write({
'project_id': False 'project_id': False
}) })

def test_recompute_amount_for_multiple_timesheets(self):
""" Check that amount is recomputed correctly when setting unit_amount for multiple timesheets at once. """
Timesheet = self.env['account.analytic.line']
self.empl_employee.timesheet_cost = 5.0
self.empl_employee2.timesheet_cost = 6.0
# create a timesheet for each employee
timesheet_1 = Timesheet.sudo(self.user_employee).create({
'project_id': self.project_customer.id,
'task_id': self.task1.id,
'name': '/',
'unit_amount': 1,
})
timesheet_2 = Timesheet.sudo(self.user_employee2).create({
'project_id': self.project_customer.id,
'task_id': self.task1.id,
'name': '/',
'unit_amount': 1,
})
timesheets = timesheet_1 + timesheet_2

# increase unit_amount to trigger amount recomputation
timesheets.sudo().write({
'unit_amount': 2,
})

# since timesheet costs are different for both employees, we should get different amounts
self.assertRecordValues(timesheets, [{
'amount': -10.0,
}, {
'amount': -12.0,
}])
5 changes: 3 additions & 2 deletions addons/l10n_tr/__manifest__.py
Expand Up @@ -13,8 +13,8 @@
* Sihirbaz sizden hesap planı şablonu, planın kurulacağı şirket, banka hesap * Sihirbaz sizden hesap planı şablonu, planın kurulacağı şirket, banka hesap
bilgileriniz, ilgili para birimi gibi bilgiler isteyecek. bilgileriniz, ilgili para birimi gibi bilgiler isteyecek.
""", """,
'author': 'Ahmet Altınışık', 'author': 'Ahmet Altınışık, Can Tecim',
'maintainer':'https://launchpad.net/~openerp-turkey', 'maintainer':'https://launchpad.net/~openerp-turkey, http://www.cantecim.com',
'depends': [ 'depends': [
'account', 'account',
], ],
Expand All @@ -25,5 +25,6 @@
'data/account_data.xml', 'data/account_data.xml',
'data/account_tax_template_data.xml', 'data/account_tax_template_data.xml',
'data/account_chart_template_data.xml', 'data/account_chart_template_data.xml',
'data/res.country.state.csv'
], ],
} }
82 changes: 82 additions & 0 deletions addons/l10n_tr/data/res.country.state.csv
@@ -0,0 +1,82 @@
"id","country_id:id","name","code"
state_tr_01,base.tr,"Adana","01"
state_tr_02,base.tr,"Adıyaman","02"
state_tr_03,base.tr,"Afyon","03"
state_tr_04,base.tr,"Ağrı","04"
state_tr_05,base.tr,"Amasya","05"
state_tr_06,base.tr,"Ankara","06"
state_tr_07,base.tr,"Antalya","07"
state_tr_08,base.tr,"Artvin","08"
state_tr_09,base.tr,"Aydın","09"
state_tr_10,base.tr,"Balıkesir","10"
state_tr_11,base.tr,"Bilecik","11"
state_tr_12,base.tr,"Bingöl","12"
state_tr_13,base.tr,"Bitlis","13"
state_tr_14,base.tr,"Bolu","14"
state_tr_15,base.tr,"Burdur","15"
state_tr_16,base.tr,"Bursa","16"
state_tr_17,base.tr,"Çanakkale","17"
state_tr_18,base.tr,"Çankırı","18"
state_tr_19,base.tr,"Çorum","19"
state_tr_20,base.tr,"Denizli","20"
state_tr_21,base.tr,"Diyarbakır","21"
state_tr_22,base.tr,"Edirne","22"
state_tr_23,base.tr,"Elazığ","23"
state_tr_24,base.tr,"Erzincan","24"
state_tr_25,base.tr,"Erzurum","25"
state_tr_26,base.tr,"Eskişehir","26"
state_tr_27,base.tr,"Gaziantep","27"
state_tr_28,base.tr,"Giresun","28"
state_tr_29,base.tr,"Gümüşhane","29"
state_tr_30,base.tr,"Hakkari","30"
state_tr_31,base.tr,"Hatay","31"
state_tr_32,base.tr,"Isparta","32"
state_tr_33,base.tr,"İçel","33"
state_tr_34,base.tr,"İstanbul","34"
state_tr_35,base.tr,"İzmir","35"
state_tr_36,base.tr,"Kars","36"
state_tr_37,base.tr,"Kastamonu","37"
state_tr_38,base.tr,"Kayseri","38"
state_tr_39,base.tr,"Kırklareli","39"
state_tr_40,base.tr,"Kırşehir","40"
state_tr_41,base.tr,"Kocaeli","41"
state_tr_42,base.tr,"Konya","42"
state_tr_43,base.tr,"Kütahya","43"
state_tr_44,base.tr,"Malatya","44"
state_tr_45,base.tr,"Manisa","45"
state_tr_46,base.tr,"K.maraş","46"
state_tr_47,base.tr,"Mardin","47"
state_tr_48,base.tr,"Muğla","48"
state_tr_49,base.tr,"Muş","49"
state_tr_50,base.tr,"Nevşehir","50"
state_tr_51,base.tr,"Niğde","51"
state_tr_52,base.tr,"Ordu","52"
state_tr_53,base.tr,"Rize","53"
state_tr_54,base.tr,"Sakarya","54"
state_tr_55,base.tr,"Samsun","55"
state_tr_56,base.tr,"Siirt","56"
state_tr_57,base.tr,"Sinop","57"
state_tr_58,base.tr,"Sivas","58"
state_tr_59,base.tr,"Tekirdağ","59"
state_tr_60,base.tr,"Tokat","60"
state_tr_61,base.tr,"Trabzon","61"
state_tr_62,base.tr,"Tunceli","62"
state_tr_63,base.tr,"Şanlıurfa","63"
state_tr_64,base.tr,"Uşak","64"
state_tr_65,base.tr,"Van","65"
state_tr_66,base.tr,"Yozgat","66"
state_tr_67,base.tr,"Zonguldak","67"
state_tr_68,base.tr,"Aksaray","68"
state_tr_69,base.tr,"Bayburt","69"
state_tr_70,base.tr,"Karaman","70"
state_tr_71,base.tr,"Kırıkkale","71"
state_tr_72,base.tr,"Batman","72"
state_tr_73,base.tr,"Şırnak","73"
state_tr_74,base.tr,"Bartın","74"
state_tr_75,base.tr,"Ardahan","75"
state_tr_76,base.tr,"Iğdır","76"
state_tr_77,base.tr,"Yalova","77"
state_tr_78,base.tr,"Karabük","78"
state_tr_79,base.tr,"Kilis","79"
state_tr_80,base.tr,"Osmaniye","80"
state_tr_81,base.tr,"Düzce","81"
11 changes: 7 additions & 4 deletions addons/mail/models/mail_activity.py
Expand Up @@ -501,19 +501,21 @@ def get_activity_data(self, res_model, domain):
activity_domain.append(('res_id', 'in', res.ids)) activity_domain.append(('res_id', 'in', res.ids))
grouped_activities = self.env['mail.activity'].read_group( grouped_activities = self.env['mail.activity'].read_group(
activity_domain, activity_domain,
['res_id', 'activity_type_id', 'res_name:max(res_name)', 'ids:array_agg(id)', 'date_deadline:min(date_deadline)'], ['res_id', 'activity_type_id', 'ids:array_agg(id)', 'date_deadline:min(date_deadline)'],
['res_id', 'activity_type_id'], ['res_id', 'activity_type_id'],
lazy=False) lazy=False)
# filter out unreadable records
if not domain:
res_ids = tuple(a['res_id'] for a in grouped_activities)
res = self.env[res_model].search([('id', 'in', res_ids)])
grouped_activities = [a for a in grouped_activities if a['res_id'] in res.ids]
activity_type_ids = self.env['mail.activity.type'] activity_type_ids = self.env['mail.activity.type']
res_id_to_name = {}
res_id_to_deadline = {} res_id_to_deadline = {}
activity_data = defaultdict(dict) activity_data = defaultdict(dict)
for group in grouped_activities: for group in grouped_activities:
res_id = group['res_id'] res_id = group['res_id']
res_name = group['res_name']
activity_type_id = group['activity_type_id'][0] activity_type_id = group['activity_type_id'][0]
activity_type_ids |= self.env['mail.activity.type'].browse(activity_type_id) # we will get the name when reading mail_template_ids activity_type_ids |= self.env['mail.activity.type'].browse(activity_type_id) # we will get the name when reading mail_template_ids
res_id_to_name[res_id] = res_name
res_id_to_deadline[res_id] = group['date_deadline'] if (res_id not in res_id_to_deadline or group['date_deadline'] < res_id_to_deadline[res_id]) else res_id_to_deadline[res_id] res_id_to_deadline[res_id] = group['date_deadline'] if (res_id not in res_id_to_deadline or group['date_deadline'] < res_id_to_deadline[res_id]) else res_id_to_deadline[res_id]
state = self._compute_state_from_date(group['date_deadline'], self.user_id.sudo().tz) state = self._compute_state_from_date(group['date_deadline'], self.user_id.sudo().tz)
activity_data[res_id][activity_type_id] = { activity_data[res_id][activity_type_id] = {
Expand All @@ -523,6 +525,7 @@ def get_activity_data(self, res_model, domain):
'o_closest_deadline': group['date_deadline'], 'o_closest_deadline': group['date_deadline'],
} }
res_ids_sorted = sorted(res_id_to_deadline, key=lambda item: res_id_to_deadline[item]) res_ids_sorted = sorted(res_id_to_deadline, key=lambda item: res_id_to_deadline[item])
res_id_to_name = dict(self.env[res_model].browse(res_ids_sorted).name_get())
activity_type_infos = [] activity_type_infos = []
for elem in sorted(activity_type_ids, key=lambda item: item.sequence): for elem in sorted(activity_type_ids, key=lambda item: item.sequence):
mail_template_info = [] mail_template_info = []
Expand Down

0 comments on commit 5dbf7bf

Please sign in to comment.