[MERGE] forward port branch saas-16 up to 0af13af5de

This commit is contained in:
Christophe Simonis
2018-01-02 16:54:38 +01:00
41 changed files with 315 additions and 120 deletions
+7 -4
View File
@@ -940,7 +940,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 or self.date_invoice or fields.Date.context_today(self))
currency = self.currency_id.with_context(date=self._get_currency_rate_date() 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'])
@@ -1090,7 +1090,7 @@ class AccountInvoice(models.Model):
if inv.payment_term_id:
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'] = inv.date or inv.date_invoice
ctx['date'] = inv._get_currency_rate_date()
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)
@@ -1289,6 +1289,9 @@ class AccountInvoice(models.Model):
copy_fields = ['company_id', 'user_id', 'fiscal_position_id']
return self._get_refund_common_fields() + self._get_refund_prepare_fields() + copy_fields
def _get_currency_rate_date(self):
return self.date or self.date_invoice
@api.model
def _prepare_refund(self, invoice, date_invoice=None, date=None, description=None, journal_id=None):
""" Prepare the dict of values to create the new credit note from the invoice.
@@ -1449,7 +1452,7 @@ class AccountInvoiceLine(models.Model):
@api.one
@api.depends('price_unit', 'discount', 'invoice_line_tax_ids', 'quantity',
'product_id', 'invoice_id.partner_id', 'invoice_id.currency_id', 'invoice_id.company_id',
'invoice_id.date_invoice')
'invoice_id.date_invoice', 'invoice_id.date')
def _compute_price(self):
currency = self.invoice_id and self.invoice_id.currency_id or None
price = self.price_unit * (1 - (self.discount or 0.0) / 100.0)
@@ -1459,7 +1462,7 @@ class AccountInvoiceLine(models.Model):
self.price_subtotal = price_subtotal_signed = taxes['total_excluded'] if taxes else self.quantity * price
self.price_total = taxes['total_included'] if taxes else self.price_subtotal
if self.invoice_id.currency_id and self.invoice_id.currency_id != self.invoice_id.company_id.currency_id:
price_subtotal_signed = self.invoice_id.currency_id.with_context(date=self.invoice_id.date_invoice).compute(price_subtotal_signed, self.invoice_id.company_id.currency_id)
price_subtotal_signed = self.invoice_id.currency_id.with_context(date=self.invoice_id._get_currency_rate_date()).compute(price_subtotal_signed, self.invoice_id.company_id.currency_id)
sign = self.invoice_id.type in ['in_refund', 'out_refund'] and -1 or 1
self.price_subtotal_signed = price_subtotal_signed * sign
+2
View File
@@ -171,6 +171,8 @@ class AccountMove(models.Model):
if not move.journal_id.update_posted:
raise UserError(_('You cannot modify a posted entry of this journal.\nFirst you should set the journal to allow cancelling entries.'))
if self.ids:
self.check_access_rights('write')
self.check_access_rule('write')
self._check_lock_date()
self._cr.execute('UPDATE account_move '\
'SET state=%s '\
+1 -1
View File
@@ -16,7 +16,7 @@
</p>
<t t-if="Lines[o.id]">
<span t-field="res_company.overdue_msg"/>
<span t-field="res_company.with_context(lang=o.lang).overdue_msg"/>
<table class="table table-condensed" t-foreach="Lines[o.id]" t-as="currency">
<thead>
<tr>
@@ -97,7 +97,7 @@
<field name="crossovered_budget_line" context="{'default_date_from': date_from,'default_date_to': date_to}" colspan="4" nolabel="1" attrs="{'readonly':[('state','!=','draft')]}">
<tree string="Budget Lines">
<field name="general_budget_id"/>
<field name="analytic_account_id" groups="analytic.group_analytic_accounting"/>
<field name="analytic_account_id" groups="analytic.group_analytic_accounting" required="1"/>
<field name="date_from"/>
<field name="date_to"/>
<field name="paid_date" groups="base.group_no_one"/>
@@ -111,7 +111,7 @@
<group>
<field name="general_budget_id"/>
<field name="planned_amount" widget="monetary"/>
<field name="analytic_account_id" groups="analytic.group_analytic_accounting"/>
<field name="analytic_account_id" groups="analytic.group_analytic_accounting" required="1"/>
</group>
<group>
<label for="date_from" string="Period"/>
+1 -1
View File
@@ -578,7 +578,7 @@ class Import(models.TransientModel):
# Check that currency exists
currency = self.env['res.currency'].search([('symbol', '=', split_value[currency_index].strip())])
if len(currency):
return split_value[currency_index + 1 % 2] if not negative else '-' + split_value[currency_index + 1 % 2]
return split_value[(currency_index + 1) % 2] if not negative else '-' + split_value[(currency_index + 1) % 2]
# Otherwise it is not a float with a currency symbol
return False
+1 -1
View File
@@ -21,7 +21,7 @@ class FleetVehicle(models.Model):
company_id = fields.Many2one('res.company', 'Company')
license_plate = fields.Char(required=True, help='License plate number of the vehicle (i = plate number for a car)')
vin_sn = fields.Char('Chassis Number', help='Unique number written on the vehicle motor (VIN/SN number)', copy=False)
driver_id = fields.Many2one('res.partner', 'Driver', help='Driver of the vehicle')
driver_id = fields.Many2one('res.partner', 'Driver', help='Driver of the vehicle', copy=False)
model_id = fields.Many2one('fleet.vehicle.model', 'Model', required=True, help='Model of the vehicle')
log_fuel = fields.One2many('fleet.vehicle.log.fuel', 'vehicle_id', 'Fuel Logs')
log_services = fields.One2many('fleet.vehicle.log.services', 'vehicle_id', 'Services Logs')
@@ -15,6 +15,8 @@ class AccountMoveLine(models.Model):
res = super(AccountMoveLine, self).reconcile(writeoff_acc_id=writeoff_acc_id, writeoff_journal_id=writeoff_journal_id)
account_move_ids = [l.move_id.id for l in self if float_compare(l.move_id.matched_percentage, 1, precision_digits=5) == 0]
if account_move_ids:
expense_sheets = self.env['hr.expense.sheet'].search([('account_move_id', 'in', account_move_ids)])
expense_sheets = self.env['hr.expense.sheet'].search([
('account_move_id', 'in', account_move_ids), ('state', '!=', 'done')
])
expense_sheets.set_to_paid()
return res
+10 -8
View File
@@ -433,18 +433,20 @@ class HrExpenseSheet(models.Model):
@api.multi
def check_consistency(self):
if any(sheet.employee_id != self[0].employee_id for sheet in self):
raise UserError(_("Expenses must belong to the same Employee."))
expense_lines = self.mapped('expense_line_ids')
if expense_lines and any(expense.payment_mode != expense_lines[0].payment_mode for expense in expense_lines):
raise UserError(_("Expenses must have been paid by the same entity (Company or employee)"))
for rec in self:
expense_lines = rec.expense_line_ids
if not expense_lines:
continue
if any(expense.employee_id != rec.employee_id for expense in expense_lines):
raise UserError(_("Expenses must belong to the same Employee."))
if any(expense.payment_mode != expense_lines[0].payment_mode for expense in expense_lines):
raise UserError(_("Expenses must have been paid by the same entity (Company or employee)"))
@api.model
def create(self, vals):
self._create_set_followers(vals)
sheet = super(HrExpenseSheet, self).create(vals)
self.check_consistency()
sheet.check_consistency()
return sheet
@api.multi
@@ -586,7 +588,7 @@ class HrExpenseSheet(models.Model):
return res
@api.one
@api.constrains('expense_line_ids')
@api.constrains('expense_line_ids', 'employee_id')
def _check_employee(self):
employee_ids = self.expense_line_ids.mapped('employee_id')
if len(employee_ids) > 1 or (len(employee_ids) == 1 and employee_ids != self.employee_id):
@@ -373,7 +373,7 @@ elif salary &lt;= 2461.27:
<field name="code">Red.Spouse.Net</field>
<field name="sequence">148</field>
<field name="condition_select">python</field>
<field name="condition_python">result = (employee.marital == 'married' and employee.spouse_net_revenue &lt;= 221.0) and (categories.PP + categories.PPRed + categories.FamRed + categories.ChA &lt; 0.0)</field>
<field name="condition_python">result = (employee.marital == 'married' and employee.spouse_fiscal_status=='with income' and employee.spouse_net_revenue &lt;= 221.0) and (categories.PP + categories.PPRed + categories.FamRed + categories.ChA &lt; 0.0)</field>
<field name="amount_select">code</field>
<field name="amount_python_compute">result = min(110.50, - (categories.PP + categories.PPRed + categories.FamRed + categories.ChA))</field>
</record>
@@ -384,7 +384,7 @@ elif salary &lt;= 2461.27:
<field name="code">Red.Spouse.Oth.Net</field>
<field name="sequence">149</field>
<field name="condition_select">python</field>
<field name="condition_python">result = (employee.marital == 'married' and not employee.spouse_net_revenue and employee.spouse_other_net_revenue &lt;= 440.0) and (categories.PP + categories.PPRed + categories.FamRed + categories.ChA &lt; 0.0)</field>
<field name="condition_python">result = (employee.marital == 'married' and employee.spouse_fiscal_status=='with income' and not employee.spouse_net_revenue and employee.spouse_other_net_revenue &lt;= 440.0) and (categories.PP + categories.PPRed + categories.FamRed + categories.ChA &lt; 0.0)</field>
<field name="amount_select">code</field>
<field name="amount_python_compute">result = min(220.0, - (categories.PP + categories.PPRed + categories.FamRed + categories.ChA))</field>
</record>
@@ -215,23 +215,23 @@ class HrEmployee(models.Model):
spouse_fiscal_status = fields.Selection([
('without income', 'Without Income'),
('with income', 'With Income')
], string='Tax status for spouse')
disabled = fields.Boolean(string="Disabled", help="If the employee is declared disabled by law")
disabled_spouse_bool = fields.Boolean(string='Disabled Spouse', help='if recipient spouse is declared disabled by law')
disabled_children_bool = fields.Boolean(string='Disabled Children', help='if recipient children is/are declared disabled by law')
resident_bool = fields.Boolean(string='Nonresident', help='if recipient lives in a foreign country')
disabled_children_number = fields.Integer('Number of disabled children')
dependent_children = fields.Integer(compute='_compute_dependent_children', string='Considered number of dependent children')
other_dependent_people = fields.Boolean(string="Other Dependent People", help="If other people are dependent on the employee")
other_senior_dependent = fields.Integer('# seniors (>=65)', help="Number of seniors dependent on the employee, including the disabled ones")
other_disabled_senior_dependent = fields.Integer('# disabled seniors (>=65)')
other_juniors_dependent = fields.Integer('# people (<65)', help="Number of juniors dependent on the employee, including the disabled ones")
other_disabled_juniors_dependent = fields.Integer('# disabled people (<65)')
dependent_seniors = fields.Integer(compute='_compute_dependent_people', string="Considered number of dependent seniors")
dependent_juniors = fields.Integer(compute='_compute_dependent_people', string="Considered number of dependent juniors")
spouse_net_revenue = fields.Float(string="Spouse Net Revenue", help="Own professional income, other than pensions, annuities or similar income")
], string='Tax status for spouse', groups="hr.group_hr_user")
disabled = fields.Boolean(string="Disabled", help="If the employee is declared disabled by law", groups="hr.group_hr_user")
disabled_spouse_bool = fields.Boolean(string='Disabled Spouse', help='if recipient spouse is declared disabled by law', groups="hr.group_hr_user")
disabled_children_bool = fields.Boolean(string='Disabled Children', help='if recipient children is/are declared disabled by law', groups="hr.group_hr_user")
resident_bool = fields.Boolean(string='Nonresident', help='if recipient lives in a foreign country', groups="hr.group_hr_user")
disabled_children_number = fields.Integer('Number of disabled children', groups="hr.group_hr_user")
dependent_children = fields.Integer(compute='_compute_dependent_children', string='Considered number of dependent children', groups="hr.group_hr_user")
other_dependent_people = fields.Boolean(string="Other Dependent People", help="If other people are dependent on the employee", groups="hr.group_hr_user")
other_senior_dependent = fields.Integer('# seniors (>=65)', help="Number of seniors dependent on the employee, including the disabled ones", groups="hr.group_hr_user")
other_disabled_senior_dependent = fields.Integer('# disabled seniors (>=65)', groups="hr.group_hr_user")
other_juniors_dependent = fields.Integer('# people (<65)', help="Number of juniors dependent on the employee, including the disabled ones", groups="hr.group_hr_user")
other_disabled_juniors_dependent = fields.Integer('# disabled people (<65)', groups="hr.group_hr_user")
dependent_seniors = fields.Integer(compute='_compute_dependent_people', string="Considered number of dependent seniors", groups="hr.group_hr_user")
dependent_juniors = fields.Integer(compute='_compute_dependent_people', string="Considered number of dependent juniors", groups="hr.group_hr_user")
spouse_net_revenue = fields.Float(string="Spouse Net Revenue", help="Own professional income, other than pensions, annuities or similar income", groups="hr.group_hr_user")
spouse_other_net_revenue = fields.Float(string="Spouse Other Net Revenue",
help='Own professional income which is exclusively composed of pensions, annuities or similar income')
help='Own professional income which is exclusively composed of pensions, annuities or similar income', groups="hr.group_hr_user")
@api.constrains('spouse_fiscal_status', 'spouse_net_revenue', 'spouse_other_net_revenue')
def _check_spouse_revenue(self):
@@ -141,7 +141,7 @@
<field name="disabled_children_bool"/>
<field name="disabled_children_number" attrs="{'invisible':[('disabled_children_bool','!=',True)]}" colspan="1" help="number of dependent children declared as disabled"/>
<field name="dependent_children" invisible="1"/>
<field name="other_dependent_people"/>
<field name="other_dependent_people" groups="hr.group_hr_user"/>
<field name="other_senior_dependent" attrs="{'invisible':[('other_dependent_people','!=',True)]}"/>
<field name="other_disabled_senior_dependent" attrs="{'invisible':[('other_dependent_people','!=',True)]}"/>
<field name="other_juniors_dependent" attrs="{'invisible':[('other_dependent_people','!=',True)]}"/>
@@ -57,11 +57,12 @@ class FleetVehicle(models.Model):
for car in self:
car.atn = car._get_car_atn(car.acquisition_date, car.car_value, car.fuel_type, car.co2)
@api.depends('model_id', 'license_plate', 'log_contracts', 'total_depreciated_cost', 'acquisition_date')
@api.depends('model_id', 'license_plate', 'log_contracts', 'acquisition_date',
'co2_fee', 'log_contracts', 'log_contracts.state', 'log_contracts.recurring_cost_amount_depreciated')
def _compute_vehicle_name(self):
super(FleetVehicle, self)._compute_vehicle_name()
for vehicle in self:
acquisition_date = self._get_acquisition_date()
acquisition_date = vehicle._get_acquisition_date()
vehicle.name += u" \u2022 " + str(round(vehicle.total_depreciated_cost, 2)) + u" \u2022 " + acquisition_date
def _get_acquisition_date(self):
@@ -138,14 +139,14 @@ class FleetVehicleModel(models.Model):
model.default_total_depreciated_cost = model.co2_fee + model.default_recurring_cost_amount_depreciated
@api.multi
@api.depends('name', 'brand_id')
@api.depends('name', 'brand_id', 'default_total_depreciated_cost')
def name_get(self):
res = super(FleetVehicleModel, self).name_get()
new_res = []
for res_item in res:
model = self.browse(res_item[0])
if model.default_total_depreciated_cost != 0.0:
new_res.append((res_item[0], res_item[1] + u" \u2022 " + str(model.default_total_depreciated_cost)))
new_res.append((res_item[0], res_item[1] + u" \u2022 " + str(round(model.default_total_depreciated_cost, 2))))
else:
new_res.append(res_item)
return new_res
@@ -11,8 +11,8 @@ class HrContract(models.Model):
domain=lambda self: self._get_available_cars_domain(),
default=lambda self: self.env['fleet.vehicle'].search([('driver_id', '=', self.employee_id.address_home_id.id)], limit=1),
help="Employee's company car.")
car_atn = fields.Float(compute='_compute_car_atn_and_costs', string='ATN Company Car')
company_car_total_depreciated_cost = fields.Float(compute='_compute_car_atn_and_costs')
car_atn = fields.Float(compute='_compute_car_atn_and_costs', string='ATN Company Car', store=True)
company_car_total_depreciated_cost = fields.Float(compute='_compute_car_atn_and_costs', store=True)
available_cars_amount = fields.Integer(compute='_compute_available_cars_amount', string='Number of available cars')
new_car = fields.Boolean('Request a new car')
new_car_model_id = fields.Many2one('fleet.vehicle.model', string="Model", domain=lambda self: self._get_possible_model_domain())
@@ -12,7 +12,7 @@
<field name="car_id" nolabel="1" attrs="{'invisible': [('new_car', '=', True)]}"/>
<field name="available_cars_amount" invisible="1"/>
</div>
<field name="new_car_model_id" attrs="{'invisible': [('new_car', '=', False)]}"/>
<field name="new_car_model_id" attrs="{'invisible': ['|', ('transport_mode', '!=', 'company_car'), ('new_car', '=', False)]}"/>
<field name="max_unused_cars" invisible="1"/>
</xpath>
<xpath expr="//field[@name='car_atn']" position="after">
+5 -4
View File
@@ -59,10 +59,11 @@ class AccountInvoice(models.Model):
""" The ISR reference number is 27 characters long. The first 12 of them
contain the postal account number of this ISR's issuer, removing the zeros
at the beginning and filling the empty places with zeros on the right if it is
too short. The 15 other characters contain an internal reference identifying
too short. The next 14 characters contain an internal reference identifying
the invoice. For this, we use the invoice sequence number, removing each
of its non-digit characters, and pad the unused spaces on the left of
this number with zeros.
this number with zeros. The last character of the ISR number is the result
of a recursive modulo 10 on its first 26 characters.
"""
def _space_isr_number(isr_number):
to_treat = isr_number
@@ -81,9 +82,9 @@ class AccountInvoice(models.Model):
invoice_ref = re.sub('[^\d]', '', record.number)
#We only keep the last digits of the sequence number if it is too long
invoice_ref = invoice_ref[-l10n_ch_ISR_NUMBER_ISSUER_LENGTH:]
internal_ref = invoice_ref.zfill(l10n_ch_ISR_NUMBER_LENGTH - l10n_ch_ISR_NUMBER_ISSUER_LENGTH)
internal_ref = invoice_ref.zfill(l10n_ch_ISR_NUMBER_LENGTH - l10n_ch_ISR_NUMBER_ISSUER_LENGTH - 1) # -1 for mod10r check character
record.l10n_ch_isr_number = invoice_issuer_ref + internal_ref
record.l10n_ch_isr_number = mod10r(invoice_issuer_ref + internal_ref)
record.l10n_ch_isr_number_spaced = _space_isr_number(record.l10n_ch_isr_number)
@api.depends('currency_id.name', 'amount_total', 'partner_bank_id.bank_id', 'number', 'partner_bank_id.l10n_ch_postal', 'partner_bank_id.bank_id.l10n_ch_postal_eur', 'partner_bank_id.bank_id.l10n_ch_postal_chf')
+2 -2
View File
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from datetime import datetime
from datetime import datetime, timedelta
from hashlib import sha256
from json import dumps
import pytz
@@ -43,7 +43,7 @@ class pos_session(models.Model):
self.ensure_one()
date_today = datetime.utcnow()
session_start = Datetime.from_string(self.start_at)
if not date_today - datetime.timedelta(hours=24) <= session_start:
if not date_today - timedelta(hours=24) <= session_start:
raise UserError(_("This session has been opened another day. To comply with the French law, you should close sessions on a daily basis. Please close session %s and open a new one.") % self.name)
return True
@@ -160,7 +160,7 @@ or (contract.wage * 0.10))</field>
<field name="name">Arrears</field>
<field name="category_id" ref="hr_payroll.ALW"/>
<field name="amount_select">code</field>
<field name="amount_python_compute">result = inputs.ARS.amount</field>
<field name="amount_python_compute">result = inputs.ARS and inputs.ARS.amount</field>
<field eval="0.0" name="amount_fix"/>
<field name="sequence" eval="28"/>
<field name="note">Generally arrears are fully taxable, but employee may claim exemption u/s 89(1).
@@ -185,7 +185,7 @@ Now difference of income tax between payment year and actual year would be allow
<field name="category_id" ref="hr_payroll.ALW"/>
<field name="condition_select">none</field>
<field name="amount_select">code</field>
<field name="amount_python_compute">result = inputs.LE.amount</field>
<field name="amount_python_compute">result = inputs.LE and inputs.LE.amount</field>
<field name="sequence" eval="30"/>
<field name="note">Payment by way of leave encashment received by Central and State Govt.employees at the time of retirement in respect of the period of earned leave at credit is fully exempt. In case of other employees, the exemption is to be limited to minimum of all below:
1.The actual amount received
@@ -199,7 +199,7 @@ Now difference of income tax between payment year and actual year would be allow
<field name="name">Performance Incentive</field>
<field name="category_id" ref="hr_payroll.ALW"/>
<field name="amount_select">code</field>
<field name="amount_python_compute">result = inputs.PERF.amount</field>
<field name="amount_python_compute">result = inputs.PERF and inputs.PERF.amount</field>
<field name="sequence" eval="31"/>
<field name="note">This would be fully taxable based on incentive.</field>
</record>
@@ -209,7 +209,7 @@ Now difference of income tax between payment year and actual year would be allow
<field name="name">Bonus</field>
<field name="category_id" ref="hr_payroll.ALW"/>
<field name="amount_select">code</field>
<field name="amount_python_compute">result = inputs.BNS.amount</field>
<field name="amount_python_compute">result = inputs.BNS and inputs.BNS.amount</field>
<field name="sequence" eval="41"/>
<field name="note">This would be fully taxable based on Bonus.</field>
</record>
@@ -229,7 +229,7 @@ Now difference of income tax between payment year and actual year would be allow
<field name="name">Medical Reimbursement</field>
<field name="category_id" ref="hr_payroll.ALW"/>
<field name="amount_select">code</field>
<field name="amount_python_compute">result = inputs.MR.amount</field>
<field name="amount_python_compute">result = inputs.MR and inputs.MR.amount</field>
<field name="sequence" eval="32"/>
<field name="note">This component is on-taxable up to 15000 per year (or Rs 1250 per month) on producing medical bills.</field>
</record>
@@ -250,7 +250,7 @@ Now difference of income tax between payment year and actual year would be allow
<field name="name">Periodical Journals</field>
<field name="category_id" ref="hr_payroll.ALW"/>
<field name="amount_select">code</field>
<field name="amount_python_compute">result = inputs.PJ.amount</field>
<field name="amount_python_compute">result = inputs.PJ and inputs.PJ.amount</field>
<field name="sequence" eval="34"/>
<field name="note">Some employers may provide component for buying magazines, journals and books as a part of knowledge enhancement for business growth.This part would become non taxable on providing original bills.</field>
</record>
@@ -280,7 +280,7 @@ Now difference of income tax between payment year and actual year would be allow
<field name="name">Telephone Reimbursement</field>
<field name="category_id" ref="hr_payroll.ALW"/>
<field name="amount_select">code</field>
<field name="amount_python_compute">result = inputs.TR.amount</field>
<field name="amount_python_compute">result = inputs.TR and inputs.TR.amount</field>
<field name="sequence" eval="36"/>
<field name="note">In some of the cases, companies may provide a component for telephone bills.Employees may provide actual phone usage bills to reimburse this component and make it non-taxable.
</field>
@@ -300,7 +300,7 @@ Now difference of income tax between payment year and actual year would be allow
<field name="name">Car Expenses Reimbursement</field>
<field name="category_id" ref="hr_payroll.ALW"/>
<field name="amount_select">code</field>
<field name="amount_python_compute">result = inputs.CEI.amount</field>
<field name="amount_python_compute">result = inputs.CEI and inputs.CEI.amount</field>
<field name="sequence" eval="38"/>
<field name="note">In case company provides component for this and employee use self owned car for official and personal purposes, Rs 1800 per month would be non-taxable on showing bills for fuel or can maintenance. This amount would be Rs 2400 in case car is more capacity than 1600cc.</field>
</record>
@@ -310,7 +310,7 @@ Now difference of income tax between payment year and actual year would be allow
<field name="name">Internet Expense</field>
<field name="category_id" ref="hr_payroll.ALW"/>
<field name="amount_select">code</field>
<field name="amount_python_compute">result = inputs.IE.amount</field>
<field name="amount_python_compute">result = inputs.IE and inputs.IE.amount</field>
<field name="sequence" eval="39"/>
<field name="note">Employer may also provide reimbursement of internet expenses and thus this would become non taxable.</field>
</record>
@@ -463,7 +463,7 @@ or - (contract.wage * contract.voluntary_provident_fund / 100))</field>
<field name="category_id" ref="hr_payroll.DED"/>
<field name="condition_select">none</field>
<field name="amount_select">code</field>
<field name="amount_python_compute">result = - (inputs.LAI.amount)</field>
<field name="amount_python_compute">result = inputs.LAI and - (inputs.LAI.amount)</field>
<field name="register_id" ref="hr_leave_availed_register"/>
<field name="sequence" eval="180"/>
</record>
+5 -8
View File
@@ -179,20 +179,17 @@ tag_lu_177,I.B.5. Tabacs fabriqués,taxes
tag_lu_178,Base - Vente Biens Tabacs - Extracommunautaire,taxes
tag_lu_179,Base - Vente Biens Tabacs - Intracommunautaire,taxes
tag_lu_180,Base - Vente Biens Tabacs - Pays,taxes
tag_lu_181,I.B.6.a) Livraisons subséquentes à des acquisitions intracommunautaires dans
le cadre d'opérations triangulaires, lorsque le destinataire identifié à la TVA dans l'Etat membre de destination des biens y est le redevable de la taxe (4),taxes
tag_lu_181,"I.B.6.a) Livraisons subséquentes à des acquisitions intracommunautaires dans le cadre d'opérations triangulaires, lorsque le destinataire identifié à la TVA dans l'Etat membre de destination des biens y est le redevable de la taxe (4)",taxes
tag_lu_182,Base - Vente Biens – Triangulaire Intracommunautaire,taxes
tag_lu_183,I.B.6.d) Autres opérations réalisées (imposables) à l'étranger,taxes
tag_lu_184,Base - Vente Prestations - Extracommunautaire,taxes
tag_lu_185,I.B.6.c) Opérations réalisées dans le cadre du régime particulier de l'article 56sexies,taxes
tag_lu_186,I.B.7. Opérations à l'intérieur du pays pour lesquelles le preneur est le redevable,taxes
tag_lu_187,I.B.6.b)1) Prestations de services à des identifiés à la TVA dans un autre
Etat membre non exonérées dans l'Etat membre du preneur redevable (art.17/1/b) (5),taxes
tag_lu_187,I.B.6.b)1) Prestations de services à des identifiés à la TVA dans un autre Etat membre non exonérées dans l'Etat membre du preneur redevable (art.17/1/b) (5),taxes
tag_lu_188,Base - Vente Prestations - Intracommunautaire,taxes
tag_lu_189,I.B.6.b)2) Prestations de services à des identifiés à la TVA dans un autre
Etat membre exonérées dans l'Etat membre du preneur (art.17/1/b),taxes
tag_lu_189,I.B.6.b)2) Prestations de services à des identifiés à la TVA dans un autre Etat membre exonérées dans l'Etat membre du preneur (art.17/1/b),taxes
tag_lu_190,Base - Vente Prestations - Intracommunautaire exonérées dans l'état membre,taxes
tag_lu_191,I.B.1. Livraisons intracommunautaires de biens à des personnes identifiées à la TVA dans unautre Etat membre (art.43/1/d e et f) (3),taxes
tag_lu_191,I.B.1. Livraisons intracommunautaires de biens à des personnes identifiées à la TVA dans un autre Etat membre (art.43/1/d e et f) (3),taxes
tag_lu_192,Base - Vente Biens - Intracommunautaire,taxes
tag_lu_193,I.C. Chiffre d'affaires imposable,taxes
tag_lu_194,II.A. Ventilation du chiffre d'affaires imposable – base,taxes
@@ -598,4 +595,4 @@ tag_lu_594,Taxe - Investissement Prestations 17% - Intracommunautaire (-),taxes
tag_lu_595,Taxe - Investissement Prestations 3% - Intracommunautaire (-),taxes
tag_lu_596,Taxe - Investissement Prestations 6% - Intracommunautaire (-),taxes
tag_lu_597,Taxe - Investissement Prestations 8% - Intracommunautaire (-),taxes
tag_lu_598,Base - Sans Taxe,taxes
tag_lu_598,Base - Sans Taxe,taxes
1 id name applicability
179 tag_lu_178 Base - Vente Biens Tabacs - Extracommunautaire taxes
180 tag_lu_179 Base - Vente Biens Tabacs - Intracommunautaire taxes
181 tag_lu_180 Base - Vente Biens Tabacs - Pays taxes
182 tag_lu_181 I.B.6.a) Livraisons subséquentes à des acquisitions intracommunautaires dans I.B.6.a) Livraisons subséquentes à des acquisitions intracommunautaires dans le cadre d'opérations triangulaires, lorsque le destinataire identifié à la TVA dans l'Etat membre de destination des biens y est le redevable de la taxe (4) taxes
le cadre d'opérations triangulaires lorsque le destinataire identifié à la TVA dans l'Etat membre de destination des biens y est le redevable de la taxe (4) taxes
183 tag_lu_182 Base - Vente Biens – Triangulaire Intracommunautaire taxes
184 tag_lu_183 I.B.6.d) Autres opérations réalisées (imposables) à l'étranger taxes
185 tag_lu_184 Base - Vente Prestations - Extracommunautaire taxes
186 tag_lu_185 I.B.6.c) Opérations réalisées dans le cadre du régime particulier de l'article 56sexies taxes
187 tag_lu_186 I.B.7. Opérations à l'intérieur du pays pour lesquelles le preneur est le redevable taxes
188 tag_lu_187 I.B.6.b)1) Prestations de services à des identifiés à la TVA dans un autre I.B.6.b)1) Prestations de services à des identifiés à la TVA dans un autre Etat membre non exonérées dans l'Etat membre du preneur redevable (art.17/1/b) (5) taxes
Etat membre non exonérées dans l'Etat membre du preneur redevable (art.17/1/b) (5) taxes
189 tag_lu_188 Base - Vente Prestations - Intracommunautaire taxes
190 tag_lu_189 I.B.6.b)2) Prestations de services à des identifiés à la TVA dans un autre I.B.6.b)2) Prestations de services à des identifiés à la TVA dans un autre Etat membre exonérées dans l'Etat membre du preneur (art.17/1/b) taxes
Etat membre exonérées dans l'Etat membre du preneur (art.17/1/b) taxes
191 tag_lu_190 Base - Vente Prestations - Intracommunautaire exonérées dans l'état membre taxes
192 tag_lu_191 I.B.1. Livraisons intracommunautaires de biens à des personnes identifiées à la TVA dans unautre Etat membre (art.43/1/d e et f) (3) I.B.1. Livraisons intracommunautaires de biens à des personnes identifiées à la TVA dans un autre Etat membre (art.43/1/d e et f) (3) taxes
193 tag_lu_192 Base - Vente Biens - Intracommunautaire taxes
194 tag_lu_193 I.C. Chiffre d'affaires imposable taxes
195 tag_lu_194 II.A. Ventilation du chiffre d'affaires imposable – base taxes
595 tag_lu_595 Taxe - Investissement Prestations 3% - Intracommunautaire (-) taxes
596 tag_lu_596 Taxe - Investissement Prestations 6% - Intracommunautaire (-) taxes
597 tag_lu_597 Taxe - Investissement Prestations 8% - Intracommunautaire (-) taxes
598 tag_lu_598 Base - Sans Taxe taxes
+6
View File
@@ -15,6 +15,12 @@ class IrModel(models.Model):
help="Whether this model supports messages and notifications.",
)
def unlink(self):
# Delete followers for models that will be unlinked.
query = "DELETE FROM mail_followers WHERE res_model IN %s"
self.env.cr.execute(query, [tuple(self.mapped('model'))])
return super(IrModel, self).unlink()
@api.multi
def write(self, vals):
if self and 'is_mail_thread' in vals:
+3
View File
@@ -846,6 +846,9 @@ class Message(models.Model):
('channel_ids', 'in', email_channels.ids),
('email', '!=', self_sudo.author_id.email or self_sudo.email_from),
])._notify(self, force_send=force_send, send_after_commit=send_after_commit, user_signature=user_signature)
notif_users.mapped('partner_id')._notify_by_chat(self)
channels_sudo._notify(self)
# Discard cache, because child / parent allow reading and therefore
+1 -1
View File
@@ -2150,7 +2150,7 @@ class MailThread(models.AbstractModel):
new_channels.setdefault(header_follower.channel_id.id, set()).add(new_subtype.id)
# add followers coming from res.users relational fields that are tracked
to_add_users = self.env['res.users'].sudo().browse([values[name] for name in user_field_lst if values.get(name)])
to_add_users = self.env['res.users'].sudo().browse([values[name] for name in user_field_lst if values.get(name)]).filtered(lambda u: u.partner_id.active)
for partner in to_add_users.mapped('partner_id'):
new_partners.setdefault(partner.id, None)
@@ -127,6 +127,21 @@ var FieldMany2ManyTagsEmail = M2MTags.extend({
return _super.apply(self, arguments);
});
},
/**
* Override for res.partner
* name_get is dynamic (based on context) while display_name is static
* (stored)
*/
get_render_data: function(ids){
this.dataset.cancel_read();
return this.dataset.name_get(ids).then(function (names) {
return _.map(names, function(name) {
return {id: name[0], display_name: name[1]};
});
});
},
});
field_registry.add('many2many_tags_email', FieldMany2ManyTagsEmail);
+3 -1
View File
@@ -125,9 +125,11 @@
<field name="name">Work Orders Performance</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">mrp.workorder</field>
<field name="domain">[('state','=','done')]</field>
<field name="domain">[]</field>
<field name="context">{'search_default_done': True}</field>
<field name="view_type">form</field>
<field name="view_mode">graph,pivot,tree,form,gantt</field>
<field name="search_view_id" ref="view_mrp_production_work_order_search"/>
</record>
<!-- Workcenter Kanban view-->
+1 -1
View File
@@ -200,7 +200,7 @@
<tree>
<field name="date_start"/>
<field name="date_end"/>
<field name="duration" widget="float_time"/>
<field name="duration" widget="float_time" sum="Total duration"/>
<field name="user_id"/>
<field name="loss_id" string="Efficiency"/>
</tree>
+1 -1
View File
@@ -206,7 +206,7 @@ exports.PosModel = Backbone.Model.extend({
},{
model: 'account.tax',
fields: ['name','amount', 'price_include', 'include_base_amount', 'amount_type', 'children_tax_ids'],
domain: null,
domain: function(self) {return [['company_id', '=', self.company && self.company.id || false]]},
loaded: function(self, taxes){
self.taxes = taxes;
self.taxes_by_id = {};
@@ -2030,6 +2030,7 @@ var PaymentScreenWidget = ScreenWidget.extend({
}
order.initialize_validation_date();
order.finalized = true;
if (order.is_to_invoice()) {
var invoiced = this.pos.push_and_invoice_order(order);
+1 -1
View File
@@ -108,7 +108,7 @@ class Pricelist(models.Model):
"""
self.ensure_one()
if not date:
date = self._context.get('date') or fields.Date.today()
date = self._context.get('date') or fields.Date.context_today(self)
if not uom_id and self._context.get('uom'):
uom_id = self._context['uom']
if uom_id:
+1 -1
View File
@@ -15,7 +15,7 @@ class CustomerPortal(CustomerPortal):
def _prepare_portal_layout_values(self):
values = super(CustomerPortal, self)._prepare_portal_layout_values()
partner = request.env.user.partner_id
values['purchase_count'] = request.env['purchase.order'].search_count([
values['purchase_count'] = request.env['purchase.order'].sudo().search_count([
'|',
('message_partner_ids', 'child_of', [partner.commercial_partner_id.id]),
('partner_id', 'child_of', [partner.commercial_partner_id.id]),
+1 -1
View File
@@ -76,7 +76,7 @@ class AccountInvoiceLine(models.Model):
prod_moves = [m for m in moves if m.product_id.id == product_id]
prod_qty_done = factor * qty_done
prod_quantity = factor * quantity
average_price_unit += self._compute_average_price(prod_qty_done, prod_quantity, prod_moves)
average_price_unit += factor * self._compute_average_price(prod_qty_done, prod_quantity, prod_moves)
price_unit = average_price_unit or price_unit
price_unit = self.product_id.uom_id._compute_price(price_unit, self.uom_id)
return price_unit
+1 -1
View File
@@ -608,7 +608,7 @@ class WebClient(http.Controller):
def version_info(self):
return odoo.service.common.exp_version()
@http.route('/web/tests', type='http', auth="none")
@http.route('/web/tests', type='http', auth="user")
def test_suite(self, mod=None, **kwargs):
return request.render('web.qunit_suite')
@@ -941,7 +941,7 @@ var ActionManager = Widget.extend({
});
});
},
ir_actions_act_url: function (action) {
ir_actions_act_url: function (action, options) {
var url = action.url;
if (session.debug && url && url.length && url[0] === '/') {
url = $.param.querystring(url, {debug: session.debug});
@@ -952,6 +952,7 @@ var ActionManager = Widget.extend({
return $.Deferred(); // The action is finished only when the redirection is done
} else {
window.open(url, '_blank');
options.on_close();
}
return $.when();
},
@@ -139,6 +139,9 @@
// Filters
.o_filters_menu {
.o-selected-li;
max-height: ~"calc(100vh - 125px)";
overflow: auto;
.o_filter_condition {
.o-search-options-dropdown-custom-li;
@@ -175,6 +178,9 @@
// Group By
.o_group_by_menu {
.o-selected-li;
max-height: ~"calc(100vh - 125px)";
overflow: auto;
.divider {display: none;} // hide the divider by default (no group)
.o_add_group { // add button
@@ -188,6 +194,9 @@
max-height: 75vh;
overflow-y: auto;
.o-selected-li;
max-height: ~"calc(100vh - 125px)";
overflow: auto;
.divider {display: none;} // hide the divider by default (no favorite)
.o-searchview-custom-private, .o-searchview-custom-public {
@@ -221,7 +221,8 @@ var FieldTextHtml = AbstractField.extend({
start: function () {
var self = this;
this.loaded = false;
this.editorLoadedDeferred = $.Deferred();
this.contentLoadedDeferred = $.Deferred();
this.callback = _.uniqueId('FieldTextHtml_');
window.odoo[this.callback+"_editor"] = function (EditorBar) {
setTimeout(function () {
@@ -312,8 +313,6 @@ var FieldTextHtml = AbstractField.extend({
old_initialize_content: function () {
this.$el.closest('.modal-body').css('max-height', 'none');
this.$iframe = this.$el.find('iframe');
// deactivate any button to avoid saving a not ready iframe
$('.o_cp_buttons, .o_statusbar_buttons').find('button').addClass('o_disabled').attr('disabled', true);
this.document = null;
this.$body = $();
this.$content = $();
@@ -328,9 +327,7 @@ var FieldTextHtml = AbstractField.extend({
this.$content = this.$body.find("#editable_area");
this.render();
this.add_button();
this.loaded = true;
// reactivate all the buttons when the field's content (the iframe) is loaded
$('.o_cp_buttons, .o_statusbar_buttons').find('button').removeClass('o_disabled').attr('disabled', false);
this.contentLoadedDeferred.resolve();
setTimeout(self.resize, 0);
},
on_editor_loaded: function (EditorBar) {
@@ -339,6 +336,7 @@ var FieldTextHtml = AbstractField.extend({
if (this.value && window.odoo[self.callback+"_updown"] && !(this.$content.html()||"").length) {
this.render();
}
this.editorLoadedDeferred.resolve();
setTimeout(function () {
setTimeout(self.resize,0);
}, 0);
@@ -395,20 +393,25 @@ var FieldTextHtml = AbstractField.extend({
//--------------------------------------------------------------------------
/**
* Set the value when the widget is fully loaded (content + editor).
*
* @override
*/
commitChanges: function () {
if (!this.loaded || this.mode === 'readonly') {
var self = this;
var result = this._super.bind(this, arguments);
if (this.mode === 'readonly') {
return;
}
// switch to WYSIWYG mode if currently in code mode to get all changes
if (config.debug && this.mode === 'edit' && this.editor.rte) {
var layoutInfo = this.editor.rte.editable().data('layoutInfo');
$.summernote.pluginEvents.codeview(undefined, undefined, layoutInfo, false);
}
this.editor.snippetsMenu && this.editor.snippetsMenu.cleanForSave();
this._setValue(this.$content.html());
return this._super.apply(this, arguments);
return $.when(this.contentLoadedDeferred, this.editorLoadedDeferred, result).then(function () {
// switch to WYSIWYG mode if currently in code mode to get all changes
if (config.debug && self.editor.rte) {
var layoutInfo = self.editor.rte.editable().data('layoutInfo');
$.summernote.pluginEvents.codeview(undefined, undefined, layoutInfo, false);
}
self.editor.snippetsMenu && self.editor.snippetsMenu.cleanForSave();
self._setValue(self.$content.html());
});
},
});
@@ -17,7 +17,60 @@ var eventHandler = $.summernote.eventHandler;
var renderer = $.summernote.renderer;
var options = $.summernote.options;
// Browser-unify execCommand
var oldJustify = {};
_.each(['Left', 'Right', 'Full', 'Center'], function (align) {
oldJustify[align] = editor['justify' + align];
editor['justify' + align] = function ($editable, value) {
// Before calling the standard function, check all elements which have
// an 'align' attribute and mark them with their value
var $align = $editable.find('[align]');
_.each($align, function (el) {
var $el = $(el);
$el.data('__align', $el.attr('align'));
});
// Call the standard function
oldJustify[align].apply(this, arguments);
// Then:
// Remove the text-align of elements which lost the 'align' attribute
var $newAlign = $editable.find('[align]');
$align.not($newAlign).css('text-align', '');
// Transform the 'align' attribute into the 'text-align' css
// property for elements which received the 'align' attribute or whose
// 'align' attribute changed
_.each($newAlign, function (el) {
var $el = $(el);
var oldAlignValue = $align.data('__align');
var alignValue = $el.attr('align');
if (oldAlignValue === alignValue) {
// If the element already had an 'align' attribute and that it
// did not changed, do nothing (compatibility)
return;
}
$el.removeAttr('align');
$el.css('text-align', alignValue);
// Note the first step (removing the text-align of elemnts which
// lost the 'align' attribute) is kinda the same as this one, but
// this one handles the elements which have been edited with chrome
// or with this new system
$el.find('*').css('text-align', '');
});
// Unmark the elements
$align.removeData('__align');
};
});
// Add methods to summernote
dom.hasContentAfter = function (node) {
var next;
if (dom.isEditable(node)) return;
@@ -2289,26 +2342,6 @@ eventHandler.modules.popover.update = function ($popover, oStyle, isAirMode) {
}
};
// override summernote clipboard functionality
eventHandler.modules.clipboard.attach = function (layoutInfo) {
var $editable = layoutInfo.editable();
$editable.on('paste', function (e) {
var clipboardData = ((e.originalEvent || e).clipboardData || window.clipboardData);
// Change nothing if pasting html (copy from text editor / web / ...) or
// if clipboardData is not available (IE / ...)
if (clipboardData && clipboardData.types && clipboardData.types.length === 1 && clipboardData.types[0] === "text/plain") {
e.preventDefault();
$editable.data('NoteHistory').recordUndo($editable); // FIXME
var pastedText = clipboardData.getData("text/plain");
// Try removing linebreaks which are not really linebreaks (in a PDF,
// when a sentence goes over the next line, copying it considers it
// a linebreak for example).
var formattedText = pastedText.replace(/([\w-])\r?\n([\w-])/g, "$1 $2").trim();
document.execCommand("insertText", false, formattedText);
}
});
};
var fn_attach = eventHandler.attach;
eventHandler.attach = function (oLayoutInfo, options) {
var $editable = oLayoutInfo.editor().hasClass('note-editable') ? oLayoutInfo.editor() : oLayoutInfo.editor().find('.note-editable');
@@ -4,6 +4,7 @@ odoo.define('web_editor.web_editor_tests', function (require) {
var FormView = require('web.FormView');
var testUtils = require('web.test_utils');
var core = require('web.core');
var web_editor = require('web_editor.editor');
var _t = core._t;
@@ -191,7 +192,7 @@ QUnit.test('field htmlsimple does not crash when commitChanges is called in mode
QUnit.test('html_frame does not crash when saving in readonly', function (assert) {
// The 'Save' action may be triggered even in readonly (e.g. when clicking
// on a button in the form view)
assert.expect(0);
assert.expect(2);
var form = testUtils.createView({
View: FormView,
@@ -203,10 +204,13 @@ QUnit.test('html_frame does not crash when saving in readonly', function (assert
'</sheet>' +
'</form>',
res_id: 1,
mockRPC: function (route) {
mockRPC: function (route, args) {
if (args.method) {
assert.step(args.method);
}
if (_.str.startsWith(route, '/test')) {
// manually call the callback to simulate that the iframe has
// been correctly loaded
// been loaded (note: just the content, not the editor)
window.odoo[$.deparam(route).callback + '_content'].call();
return $.when();
}
@@ -215,7 +219,109 @@ QUnit.test('html_frame does not crash when saving in readonly', function (assert
});
form.saveRecord(); // before the fix done in this commit, it crashed here
assert.verifySteps(['read']);
form.destroy();
});
QUnit.test('html_frame does not crash when saving in edit mode (editor not loaded)', function (assert) {
// The 'Save' action may be triggered when saving in edit mode very fast
// so that the editor may be not loaded, even though the content is!
assert.expect(2);
var form = testUtils.createView({
View: FormView,
model: 'mass.mailing',
data: this.data,
arch: '<form string="Partners">' +
'<sheet>' +
'<field name="display_name"/>' +
'<field name="body" widget="html_frame" options="{\'editor_url\': \'/test\'}"/>' +
'</sheet>' +
'</form>',
res_id: 1,
mockRPC: function (route, args) {
if (args.method) {
assert.step(args.method);
}
if (_.str.startsWith(route, '/test')) {
// manually call the callback to simulate that the iframe has
// been partially loaded (just the content, not the editor)
window.odoo[$.deparam(route).callback + '_content']();
return $.when();
}
return this._super.apply(this, arguments);
},
});
form.$buttons.find('.o_form_button_edit').click();
form.$('input').val('trululu').trigger('input');
form.$buttons.find('.o_form_button_save').click(); // crash without editor fully loaded
assert.verifySteps(['read']);
form.destroy();
});
QUnit.test('html_frame saving in edit mode (editor and content fully loaded)', function (assert) {
var done = assert.async();
assert.expect(4);
var editorBar = new web_editor.Class();
var loadDeferred = $.Deferred();
var writeDeferred = $.Deferred();
var form = testUtils.createView({
View: FormView,
model: 'mass.mailing',
data: this.data,
arch: '<form string="Partners">' +
'<sheet>' +
'<field name="display_name"/>' +
'<field name="body" widget="html_frame" options="{\'editor_url\': \'/test\'}"/>' +
'</sheet>' +
'</form>',
res_id: 1,
mockRPC: function (route, args) {
if (args.method) {
assert.step(args.method);
if (args.method === 'write') {
writeDeferred.resolve();
}
}
if (_.str.startsWith(route, '/test')) {
// manually call the callback to simulate that the iframe has
// been fully loaded (content + editor)
var callback = $.deparam(route).callback;
return loadDeferred.then(function () {
var contentCallback = window.odoo[callback + '_content'];
var editorCallback = window.odoo[callback + '_editor'];
if (editorCallback && contentCallback) {
contentCallback();
editorCallback(editorBar);
}
});
}
return this._super.apply(this, arguments);
},
});
form.$buttons.find('.o_form_button_edit').click();
form.$('input').val('trululu').trigger('input');
form.$buttons.find('.o_form_button_save').click();
loadDeferred.resolve(); // simulate late loading of html frame
assert.strictEqual(form.$('.o_field_char').val(), 'trululu',
"should have saved the char field text");
writeDeferred.then( function () { // html_frame is async with write
assert.verifySteps(['read', 'write']);
form.destroy();
done();
});
});
});
@@ -79,7 +79,7 @@
</t>
</xpath>
<xpath expr="//form[@id='registration_form']" position="attributes">
<attribute name="t-if">event.event_ticket_ids</attribute>
<attribute name="t-if">event.event_ticket_ids and not all([ticket.is_expired for ticket in event.event_ticket_ids])</attribute>
</xpath>
</template>
@@ -168,7 +168,7 @@ odoo.define('website_sale.website_sale', function (require) {
}
else {
$q.parents('li:first').addClass("hidden");
$('a[href^="/shop/checkout"]').addClass("hidden");
$('a[href*="/shop/checkout"]').addClass("hidden");
}
$q.html(data.cart_quantity).hide().fadeIn(600);
+1
View File
@@ -12,3 +12,4 @@ List of contributors:
Laurent Destailleur ldestailleur@teclib.com https://github.com/eldy
David Halgand dhalgand@teclib.com https://github.com/halgandd
Guillaume Masson gmasson@teclib.com https://github.com/metaminux
+2 -2
View File
@@ -96,7 +96,7 @@ class IrSequence(models.Model):
def _set_number_next_actual(self):
for seq in self:
seq.write({'number_next': seq.number_next_actual or 0})
seq.write({'number_next': seq.number_next_actual or 1})
@api.model
def _get_current_sequence(self):
@@ -327,7 +327,7 @@ class IrSequenceDateRange(models.Model):
def _set_number_next_actual(self):
for seq in self:
seq.write({'number_next': seq.number_next_actual or 0})
seq.write({'number_next': seq.number_next_actual or 1})
@api.model
def default_get(self, fields):
+1 -1
View File
@@ -509,7 +509,7 @@ class Partner(models.Model):
result = True
# To write in SUPERUSER on field is_company and avoid access rights problems.
if 'is_company' in vals and self.user_has_groups('base.group_partner_manager') and not self.env.uid == SUPERUSER_ID:
result = super(Partner, self).sudo().write({'is_company': vals.get('is_company')})
result = super(Partner, self.sudo()).write({'is_company': vals.get('is_company')})
del vals['is_company']
result = result and super(Partner, self).write(vals)
for partner in self:
+7
View File
@@ -282,6 +282,13 @@ class Users(models.Model):
if any(user.company_ids and user.company_id not in user.company_ids for user in self):
raise ValidationError(_('The chosen company is not in the allowed companies for this user'))
@api.multi
@api.constrains('action_id')
def _check_action_id(self):
action_open_website = self.env.ref('base.action_open_website', raise_if_not_found=False)
if action_open_website and any(user.action_id.id == action_open_website.id for user in self):
raise ValidationError(_('The "App Switcher" action cannot be selected as home action.'))
@api.multi
def read(self, fields=None, load='_classic_read'):
if fields and self == self.env.user: