[MERGE] forward port branch saas-16 up to 4c410ee6a1
This commit is contained in:
@@ -290,7 +290,8 @@ class AccountChartTemplate(models.Model):
|
||||
tmp1, tmp2 = self.parent_id._install_template(company, code_digits=code_digits, transfer_account_id=transfer_account_id, acc_ref=acc_ref, taxes_ref=taxes_ref)
|
||||
acc_ref.update(tmp1)
|
||||
taxes_ref.update(tmp2)
|
||||
tmp1, tmp2 = self._load_template(company, code_digits=code_digits, transfer_account_id=transfer_account_id, account_ref=acc_ref, taxes_ref=taxes_ref)
|
||||
# Ensure, even if individually, that everything is translated according to the company's language.
|
||||
tmp1, tmp2 = self.with_context(lang=company.partner_id.lang)._load_template(company, code_digits=code_digits, transfer_account_id=transfer_account_id, account_ref=acc_ref, taxes_ref=taxes_ref)
|
||||
acc_ref.update(tmp1)
|
||||
taxes_ref.update(tmp2)
|
||||
return acc_ref, taxes_ref
|
||||
@@ -856,6 +857,8 @@ class WizardMultiChartsAccounts(models.TransientModel):
|
||||
all the provided information to create the accounts, the banks, the journals, the taxes, the
|
||||
accounting properties... accordingly for the chosen company.
|
||||
'''
|
||||
# Ensure everything is translated consitingly to the company's language, not the user's one.
|
||||
self = self.with_context(lang=self.company_id.partner_id.lang)
|
||||
if not self.env.user._is_admin():
|
||||
raise AccessError(_("Only administrators can change the settings"))
|
||||
|
||||
|
||||
@@ -73,8 +73,8 @@ class HrHolidaySummaryReport(models.AbstractModel):
|
||||
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
|
||||
count+=1
|
||||
date_from += timedelta(1)
|
||||
count += abs(holiday.number_of_days)
|
||||
self.sum = count
|
||||
return res
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -55,7 +55,7 @@
|
||||
<field name="parent_id" ref="hr_payroll.ALW"/>
|
||||
</record>
|
||||
<record id="hr_payroll_head_pp" model="hr.salary.rule.category">
|
||||
<field name="name">Withholding Tax Grid 2017 (Precompte Professionnel Bareme 2017)</field>
|
||||
<field name="name">Withholding Tax Grid 2018 (Precompte Professionnel Bareme 2018)</field>
|
||||
<field name="code">PP</field>
|
||||
<field name="parent_id" ref="hr_payroll.DED"/>
|
||||
</record>
|
||||
@@ -126,14 +126,14 @@ if contract.mobile:
|
||||
<field name="code">EmpBonus.1</field>
|
||||
<field name="sequence">42</field>
|
||||
<field name="condition_select">python</field>
|
||||
<field name="condition_python">result = categories.BRUT <= 2431.27</field>
|
||||
<field name="condition_python">result = categories.BRUT <= 2510.47</field>
|
||||
<field name="amount_select">code</field>
|
||||
<field name="amount_python_compute">
|
||||
salary = categories.BRUT
|
||||
if salary <= 1577.89:
|
||||
result = 193.79
|
||||
elif salary <= 2461.27:
|
||||
result = 193.79 - (0.2194 * (salary - 1577.89))
|
||||
if salary <= 1609.47:
|
||||
result = 197.67
|
||||
elif salary <= 2510.47:
|
||||
result = 197.67 - (0.2194 * (salary - 1609.47))
|
||||
</field>
|
||||
</record>
|
||||
<record id="hr_salary_rule_employment_bonus_workers" model="hr.salary.rule">
|
||||
@@ -142,14 +142,14 @@ elif salary <= 2461.27:
|
||||
<field name="code">EmpBonus.2</field>
|
||||
<field name="sequence">42</field>
|
||||
<field name="condition_select">python</field>
|
||||
<field name="condition_python">result = categories.BRUT + categories.ONSS <= 2431.27</field>
|
||||
<field name="condition_python">result = categories.BRUT + categories.ONSS <= 2510.47</field>
|
||||
<field name="amount_select">code</field>
|
||||
<field name="amount_python_compute">
|
||||
salary = categories.BRUT
|
||||
if salary <= 1577.89:
|
||||
result = 209.29
|
||||
elif salary <= 2461.27:
|
||||
result = 209.29 - (0.2369 * (salary - 1577.89))
|
||||
if salary <= 1609.47:
|
||||
result = 213.48
|
||||
elif salary <= 2510.47:
|
||||
result = 213.48 - (0.2369 * (salary - 1609.47))
|
||||
</field>
|
||||
</record>
|
||||
<record id="hr_payroll_rules_mis_ex_onss_1" model="hr.salary.rule">
|
||||
@@ -160,7 +160,7 @@ elif salary <= 2461.27:
|
||||
<field name="sequence">165</field>
|
||||
<field name="condition_select">python</field>
|
||||
<field name="appears_on_payslip" eval="False"/>
|
||||
<field name="condition_python">result = (((employee.marital=='single') or (employee.marital=='married' and employee.spouse_fiscal_status=='without income')) and (employee.resident_bool!=True))</field>
|
||||
<field name="condition_python">result = (((employee.marital in ['divorced', 'single', 'widower']) or (employee.marital=='married' and employee.spouse_fiscal_status=='without income')) and (employee.resident_bool!=True))</field>
|
||||
<field name="amount_select">code</field>
|
||||
<field name="amount_python_compute">
|
||||
wage = categories.BASIC
|
||||
@@ -208,7 +208,7 @@ else:
|
||||
<field name="sequence">120</field>
|
||||
<field name="condition_select">python</field>
|
||||
<field name="appears_on_payslip" eval="False"/>
|
||||
<field name="condition_python">result = (((employee.marital=='single') or (employee.marital=='married' and employee.spouse_fiscal_status=='with income')) and (employee.resident_bool!=True))</field>
|
||||
<field name="condition_python">result = (((employee.marital in ['divorced', 'single', 'widower']) or (employee.marital=='married' and employee.spouse_fiscal_status=='with income')) and (employee.resident_bool!=True))</field>
|
||||
</record>
|
||||
<record id="hr_payroll_rules_baremeII" model="hr.salary.rule">
|
||||
<field name="category_id" ref="hr_payroll_head_pp"/>
|
||||
@@ -230,18 +230,6 @@ else:
|
||||
<field name="condition_python">result = employee.resident_bool==True</field>
|
||||
<field name="appears_on_payslip" eval="False"/>
|
||||
</record>
|
||||
<record id="hr_salary_rule_pp_reduction_low_salary" model="hr.salary.rule">
|
||||
<field name="category_id" ref="hr_salary_rule_pp_reductions"/>
|
||||
<field name="name">Withholding Tax Reduction for Low Salaries</field>
|
||||
<field name="code">PPRed.0</field>
|
||||
<field name="sequence">131</field>
|
||||
<field name="condition_select">python</field>
|
||||
<field name="condition_python">result = categories.GROSS <= 2027.86 and categories.GROSS > 565.02</field>
|
||||
<field name="amount_select">code</field>
|
||||
<field name="amount_python_compute">
|
||||
result = 5.83
|
||||
</field>
|
||||
</record>
|
||||
<record id="hr_salary_rule_pp_reduction_employees" model="hr.salary.rule">
|
||||
<field name="category_id" ref="hr_salary_rule_pp_reductions"/>
|
||||
<field name="name">Withholding Tax Reduction for Low Salaries (ONSS)</field>
|
||||
@@ -318,7 +306,7 @@ elif salary <= 2461.27:
|
||||
<field name="code">Red.Iso</field>
|
||||
<field name="sequence">144</field>
|
||||
<field name="condition_select">python</field>
|
||||
<field name="condition_python">result = (employee.marital == 'single') and (categories.PP + categories.PPRed + categories.FamRed + categories.ChA < 0.0)</field>
|
||||
<field name="condition_python">result = (employee.marital in ['divorced', 'single', 'widower']) and (categories.PP + categories.PPRed + categories.FamRed + categories.ChA < 0.0)</field>
|
||||
<field name="amount_select">code</field>
|
||||
<field name="amount_python_compute">result = min(25.00, - (categories.PP + categories.PPRed + categories.FamRed + categories.ChA))</field>
|
||||
</record>
|
||||
@@ -331,7 +319,7 @@ elif salary <= 2461.27:
|
||||
<field name="condition_select">python</field>
|
||||
<field name="condition_python">result = (employee.marital == 'widower' or (employee.marital in ['divorced', 'single', 'widower'] and employee.dependent_children)) and (categories.PP + categories.PPRed + categories.FamRed + categories.ChA < 0.0)</field>
|
||||
<field name="amount_select">code</field>
|
||||
<field name="amount_python_compute">result = min(35.00, - (categories.PP + categories.PPRed + categories.FamRed + categories.ChA))</field>
|
||||
<field name="amount_python_compute">result = min(36.00, - (categories.PP + categories.PPRed + categories.FamRed + categories.ChA))</field>
|
||||
</record>
|
||||
<record id="hr_salary_rule_reduction_disabled" model="hr.salary.rule">
|
||||
<field name="category_id" ref='hr_salary_rule_category_family_reductions'/>
|
||||
@@ -342,7 +330,7 @@ elif salary <= 2461.27:
|
||||
<field name="condition_select">python</field>
|
||||
<field name="condition_python">result = (employee.disabled) and (categories.PP + categories.PPRed + categories.FamRed + categories.ChA < 0.0)</field>
|
||||
<field name="amount_select">code</field>
|
||||
<field name="amount_python_compute">result = min(35.00, - (categories.PP + categories.PPRed + categories.FamRed + categories.ChA))</field>
|
||||
<field name="amount_python_compute">result = min(36.00, - (categories.PP + categories.PPRed + categories.FamRed + categories.ChA))</field>
|
||||
</record>
|
||||
<record id="hr_salary_rule_reduction_seniors" model="hr.salary.rule">
|
||||
<field name="category_id" ref='hr_salary_rule_category_family_reductions'/>
|
||||
@@ -353,7 +341,7 @@ elif salary <= 2461.27:
|
||||
<field name="condition_select">python</field>
|
||||
<field name="condition_python">result = (employee.other_dependent_people and employee.dependent_seniors) and (categories.PP + categories.PPRed + categories.FamRed + categories.ChA < 0.0)</field>
|
||||
<field name="amount_select">code</field>
|
||||
<field name="amount_python_compute">result = min(71.0 * employee.dependent_seniors, - (categories.PP + categories.PPRed + categories.FamRed + categories.ChA))</field>
|
||||
<field name="amount_python_compute">result = min(73.0 * employee.dependent_seniors, - (categories.PP + categories.PPRed + categories.FamRed + categories.ChA))</field>
|
||||
</record>
|
||||
<record id="hr_salary_rule_reduction_juniors" model="hr.salary.rule">
|
||||
<field name="category_id" ref='hr_salary_rule_category_family_reductions'/>
|
||||
@@ -364,7 +352,7 @@ elif salary <= 2461.27:
|
||||
<field name="condition_select">python</field>
|
||||
<field name="condition_python">result = (employee.other_dependent_people and employee.dependent_juniors) and (categories.PP + categories.PPRed + categories.FamRed + categories.ChA < 0.0)</field>
|
||||
<field name="amount_select">code</field>
|
||||
<field name="amount_python_compute">result = min(35.0 * employee.dependent_juniors, - (categories.PP + categories.PPRed + categories.FamRed + categories.ChA))</field>
|
||||
<field name="amount_python_compute">result = min(36.0 * employee.dependent_juniors, - (categories.PP + categories.PPRed + categories.FamRed + categories.ChA))</field>
|
||||
</record>
|
||||
<record id="hr_salary_rule_reduction_insufficient_net" model="hr.salary.rule">
|
||||
<field name="category_id" ref='hr_salary_rule_category_family_reductions'/>
|
||||
@@ -373,7 +361,7 @@ elif salary <= 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_fiscal_status=='with income' and employee.spouse_net_revenue <= 221.0) and (categories.PP + categories.PPRed + categories.FamRed + categories.ChA < 0.0)</field>
|
||||
<field name="condition_python">result = (employee.marital == 'married' and employee.spouse_fiscal_status=='with income' and employee.spouse_net_revenue <= 225.0) and (categories.PP + categories.PPRed + categories.FamRed + categories.ChA < 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 +372,7 @@ elif salary <= 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 employee.spouse_fiscal_status=='with income' and not employee.spouse_net_revenue and employee.spouse_other_net_revenue <= 440.0) and (categories.PP + categories.PPRed + categories.FamRed + categories.ChA < 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 <= 450.0) and (categories.PP + categories.PPRed + categories.FamRed + categories.ChA < 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>
|
||||
@@ -397,7 +385,7 @@ elif salary <= 2461.27:
|
||||
<field name="condition_select">python</field>
|
||||
<field name="condition_python">result = (employee.disabled_spouse_bool==True) and (categories.PP + categories.PPRed + categories.FamRed + categories.ChA < 0.0)</field>
|
||||
<field name="amount_select">code</field>
|
||||
<field name="amount_python_compute">min(35.0, - (categories.PP + categories.PPRed + categories.FamRed + categories.ChA))</field>
|
||||
<field name="amount_python_compute">min(36.0, - (categories.PP + categories.PPRed + categories.FamRed + categories.ChA))</field>
|
||||
</record>
|
||||
<record id="hr_payroll_rules_parent_company_car" model="hr.salary.rule">
|
||||
<field name="category_id" ref="hr_payroll_head_div_impos"/>
|
||||
@@ -559,7 +547,6 @@ result = -contract.car_atn
|
||||
ref('hr_payroll_rules_retained_net'),
|
||||
ref('hr_salary_rule_commission_on_target'),
|
||||
ref('hr_salary_rule_pp_reduction_employees'),
|
||||
ref('hr_salary_rule_pp_reduction_low_salary'),
|
||||
|
||||
])]" name="rule_ids"/>
|
||||
<field name="company_id" ref="base.main_company"/>
|
||||
|
||||
@@ -94,7 +94,7 @@ class HrContract(models.Model):
|
||||
elif contract.transport_mode == 'public_transport':
|
||||
contract.transport_employer_cost = contract.public_transport_reimbursed_amount
|
||||
elif contract.transport_mode == 'others':
|
||||
contract.transport_employer_cost == contract.others_reimbursed_amount
|
||||
contract.transport_employer_cost = contract.others_reimbursed_amount
|
||||
|
||||
@api.depends('commission_on_target')
|
||||
def _compute_warrants_cost(self):
|
||||
@@ -176,6 +176,16 @@ class HrContract(models.Model):
|
||||
contract.double_holidays = contract.wage * 0.92
|
||||
contract.thirteen_month = contract.wage
|
||||
|
||||
@api.onchange('transport_mode')
|
||||
def _onchange_transport_mode(self):
|
||||
if self.transport_mode != 'company_car':
|
||||
self.fuel_card = 0
|
||||
self.company_car_total_depreciated_cost = 0
|
||||
if self.transport_mode != 'others':
|
||||
self.others_reimbursed_amount = 0
|
||||
if self.transport_mode != 'public_transports':
|
||||
self.public_transport_reimbursed_amount = 0
|
||||
|
||||
@api.onchange('mobile', 'mobile_plus')
|
||||
def _onchange_mobile(self):
|
||||
if self.mobile_plus and not self.mobile:
|
||||
|
||||
@@ -46,6 +46,13 @@ class HrContract(models.Model):
|
||||
self.car_id = self.env['fleet.vehicle'].search([('driver_id', '=', self.employee_id.address_home_id.id)], limit=1)
|
||||
return {'domain': {'car_id': self._get_available_cars_domain()}}
|
||||
|
||||
@api.onchange('transport_mode')
|
||||
def _onchange_transport_mode(self):
|
||||
super(HrContract, self)._onchange_transport_mode()
|
||||
if self.transport_mode != 'company_car':
|
||||
self.car_id = False
|
||||
self.new_car_model_id = False
|
||||
|
||||
def _get_available_cars_domain(self):
|
||||
return ['|', ('driver_id', '=', False), ('driver_id', '=', self.employee_id.address_home_id.id)]
|
||||
|
||||
|
||||
@@ -165,6 +165,7 @@ class XmlDeclaration(models.TransientModel):
|
||||
lines = self.env.cr.fetchall()
|
||||
invoicelines_ids = [rec[0] for rec in lines]
|
||||
invoicelines = self.env['account.invoice.line'].browse(invoicelines_ids)
|
||||
|
||||
for inv_line in invoicelines:
|
||||
|
||||
#Check type of transaction
|
||||
@@ -190,6 +191,8 @@ class XmlDeclaration(models.TransientModel):
|
||||
#comes from purchase
|
||||
po_lines = self.env['purchase.order.line'].search([('invoice_lines', 'in', inv_line.id)], limit=1)
|
||||
if po_lines:
|
||||
if self._is_situation_triangular(company, po_line=po_lines):
|
||||
continue
|
||||
location = self.env['stock.location'].browse(po_lines.order_id._get_destination_location())
|
||||
region_id = self.env['stock.warehouse'].get_regionid_from_locationid(location)
|
||||
if region_id:
|
||||
@@ -198,6 +201,8 @@ class XmlDeclaration(models.TransientModel):
|
||||
#comes from sales
|
||||
so_lines = self.env['sale.order.line'].search([('invoice_lines', 'in', inv_line.id)], limit=1)
|
||||
if so_lines:
|
||||
if self._is_situation_triangular(company, so_line=so_lines):
|
||||
continue
|
||||
saleorder = so_lines.order_id
|
||||
if saleorder and saleorder.warehouse_id and saleorder.warehouse_id.region_id:
|
||||
exreg = IntrastatRegion.browse(saleorder.warehouse_id.region_id.id).code
|
||||
@@ -287,3 +292,24 @@ class XmlDeclaration(models.TransientModel):
|
||||
dim = ET.SubElement(item, 'Dim')
|
||||
dim.set('prop', prop)
|
||||
dim.text = value
|
||||
|
||||
def _is_situation_triangular(self, company, po_line=False, so_line=False):
|
||||
# Ignoring what is purchased and sold by us with a dropshipping route
|
||||
# outside of our country, or completely within it
|
||||
# https://www.nbb.be/doc/dq/f_pdf_ex/intra2017fr.pdf (§ 4.x)
|
||||
dropship_pick_type = self.env.ref('stock_dropshipping.picking_type_dropship', raise_if_not_found=False)
|
||||
if not dropship_pick_type:
|
||||
return False
|
||||
stock_move_domain = [('picking_type_id', '=', dropship_pick_type.id)]
|
||||
|
||||
if po_line:
|
||||
stock_move_domain.append(('purchase_line_id', '=', po_line.id))
|
||||
if so_line:
|
||||
stock_move_domain.append(('procurement_id.sale_line_id', '=', so_line.id))
|
||||
|
||||
stock_move = self.env['stock.move'].search(stock_move_domain, limit=1)
|
||||
return stock_move and (
|
||||
(stock_move.partner_id.country_id.code != company.country_id.code and
|
||||
stock_move.picking_partner_id.country_id.code != company.country_id.code) or
|
||||
(stock_move.partner_id.country_id.code == company.country_id.code and
|
||||
stock_move.picking_partner_id.country_id.code == company.country_id.code))
|
||||
|
||||
@@ -166,6 +166,9 @@ var ChatAction = Widget.extend(ControlPanelMixin, {
|
||||
this.basic_composer.on('input_focused', this, this._onComposerFocused);
|
||||
this.extended_composer.on('post_message', this, this._onPostMessage);
|
||||
this.extended_composer.on('input_focused', this, this._onComposerFocused);
|
||||
// manually call do_search to generate the initial domain and filter
|
||||
// the messages in the default channel
|
||||
self.searchview.do_search();
|
||||
|
||||
var defs = [];
|
||||
defs.push(this._renderButtons());
|
||||
@@ -908,7 +911,12 @@ var ChatAction = Widget.extend(ControlPanelMixin, {
|
||||
contexts: [session.user_context],
|
||||
});
|
||||
this.domain = result.domain;
|
||||
this._fetchAndRenderThread();
|
||||
if (this.channel) {
|
||||
// initially (when _onSearch is called manually), there is no
|
||||
// channel set yet, so don't try to fetch and render the thread as
|
||||
// this will be done as soon as the default channel is set
|
||||
this._fetchAndRenderThread();
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
|
||||
@@ -21,9 +21,9 @@ BasicModel.include({
|
||||
* @param {Object} record - an element from the localData
|
||||
* @param {string} fieldName
|
||||
* @return {Deferred<Object>} the deferred is resolved with the
|
||||
* invalidPartnerIds
|
||||
* invalidPartnerIds and partnerNames
|
||||
*/
|
||||
_setInvalidMany2ManyTagsEmail: function (record, fieldName) {
|
||||
_fetchSpecialMany2ManyTagsEmail: function (record, fieldName) {
|
||||
var self = this;
|
||||
var localID = (record._changes && fieldName in record._changes) ?
|
||||
record._changes[fieldName] :
|
||||
@@ -37,14 +37,26 @@ BasicModel.include({
|
||||
}
|
||||
});
|
||||
var def;
|
||||
if (invalidPartnerIds) {
|
||||
if (invalidPartnerIds.length) {
|
||||
// remove invalid partners
|
||||
var changes = {operation: 'DELETE', ids: _.pluck(invalidPartnerIds, 'id')};
|
||||
def = this._applyX2ManyChange(record, fieldName, changes);
|
||||
}
|
||||
return $.when(def).then(function () {
|
||||
return $.when({
|
||||
invalidPartnerIds: _.pluck(invalidPartnerIds, 'res_id'),
|
||||
list = self._applyX2ManyOperations(self.localData[localID]);
|
||||
if (list.res_ids.length) {
|
||||
def = self._rpc({
|
||||
model: list.model,
|
||||
method: 'name_get',
|
||||
args: list.res_ids,
|
||||
context: record.getContext({fieldName: fieldName}),
|
||||
});
|
||||
}
|
||||
return $.when(def).then(function (names) {
|
||||
return $.when({
|
||||
invalidPartnerIds: _.pluck(invalidPartnerIds, 'res_id'),
|
||||
partnerNames: _.object(names),
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
@@ -54,7 +66,7 @@ var FieldMany2ManyTagsEmail = M2MTags.extend({
|
||||
fieldsToFetch: _.extend({}, M2MTags.prototype.fieldsToFetch, {
|
||||
email: {type: 'char'},
|
||||
}),
|
||||
specialData: "_setInvalidMany2ManyTagsEmail",
|
||||
specialData: "_fetchSpecialMany2ManyTagsEmail",
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Private
|
||||
@@ -132,14 +144,17 @@ var FieldMany2ManyTagsEmail = M2MTags.extend({
|
||||
* Override for res.partner
|
||||
* name_get is dynamic (based on context) while display_name is static
|
||||
* (stored)
|
||||
*
|
||||
* @override
|
||||
* @private
|
||||
*/
|
||||
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]};
|
||||
});
|
||||
_getRenderTagsContext: function () {
|
||||
var result = this._super.apply(this, arguments);
|
||||
var partnerNames = this.record.specialData[this.name].partnerNames;
|
||||
_.each(result.elements, function (partner) {
|
||||
partner.display_name = partnerNames[partner.id];
|
||||
});
|
||||
return result;
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
</dd>
|
||||
<dt>Assigned to</dt>
|
||||
<dd class="mb8">
|
||||
<img t-attf-src="/web/image#{activity.user_id[0] >= 0 ? ('/res.users/' + activity.user_id[0] + '/image_small') : ''}" height="18" width="18" class="img-circle mr4" t-att-title="activity.create_uid[1]"/>
|
||||
<img t-attf-src="/web/image#{activity.user_id[0] >= 0 ? ('/res.users/' + activity.user_id[0] + '/image_small') : ''}" height="18" width="18" class="img-circle mr4" t-att-title="activity.user_id[1]"/>
|
||||
<b><t t-esc="activity.user_id[1]"/></b>
|
||||
<em>, due on </em><span t-attf-class="o_activity_color_#{activity.state}"><t t-esc="activity.date_deadline.format(date_format)"/></span>
|
||||
</dd>
|
||||
|
||||
@@ -1250,8 +1250,9 @@ QUnit.module('FieldMany2ManyTagsEmail', {
|
||||
});
|
||||
|
||||
QUnit.test('fieldmany2many tags email', function (assert) {
|
||||
assert.expect(11);
|
||||
assert.expect(13);
|
||||
var done = assert.async();
|
||||
var nameGottenIds = [[12], [12, 14]];
|
||||
|
||||
this.data.partner.records[0].timmy = [12, 14];
|
||||
|
||||
@@ -1271,7 +1272,11 @@ QUnit.test('fieldmany2many tags email', function (assert) {
|
||||
mode: 'edit',
|
||||
},
|
||||
mockRPC: function (route, args) {
|
||||
if (args.method ==='read' && args.model === 'partner_type') {
|
||||
if (route === "/web/dataset/call_kw/partner_type/name_get") {
|
||||
assert.deepEqual(args.args, nameGottenIds.shift(),
|
||||
"partner with email should be name_get'ed");
|
||||
}
|
||||
else if (args.method ==='read' && args.model === 'partner_type') {
|
||||
assert.step(args.args[0]);
|
||||
assert.deepEqual(args.args[1] , ['display_name', 'email'], "should read the email");
|
||||
}
|
||||
|
||||
@@ -1103,11 +1103,15 @@ class ProductCategory(models.Model):
|
||||
class MailComposeMessage(models.TransientModel):
|
||||
_inherit = 'mail.compose.message'
|
||||
|
||||
@api.multi
|
||||
def mail_purchase_order_on_send(self):
|
||||
if not self.filtered('subtype_id.internal'):
|
||||
order = self.env['purchase.order'].browse(self._context['default_res_id'])
|
||||
if order.state == 'draft':
|
||||
order.state = 'sent'
|
||||
|
||||
@api.multi
|
||||
def send_mail(self, auto_commit=False):
|
||||
if self._context.get('default_model') == 'purchase.order' and self._context.get('default_res_id'):
|
||||
if not self.filtered('subtype_id.internal'):
|
||||
order = self.env['purchase.order'].browse([self._context['default_res_id']])
|
||||
if order.state == 'draft':
|
||||
order.state = 'sent'
|
||||
self.mail_purchase_order_on_send()
|
||||
return super(MailComposeMessage, self.with_context(mail_post_autofollow=True)).send_mail(auto_commit=auto_commit)
|
||||
|
||||
@@ -59,7 +59,7 @@
|
||||
<field name="inherit_id" ref="account.invoice_supplier_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<field name="reference" position="after" >
|
||||
<field name="purchase_id" attrs="{'invisible': [('state', '=', 'purchase')]}" class="oe_edit_only"
|
||||
<field name="purchase_id" attrs="{'readonly': [('state','not in',['draft'])], 'invisible': [('state', '=', 'purchase')]}" class="oe_edit_only"
|
||||
options="{'no_create': True}" context="{'show_total_amount': True}"/>
|
||||
</field>
|
||||
<xpath expr="//field[@name='invoice_line_ids']/tree/field[@name='company_id']" position="after">
|
||||
|
||||
@@ -849,6 +849,18 @@ class SaleOrderLine(models.Model):
|
||||
precision = self.env['decimal.precision'].precision_get('Product Unit of Measure')
|
||||
self.filtered(
|
||||
lambda r: r.state == 'sale' and float_compare(r.product_uom_qty, values['product_uom_qty'], precision_digits=precision) != 0)._update_line_quantity(values)
|
||||
|
||||
# Prevent writing on a locked SO.
|
||||
protected_fields = self._get_protected_fields()
|
||||
if 'done' in self.mapped('order_id.state') and any(f in values.keys() for f in protected_fields):
|
||||
fields = self.env['ir.model.fields'].search([
|
||||
('name', 'in', protected_fields), ('model', '=', self._name)
|
||||
])
|
||||
raise UserError(
|
||||
_('It is forbidden to modify the following fields in a locked order:\n%s')
|
||||
% '\n'.join(fields.mapped('field_description'))
|
||||
)
|
||||
|
||||
result = super(SaleOrderLine, self).write(values)
|
||||
return result
|
||||
|
||||
@@ -1138,6 +1150,12 @@ class SaleOrderLine(models.Model):
|
||||
|
||||
return product[field_name] * uom_factor * cur_factor, currency_id.id
|
||||
|
||||
def _get_protected_fields(self):
|
||||
return [
|
||||
'product_id', 'name', 'price_unit', 'product_uom', 'product_uom_qty',
|
||||
'tax_id', 'analytic_tag_ids'
|
||||
]
|
||||
|
||||
@api.onchange('product_id', 'price_unit', 'product_uom', 'product_uom_qty', 'tax_id')
|
||||
def _onchange_discount(self):
|
||||
self.discount = 0.0
|
||||
|
||||
@@ -76,12 +76,14 @@ class SaleAdvancePaymentInv(models.TransientModel):
|
||||
|
||||
if self.amount <= 0.00:
|
||||
raise UserError(_('The value of the down payment amount must be positive.'))
|
||||
context = {'lang': order.partner_id.lang}
|
||||
if self.advance_payment_method == 'percentage':
|
||||
amount = order.amount_untaxed * self.amount / 100
|
||||
name = _("Down payment of %s%%") % (self.amount,)
|
||||
else:
|
||||
amount = self.amount
|
||||
name = _('Down Payment')
|
||||
del context
|
||||
taxes = self.product_id.taxes_id.filtered(lambda r: not order.company_id or r.company_id == order.company_id)
|
||||
if order.fiscal_position_id and taxes:
|
||||
tax_ids = order.fiscal_position_id.map_tax(taxes).ids
|
||||
@@ -152,6 +154,7 @@ class SaleAdvancePaymentInv(models.TransientModel):
|
||||
tax_ids = order.fiscal_position_id.map_tax(taxes).ids
|
||||
else:
|
||||
tax_ids = taxes.ids
|
||||
context = {'lang': order.partner_id.lang}
|
||||
so_line = sale_line_obj.create({
|
||||
'name': _('Advance: %s') % (time.strftime('%m %Y'),),
|
||||
'price_unit': amount,
|
||||
@@ -163,6 +166,7 @@ class SaleAdvancePaymentInv(models.TransientModel):
|
||||
'tax_id': [(6, 0, tax_ids)],
|
||||
'is_downpayment': True,
|
||||
})
|
||||
del context
|
||||
self._create_invoice(order, so_line, amount)
|
||||
if self._context.get('open_invoices', False):
|
||||
return sale_orders.action_view_invoice()
|
||||
|
||||
@@ -13,6 +13,7 @@ class PaymentTransaction(models.Model):
|
||||
|
||||
# YTI FIXME: The auto_join seems useless
|
||||
sale_order_id = fields.Many2one('sale.order', string='Sales Order', auto_join=True)
|
||||
so_state = fields.Selection('sale.order', string='Sale Order State', related='sale_order_id.state')
|
||||
|
||||
@api.model
|
||||
def form_feedback(self, data, acquirer_name):
|
||||
|
||||
@@ -15,6 +15,22 @@ msgstr ""
|
||||
"Content-Transfer-Encoding: \n"
|
||||
"Plural-Forms: \n"
|
||||
|
||||
#. module: stock
|
||||
#: code:addons/stock/models/stock_move.py:230
|
||||
#, python-format
|
||||
msgid "\n"
|
||||
"\n"
|
||||
"%s --> Product UoM is %s (%s) - Move UoM is %s (%s)"
|
||||
msgstr ""
|
||||
|
||||
#. module: stock
|
||||
#: code:addons/stock/models/stock_move.py:231
|
||||
#, python-format
|
||||
msgid "\n"
|
||||
"\n"
|
||||
"Blocking: %s"
|
||||
msgstr ""
|
||||
|
||||
#. module: stock
|
||||
#: code:addons/stock/models/stock_move.py:308
|
||||
#, python-format
|
||||
|
||||
@@ -316,10 +316,12 @@ class StockMove(models.Model):
|
||||
|
||||
@api.constrains('product_uom')
|
||||
def _check_uom(self):
|
||||
moves_error = self.filtered(lambda move: move.product_id.uom_id.category_id.id != move.product_uom.category_id.id)
|
||||
moves_error = self.filtered(lambda move: move.product_id.uom_id.category_id != move.product_uom.category_id)
|
||||
if moves_error:
|
||||
user_warning = _('You try to move a product using a UoM that is not compatible with the UoM of the product moved. Please use an UoM in the same UoM category.')
|
||||
user_warning += '\n\nBlocking: %s' % ' ,'.join(moves_error.mapped('name'))
|
||||
for move in moves_error:
|
||||
user_warning += _('\n\n%s --> Product UoM is %s (%s) - Move UoM is %s (%s)') % (move.product_id.display_name, move.product_id.uom_id.name, move.product_id.uom_id.category_id.name, move.product_uom.name, move.product_uom.category_id.name)
|
||||
user_warning += _('\n\nBlocking: %s') % ' ,'.join(moves_error.mapped('name'))
|
||||
raise UserError(user_warning)
|
||||
|
||||
@api.model_cr
|
||||
|
||||
@@ -455,6 +455,7 @@ class Warehouse(models.Model):
|
||||
self.Routing(warehouse.lot_stock_id, warehouse.wh_pack_stock_loc_id, warehouse.pick_type_id),
|
||||
self.Routing(warehouse.wh_pack_stock_loc_id, warehouse.wh_output_stock_loc_id, warehouse.pack_type_id),
|
||||
self.Routing(warehouse.wh_output_stock_loc_id, customer_loc, warehouse.out_type_id)],
|
||||
'company_id': warehouse.company_id.id,
|
||||
}) for warehouse in self)
|
||||
|
||||
def _get_reception_delivery_route_values(self, route_type):
|
||||
@@ -495,7 +496,8 @@ class Warehouse(models.Model):
|
||||
'product_categ_selectable': True,
|
||||
'active': self.delivery_steps != 'ship_only' and self.reception_steps != 'one_step',
|
||||
'company_id': self.company_id.id,
|
||||
'sequence': 20}
|
||||
'sequence': 20,
|
||||
}
|
||||
|
||||
# Pull / Push tools
|
||||
# ------------------------------------------------------------
|
||||
|
||||
@@ -99,6 +99,7 @@
|
||||
<field name="product_id" type="row"/>
|
||||
<field name="location_id" type="col"/>
|
||||
<field name="quantity" type="measure"/>
|
||||
<field name="inventory_value" type="measure"/>
|
||||
</pivot>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
@@ -117,7 +117,9 @@ class ProductProduct(models.Model):
|
||||
""" Changes the Standard Price of Product and creates an account move accordingly."""
|
||||
AccountMove = self.env['account.move']
|
||||
|
||||
locations = self.env['stock.location'].search([('usage', '=', 'internal'), ('company_id', '=', self.env.user.company_id.id)])
|
||||
quant_locs = self.env['stock.quant'].sudo().read_group([('product_id', 'in', self.ids)], ['location_id'], ['location_id'])
|
||||
quant_loc_ids = [loc['location_id'][0] for loc in quant_locs]
|
||||
locations = self.env['stock.location'].search([('usage', '=', 'internal'), ('company_id', '=', self.env.user.company_id.id), ('id', 'in', quant_loc_ids)])
|
||||
|
||||
product_accounts = {product.id: product.product_tmpl_id.get_product_accounts() for product in self}
|
||||
|
||||
|
||||
@@ -97,11 +97,13 @@ var x2ManyCommands = {
|
||||
// (0, virtualID, {values})
|
||||
CREATE: 0,
|
||||
create: function (virtualID, values) {
|
||||
delete values.id;
|
||||
return [x2ManyCommands.CREATE, virtualID || false, values];
|
||||
},
|
||||
// (1, id, {values})
|
||||
UPDATE: 1,
|
||||
update: function (id, values) {
|
||||
delete values.id;
|
||||
return [x2ManyCommands.UPDATE, id, values];
|
||||
},
|
||||
// (2, id[, _])
|
||||
@@ -2669,7 +2671,6 @@ var BasicModel = AbstractModel.extend({
|
||||
_.each(relRecordUpdated, function (relRecord) {
|
||||
var changes = self._generateChanges(relRecord, options);
|
||||
if (!_.isEmpty(changes)) {
|
||||
delete changes.id;
|
||||
var command = x2ManyCommands.update(relRecord.res_id, changes);
|
||||
commands[fieldName].push(command);
|
||||
}
|
||||
@@ -2689,7 +2690,6 @@ var BasicModel = AbstractModel.extend({
|
||||
relRecord = _.findWhere(relRecordUpdated, {res_id: list.res_ids[i]});
|
||||
changes = relRecord ? this._generateChanges(relRecord, options) : {};
|
||||
if (!_.isEmpty(changes)) {
|
||||
delete changes.id;
|
||||
command = x2ManyCommands.update(relRecord.res_id, changes);
|
||||
didChange = true;
|
||||
} else {
|
||||
@@ -2700,7 +2700,7 @@ var BasicModel = AbstractModel.extend({
|
||||
// this is a new id (maybe existing in DB, but new in JS)
|
||||
relRecord = _.findWhere(relRecordAdded, {res_id: list.res_ids[i]});
|
||||
changes = this._generateChanges(relRecord, options);
|
||||
if ('id' in changes) {
|
||||
if (changes.id) {
|
||||
// the subrecord already exists in db
|
||||
delete changes.id;
|
||||
if (this.isNew(record.id)) {
|
||||
|
||||
@@ -835,7 +835,7 @@
|
||||
<t t-foreach="elements" t-as="el">
|
||||
<t t-set="color" t-value="el[colorField] || 0"/>
|
||||
<span t-attf-class="badge dropdown o_tag_color_#{color}" t-att-data-color="color" t-att-data-index="el_index" t-att-data-id="el.id">
|
||||
<span class="o_badge_text"><t t-raw="el.display_name"/></span>
|
||||
<span class="o_badge_text"><t t-esc="el.display_name"/></span>
|
||||
<span t-if="!readonly" class="fa fa-times o_delete"/>
|
||||
</span>
|
||||
</t>
|
||||
|
||||
@@ -5435,6 +5435,37 @@ QUnit.module('relational_fields', {
|
||||
form.destroy();
|
||||
});
|
||||
|
||||
QUnit.test('id field in one2many in a new record', function (assert) {
|
||||
assert.expect(1);
|
||||
|
||||
var form = createView({
|
||||
View: FormView,
|
||||
model: 'partner',
|
||||
data: this.data,
|
||||
arch: '<form string="Partners">' +
|
||||
'<field name="turtles">' +
|
||||
'<tree editable="bottom">' +
|
||||
'<field name="id" invisible="1"/>' +
|
||||
'<field name="turtle_foo"/>' +
|
||||
'</tree>' +
|
||||
'</field>' +
|
||||
'</form>',
|
||||
mockRPC: function (route, args) {
|
||||
if (args.method === 'create') {
|
||||
assert.deepEqual(args.args[0].turtles,
|
||||
[[0, false, {turtle_foo: "cat"}]],
|
||||
'should send proper commands');
|
||||
}
|
||||
return this._super.apply(this, arguments);
|
||||
},
|
||||
});
|
||||
form.$('td.o_field_x2many_list_row_add a').click();
|
||||
form.$('td input[name="turtle_foo"]').val('cat').trigger('input');
|
||||
form.$buttons.find('.o_form_button_save').click();
|
||||
|
||||
form.destroy();
|
||||
});
|
||||
|
||||
QUnit.test('sub form view with a required field', function (assert) {
|
||||
assert.expect(2);
|
||||
this.data.partner.fields.foo.required = true;
|
||||
|
||||
@@ -437,7 +437,7 @@ class Post(models.Model):
|
||||
elif post.parent_id and not post.can_answer:
|
||||
raise KarmaError('Not enough karma to answer to a question')
|
||||
if not post.parent_id and not post.can_post:
|
||||
post.state = 'pending'
|
||||
post.sudo().state = 'pending'
|
||||
|
||||
# add karma for posting new questions
|
||||
if not post.parent_id and post.state == 'active':
|
||||
@@ -466,11 +466,18 @@ class Post(models.Model):
|
||||
|
||||
@api.multi
|
||||
def write(self, vals):
|
||||
trusted_keys = ['active', 'is_correct', 'tag_ids'] # fields where security is checked manually
|
||||
if 'content' in vals:
|
||||
vals['content'] = self._update_content(vals['content'], self.forum_id.id)
|
||||
if 'state' in vals:
|
||||
if vals['state'] in ['active', 'close'] and any(not post.can_close for post in self):
|
||||
raise KarmaError('Not enough karma to close or reopen a post.')
|
||||
if vals['state'] in ['active', 'close']:
|
||||
if any(not post.can_close for post in self):
|
||||
raise KarmaError('Not enough karma to close or reopen a post.')
|
||||
trusted_keys += ['state', 'closed_uid', 'closed_date', 'closed_reason_id']
|
||||
elif vals['state'] == 'flagged':
|
||||
if any(not post.can_flag for post in self):
|
||||
raise KarmaError('Not enough karma to flag a post.')
|
||||
trusted_keys += ['state', 'flag_user_id']
|
||||
if 'active' in vals:
|
||||
if any(not post.can_unlink for post in self):
|
||||
raise KarmaError('Not enough karma to delete or reactivate a post')
|
||||
@@ -487,7 +494,7 @@ class Post(models.Model):
|
||||
tag_ids = set(tag.get('id') for tag in self.resolve_2many_commands('tag_ids', vals['tag_ids']))
|
||||
if any(set(post.tag_ids) != tag_ids for post in self) and any(self.env.user.karma < post.forum_id.karma_edit_retag for post in self):
|
||||
raise KarmaError(_('Not enough karma to retag.'))
|
||||
if any(key not in ['state', 'active', 'is_correct', 'closed_uid', 'closed_date', 'closed_reason_id', 'tag_ids'] for key in vals) and any(not post.can_edit for post in self):
|
||||
if any(key not in trusted_keys for key in vals) and any(not post.can_edit for post in self):
|
||||
raise KarmaError('Not enough karma to edit a post.')
|
||||
|
||||
res = super(Post, self).write(vals)
|
||||
|
||||
@@ -5,9 +5,9 @@
|
||||
<div>
|
||||
<div t-if="'karma' in fields" class='css_editable_mode_hidden'>
|
||||
<div t-if="options.get('UserBio')">
|
||||
<span t-field="object.company_id.name" class="o_forum_tooltip_line"/><br/>
|
||||
<a t-att-href="object.company_id.website" t-if="object.company_id.website">
|
||||
<span t-field="object.company_id.website" class="o_forum_tooltip_line"/>
|
||||
<span t-field="object.company_name" class="o_forum_tooltip_line"/><br/>
|
||||
<a t-att-href="object.website" t-if="object.website">
|
||||
<span t-field="object.website" class="o_forum_tooltip_line"/>
|
||||
</a>
|
||||
</div>
|
||||
<b><t t-esc="object.karma"/></b>
|
||||
|
||||
@@ -259,7 +259,8 @@ class SaleOrderOption(models.Model):
|
||||
|
||||
order_line = order.order_line.filtered(lambda line: line.product_id == self.product_id)
|
||||
if order_line:
|
||||
order_line[0].product_uom_qty += 1
|
||||
order_line = order_line[0]
|
||||
order_line.product_uom_qty += 1
|
||||
else:
|
||||
vals = {
|
||||
'price_unit': self.price_unit,
|
||||
|
||||
Reference in New Issue
Block a user