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

This commit is contained in:
Christophe Simonis
2018-01-09 15:54:16 +01:00
18 changed files with 120 additions and 23 deletions
+4 -1
View File
@@ -283,7 +283,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
@@ -805,6 +806,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 len(self.env['account.account'].search([('company_id', '=', self.company_id.id)])) > 0:
# We are in a case where we already have some accounts existing, meaning that user has probably
# created its own accounts and does not need a coa, so skip installation of coa.
@@ -68,8 +68,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
@@ -164,6 +164,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
@@ -189,6 +190,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:
@@ -197,6 +200,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
@@ -286,3 +291,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))
+9 -1
View File
@@ -244,6 +244,9 @@ var ChatAction = Widget.extend(ControlPanelMixin, {
var def3 = this.extended_composer.appendTo(this.$('.o_mail_chat_content'));
var def4 = this.searchview.appendTo($("<div>")).then(function () {
self.$searchview_buttons = self.searchview.$buttons.contents();
// manually call do_search to generate the initial domain and filter
// the messages in the default channel
self.searchview.do_search();
});
this.render_sidebar();
@@ -556,7 +559,12 @@ var ChatAction = Widget.extend(ControlPanelMixin, {
});
this.domain = result.domain;
this.fetch_and_render_thread();
if (this.channel) {
// initially (when do_search 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.fetch_and_render_thread();
}
},
on_post_message: function (message) {
+2 -2
View File
@@ -282,7 +282,7 @@ class StockMove(models.Model):
preferred_domain_list = [preferred_domain] + [fallback_domain] + [fallback_domain2]
if move.has_tracking == 'none':
quants = quant_obj.quants_get_preferred_domain(move.product_qty, move, domain=main_domain, preferred_domain_list=preferred_domain_list)
self.env['stock.quant'].quants_move(quants, move, move.location_dest_id)
self.env['stock.quant'].quants_move(quants, move, move.location_dest_id, owner_id=move.restrict_partner_id.id)
else:
for movelot in move.active_move_lot_ids:
if float_compare(movelot.quantity_done, 0, precision_rounding=rounding) > 0:
@@ -290,7 +290,7 @@ class StockMove(models.Model):
raise UserError(_('You need to supply a lot/serial number.'))
qty = move.product_uom._compute_quantity(movelot.quantity_done, move.product_id.uom_id)
quants = quant_obj.quants_get_preferred_domain(qty, move, lot_id=movelot.lot_id.id, domain=main_domain, preferred_domain_list=preferred_domain_list)
self.env['stock.quant'].quants_move(quants, move, move.location_dest_id, lot_id = movelot.lot_id.id)
self.env['stock.quant'].quants_move(quants, move, move.location_dest_id, lot_id = movelot.lot_id.id, owner_id=move.restrict_partner_id.id)
moves_to_unreserve |= move
# Next move in production order
if move.move_dest_id and move.move_dest_id.state not in ('done', 'cancel'):
+8 -4
View File
@@ -1167,11 +1167,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}"/>
</field>
<xpath expr="//field[@name='invoice_line_ids']/tree/field[@name='company_id']" position="after">
+18
View File
@@ -737,6 +737,18 @@ class SaleOrderLine(models.Model):
msg += _("Invoiced Quantity") + ": %s <br/>" % (line.qty_invoiced,)
msg += "</ul>"
order.message_post(body=msg)
# 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)
if lines:
lines._action_procurement_create()
@@ -995,6 +1007,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()
+16
View File
@@ -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:214
#, python-format
+4 -2
View File
@@ -223,10 +223,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
+6 -2
View File
@@ -457,6 +457,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)
@api.multi
@@ -466,6 +467,7 @@ class Warehouse(models.Model):
'product_categ_selectable': True,
'product_selectable': False,
'sequence': 10,
'company_id': self.company_id.id,
}
@api.model
@@ -485,7 +487,8 @@ class Warehouse(models.Model):
'product_selectable': True,
'product_categ_selectable': True,
'supplied_wh_id': self.id,
'supplier_wh_id': supplier_warehouse.id}
'supplier_wh_id': supplier_warehouse.id,
'company_id': self.company_id.id}
def _get_crossdock_route_values(self):
return {
@@ -494,7 +497,8 @@ class Warehouse(models.Model):
'product_selectable': True,
'product_categ_selectable': True,
'active': self.delivery_steps != 'ship_only' and self.reception_steps != 'one_step',
'sequence': 20}
'sequence': 20,
'company_id': self.company_id.id}
# Pull / Push tools
# ------------------------------------------------------------
+1
View File
@@ -116,6 +116,7 @@
<pivot string="Inventory">
<field name="location_id" type="row"/>
<field name="qty" type="measure"/>
<field name="inventory_value" type="measure"/>
</pivot>
</field>
</record>
+3 -1
View File
@@ -97,7 +97,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}
+11 -4
View File
@@ -442,7 +442,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':
@@ -471,11 +471,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')
@@ -492,7 +499,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.keys()) 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)
+3 -3
View File
@@ -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>
@@ -12,6 +12,7 @@ class PaymentTransaction(models.Model):
# link with the sales order
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')
def _generate_and_pay_invoice(self, tx, acquirer_name):
tx.sale_order_id._force_lines_to_invoice_policy_order()
+2 -1
View File
@@ -263,7 +263,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,