[MERGE] forward port branch 10.0 up to 73c2ac451a
This commit is contained in:
@@ -934,25 +934,7 @@ class AccountInvoice(models.Model):
|
||||
|
||||
@api.model
|
||||
def line_get_convert(self, line, part):
|
||||
return {
|
||||
'date_maturity': line.get('date_maturity', False),
|
||||
'partner_id': part,
|
||||
'name': line['name'],
|
||||
'debit': line['price'] > 0 and line['price'],
|
||||
'credit': line['price'] < 0 and -line['price'],
|
||||
'account_id': line['account_id'],
|
||||
'analytic_line_ids': line.get('analytic_line_ids', []),
|
||||
'amount_currency': line['price'] > 0 and abs(line.get('amount_currency', False)) or -abs(line.get('amount_currency', False)),
|
||||
'currency_id': line.get('currency_id', False),
|
||||
'quantity': line.get('quantity', 1.00),
|
||||
'product_id': line.get('product_id', False),
|
||||
'product_uom_id': line.get('uom_id', False),
|
||||
'analytic_account_id': line.get('account_analytic_id', False),
|
||||
'invoice_id': line.get('invoice_id', False),
|
||||
'tax_ids': line.get('tax_ids', False),
|
||||
'tax_line_id': line.get('tax_line_id', False),
|
||||
'analytic_tag_ids': line.get('analytic_tag_ids', False),
|
||||
}
|
||||
return self.env['product.product']._convert_prepared_anglosaxon_line(line, part)
|
||||
|
||||
@api.multi
|
||||
def action_cancel(self):
|
||||
|
||||
@@ -71,3 +71,28 @@ class ProductTemplate(models.Model):
|
||||
if not fiscal_pos:
|
||||
fiscal_pos = self.env['account.fiscal.position']
|
||||
return fiscal_pos.map_accounts(accounts)
|
||||
|
||||
class ProductProduct(models.Model):
|
||||
_inherit = "product.product"
|
||||
|
||||
@api.model
|
||||
def _convert_prepared_anglosaxon_line(self, line, partner):
|
||||
return {
|
||||
'date_maturity': line.get('date_maturity', False),
|
||||
'partner_id': partner,
|
||||
'name': line['name'],
|
||||
'debit': line['price'] > 0 and line['price'],
|
||||
'credit': line['price'] < 0 and -line['price'],
|
||||
'account_id': line['account_id'],
|
||||
'analytic_line_ids': line.get('analytic_line_ids', []),
|
||||
'amount_currency': line['price'] > 0 and abs(line.get('amount_currency', False)) or -abs(line.get('amount_currency', False)),
|
||||
'currency_id': line.get('currency_id', False),
|
||||
'quantity': line.get('quantity', 1.00),
|
||||
'product_id': line.get('product_id', False),
|
||||
'product_uom_id': line.get('uom_id', False),
|
||||
'analytic_account_id': line.get('account_analytic_id', False),
|
||||
'invoice_id': line.get('invoice_id', False),
|
||||
'tax_ids': line.get('tax_ids', False),
|
||||
'tax_line_id': line.get('tax_line_id', False),
|
||||
'analytic_tag_ids': line.get('analytic_tag_ids', False),
|
||||
}
|
||||
|
||||
@@ -37,6 +37,9 @@ class AccountVoucher(models.Model):
|
||||
help="Effective date for accounting entries", copy=False, default=fields.Date.context_today)
|
||||
journal_id = fields.Many2one('account.journal', 'Journal',
|
||||
required=True, readonly=True, states={'draft': [('readonly', False)]}, default=_default_journal)
|
||||
payment_journal_id = fields.Many2one('account.journal', string='Payment Method', readonly=True, store=False,
|
||||
states={'draft': [('readonly', False)]}, domain="[('type', 'in', ['cash', 'bank'])]",
|
||||
compute='_compute_payment_journal_id', inverse='_inverse_payment_journal_id')
|
||||
account_id = fields.Many2one('account.account', 'Account',
|
||||
required=True, readonly=True, states={'draft': [('readonly', False)]},
|
||||
domain="[('deprecated', '=', False), ('internal_type','=', (pay_now == 'pay_now' and 'liquidity' or voucher_type == 'purchase' and 'payable' or 'receivable'))]")
|
||||
@@ -101,6 +104,29 @@ class AccountVoucher(models.Model):
|
||||
def _get_journal_currency(self):
|
||||
self.currency_id = self.journal_id.currency_id.id or self.company_id.currency_id.id
|
||||
|
||||
@api.depends('company_id', 'pay_now', 'account_id')
|
||||
def _compute_payment_journal_id(self):
|
||||
for voucher in self:
|
||||
if voucher.pay_now != 'pay_now':
|
||||
continue
|
||||
domain = [
|
||||
('type', 'in', ('bank', 'cash')),
|
||||
('company_id', '=', voucher.company_id.id),
|
||||
]
|
||||
if voucher.account_id and voucher.account_id.internal_type == 'liquidity':
|
||||
field = 'default_debit_account_id' if voucher.voucher_type == 'sale' else 'default_credit_account_id'
|
||||
domain.append((field, '=', voucher.account_id.id))
|
||||
voucher.payment_journal_id = self.env['account.journal'].search(domain, limit=1)
|
||||
|
||||
def _inverse_payment_journal_id(self):
|
||||
for voucher in self:
|
||||
if voucher.pay_now != 'pay_now':
|
||||
continue
|
||||
if voucher.voucher_type == 'sale':
|
||||
voucher.account_id = voucher.payment_journal_id.default_debit_account_id
|
||||
else:
|
||||
voucher.account_id = voucher.payment_journal_id.default_credit_account_id
|
||||
|
||||
@api.multi
|
||||
@api.depends('tax_correction', 'line_ids.price_subtotal')
|
||||
def _compute_total(self):
|
||||
@@ -114,22 +140,13 @@ class AccountVoucher(models.Model):
|
||||
voucher.amount = total + voucher.tax_correction
|
||||
voucher.tax_amount = tax_amount
|
||||
|
||||
@api.one
|
||||
@api.depends('account_pay_now_id', 'account_pay_later_id', 'pay_now')
|
||||
def _get_account(self):
|
||||
self.account_id = self.account_pay_now_id if self.pay_now == 'pay_now' else self.account_pay_later_id
|
||||
|
||||
@api.onchange('date')
|
||||
def onchange_date(self):
|
||||
self.account_date = self.date
|
||||
|
||||
@api.onchange('partner_id', 'pay_now')
|
||||
def onchange_partner_id(self):
|
||||
if self.pay_now == 'pay_now':
|
||||
liq_journal = self.env['account.journal'].search([('type', 'in', ('bank', 'cash'))], limit=1)
|
||||
self.account_id = liq_journal.default_debit_account_id \
|
||||
if self.voucher_type == 'sale' else liq_journal.default_credit_account_id
|
||||
else:
|
||||
if self.pay_now != 'pay_now':
|
||||
if self.partner_id:
|
||||
self.account_id = self.partner_id.property_account_receivable_id \
|
||||
if self.voucher_type == 'sale' else self.partner_id.property_account_payable_id
|
||||
@@ -177,7 +194,7 @@ class AccountVoucher(models.Model):
|
||||
'account_id': self.account_id.id,
|
||||
'move_id': move_id,
|
||||
'journal_id': self.journal_id.id,
|
||||
'partner_id': self.partner_id.id,
|
||||
'partner_id': self.partner_id.commercial_partner_id.id,
|
||||
'currency_id': company_currency != current_currency and current_currency or False,
|
||||
'amount_currency': (sign * abs(self.amount) # amount < 0 for refunds
|
||||
if company_currency != current_currency else 0.0),
|
||||
@@ -224,19 +241,29 @@ class AccountVoucher(models.Model):
|
||||
|
||||
@api.multi
|
||||
def voucher_pay_now_payment_create(self):
|
||||
payment_methods = self.journal_id.outbound_payment_method_ids
|
||||
if self.voucher_type == 'sale':
|
||||
payment_methods = self.journal_id.inbound_payment_method_ids
|
||||
payment_type = 'inbound'
|
||||
partner_type = 'customer'
|
||||
sequence_code = 'account.payment.customer.invoice'
|
||||
else:
|
||||
payment_methods = self.journal_id.outbound_payment_method_ids
|
||||
payment_type = 'outbound'
|
||||
partner_type = 'supplier'
|
||||
sequence_code = 'account.payment.supplier.invoice'
|
||||
name = self.env['ir.sequence'].with_context(ir_sequence_date=self.date).next_by_code(sequence_code)
|
||||
return {
|
||||
'payment_type': 'outbound',
|
||||
'name': name,
|
||||
'payment_type': payment_type,
|
||||
'payment_method_id': payment_methods and payment_methods[0].id or False,
|
||||
'partner_type': 'supplier',
|
||||
'partner_id': self.partner_id.id,
|
||||
'partner_type': partner_type,
|
||||
'partner_id': self.partner_id.commercial_partner_id.id,
|
||||
'amount': self.amount,
|
||||
'currency_id': self.currency_id.id,
|
||||
'payment_date': self.date,
|
||||
'journal_id': self.journal_id.id,
|
||||
'journal_id': self.payment_journal_id.id,
|
||||
'company_id': self.company_id.id,
|
||||
'communication': self.name,
|
||||
'name': self.name,
|
||||
'state': 'reconciled',
|
||||
}
|
||||
|
||||
@@ -268,7 +295,7 @@ class AccountVoucher(models.Model):
|
||||
'name': line.name or '/',
|
||||
'account_id': line.account_id.id,
|
||||
'move_id': move_id,
|
||||
'partner_id': self.partner_id.id,
|
||||
'partner_id': self.partner_id.commercial_partner_id.id,
|
||||
'analytic_account_id': line.account_analytic_id and line.account_analytic_id.id or False,
|
||||
'quantity': 1,
|
||||
'credit': abs(amount) if self.voucher_type == 'sale' else 0.0,
|
||||
|
||||
@@ -230,13 +230,18 @@
|
||||
<group>
|
||||
<field name="voucher_type" invisible="True"/>
|
||||
<field name="currency_id" invisible="True"/>
|
||||
<field name="company_id" options="{'no_create': True}" groups="base.group_multi_company"/>
|
||||
<field name="partner_id" domain="[('customer','=',True)]" string="Customer" context="{'search_default_customer':1, 'show_address': 1}" options='{"always_reload": True}'/>
|
||||
<field name="pay_now" required="1"/>
|
||||
<field name="account_id"/>
|
||||
<field name="payment_journal_id"
|
||||
attrs="{'invisible': [('pay_now', '!=', 'pay_now')], 'required': [('pay_now', '=', 'pay_now')]}"/>
|
||||
<field name="account_id" attrs="{'invisible': [('pay_now', '=', 'pay_now')]}"/>
|
||||
<field name="company_id" options="{'no_create': True}" groups="base.group_multi_company"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="journal_id" domain="[('type','=','sale')]" widget="selection" groups="account.group_account_user"/>
|
||||
<field name="journal_id"
|
||||
domain="[('type','=','sale')]"
|
||||
widget="selection"
|
||||
groups="account.group_account_user"/>
|
||||
<field name="date"/>
|
||||
<field name="date_due" attrs="{'invisible':[('pay_now','=','pay_now')]}"/>
|
||||
<field name="name" attrs="{'invisible': [('pay_now', '=', 'pay_later')]}"/>
|
||||
@@ -338,25 +343,27 @@
|
||||
<h1>
|
||||
<field name="number" readonly="1"/>
|
||||
</h1>
|
||||
<field name="voucher_type" invisible="True"/>
|
||||
<group>
|
||||
<group>
|
||||
<field name="voucher_type" invisible="True"/>
|
||||
<field name="currency_id" invisible="True"/>
|
||||
<field name="partner_id" domain="[('supplier','=',True)]" string="Vendor" context="{'default_customer': 0, 'search_default_supplier': 1, 'default_supplier': 1}" />
|
||||
<field name="pay_now" required="1"/>
|
||||
<field name="account_id"/>
|
||||
<field name="name" colspan="2" attrs="{'invisible': [('pay_now', '=', 'pay_later')]}"/>
|
||||
<field name="reference"/>
|
||||
<field name="payment_journal_id"
|
||||
attrs="{'invisible': [('pay_now', '!=', 'pay_now')], 'required': [('pay_now', '=', 'pay_now')]}"/>
|
||||
<field name="account_id" groups="account.group_account_user" attrs="{'invisible': [('pay_now', '=', 'pay_now')]}"/>
|
||||
<field name="company_id" options="{'no_create': True}" groups="base.group_multi_company"/>
|
||||
<field name="reference"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="journal_id"
|
||||
domain="[('type','=','purchase')]"
|
||||
widget="selection"
|
||||
groups="account.group_account_user"/>
|
||||
<field name="date" string="Bill Date"/>
|
||||
<field name="date_due" attrs="{'invisible': [('pay_now', '=', 'pay_now')]}"/>
|
||||
<field name="name" colspan="2" attrs="{'invisible': [('pay_now', '=', 'pay_later')]}"/>
|
||||
<field name="paid" invisible="1"/>
|
||||
<field name="currency_id" invisible="1"/>
|
||||
<field name="journal_id"
|
||||
domain="[('type','=','purchase')]"
|
||||
widget="selection"
|
||||
groups="account.group_account_user"/>
|
||||
</group>
|
||||
</group>
|
||||
<notebook>
|
||||
|
||||
@@ -89,6 +89,13 @@ class MailController(http.Controller):
|
||||
return cls._redirect_to_messaging()
|
||||
if record_action['type'] == 'ir.actions.act_url':
|
||||
return werkzeug.utils.redirect(record_action['url'])
|
||||
else:
|
||||
# Specific case in 10.0 only: not logged users could receive an act_url that is
|
||||
# not public. As we don't handle fully access tokens in 10.0 we have to redirect
|
||||
# to the login to avoid access issues and/or crash when computing url_params.
|
||||
# CHS-note: do not forward-port me as in saas-16 it is already managed
|
||||
if record_action['type'] == 'ir.actions.act_url':
|
||||
return cls._redirect_to_messaging()
|
||||
|
||||
url_params = {
|
||||
'view_type': record_action['view_type'],
|
||||
|
||||
@@ -87,11 +87,11 @@ options.registry["width-x"] = options.Class.extend({
|
||||
});
|
||||
|
||||
options.registry.table_item = options.Class.extend({
|
||||
on_clone: function ($clone) {
|
||||
on_clone: function ($clone, options) {
|
||||
this._super.apply(this, arguments);
|
||||
|
||||
// If we cloned a td or th element...
|
||||
if (this.$target.is("td, th")) {
|
||||
if (options.isCurrent && this.$target.is("td, th")) {
|
||||
// ... and that the td or th element was alone on its row ...
|
||||
if (this.$target.siblings().length === 1) {
|
||||
var $tr = $clone.parent();
|
||||
|
||||
@@ -46,6 +46,11 @@
|
||||
background-color: @o_mm_def_color_alpha;
|
||||
}
|
||||
|
||||
td {
|
||||
// Default browser style but needed so that alignment works on some mail
|
||||
// clients (see transcoder)
|
||||
text-align: inherit;
|
||||
}
|
||||
|
||||
// ===== Layout =====
|
||||
.o_layout {
|
||||
|
||||
@@ -312,20 +312,24 @@ class MrpWorkorder(models.Model):
|
||||
# If last work order, then post lots used
|
||||
# TODO: should be same as checking if for every workorder something has been done?
|
||||
if not self.next_work_order_id:
|
||||
production_move = self.production_id.move_finished_ids.filtered(lambda x: (x.product_id.id == self.production_id.product_id.id) and (x.state not in ('done', 'cancel')))
|
||||
if production_move.product_id.tracking != 'none':
|
||||
move_lot = production_move.move_lot_ids.filtered(lambda x: x.lot_id.id == self.final_lot_id.id)
|
||||
if move_lot:
|
||||
move_lot.quantity += self.qty_producing
|
||||
production_moves = self.production_id.move_finished_ids.filtered(lambda x: (x.state not in ('done', 'cancel')))
|
||||
for production_move in production_moves:
|
||||
if production_move.product_id.id == self.production_id.product_id.id and production_move.product_id.tracking != 'none':
|
||||
move_lot = production_move.move_lot_ids.filtered(lambda x: x.lot_id.id == self.final_lot_id.id)
|
||||
if move_lot:
|
||||
move_lot.quantity += self.qty_producing
|
||||
else:
|
||||
move_lot.create({'move_id': production_move.id,
|
||||
'lot_id': self.final_lot_id.id,
|
||||
'quantity': self.qty_producing,
|
||||
'quantity_done': self.qty_producing,
|
||||
'workorder_id': self.id,
|
||||
})
|
||||
elif production_move.unit_factor:
|
||||
rounding = production_move.product_uom.rounding
|
||||
production_move.quantity_done += float_round(self.qty_producing * production_move.unit_factor, precision_rounding=rounding)
|
||||
else:
|
||||
move_lot.create({'move_id': production_move.id,
|
||||
'lot_id': self.final_lot_id.id,
|
||||
'quantity': self.qty_producing,
|
||||
'quantity_done': self.qty_producing,
|
||||
'workorder_id': self.id,
|
||||
})
|
||||
else:
|
||||
production_move.quantity_done += self.qty_producing # TODO: UoM conversion?
|
||||
production_move.quantity_done += self.qty_producing # TODO: UoM conversion?
|
||||
# Update workorder quantity produced
|
||||
self.qty_produced += self.qty_producing
|
||||
|
||||
|
||||
@@ -5,3 +5,4 @@ import models
|
||||
import controllers
|
||||
import report
|
||||
import wizard
|
||||
import tests
|
||||
|
||||
@@ -215,6 +215,41 @@ class PosOrder(models.Model):
|
||||
have_to_group_by = session and session.config_id.group_by or False
|
||||
rounding_method = session and session.config_id.company_id.tax_calculation_rounding_method
|
||||
|
||||
def add_anglosaxon_lines(grouped_data):
|
||||
Product = self.env['product.product']
|
||||
Analytic = self.env['account.analytic.account']
|
||||
for product_key in list(grouped_data.keys()):
|
||||
if product_key[0] == "product":
|
||||
line = grouped_data[product_key][0]
|
||||
product = Product.browse(line['product_id'])
|
||||
# In the SO part, the entries will be inverted by function compute_invoice_totals
|
||||
price_unit = - product._get_anglo_saxon_price_unit()
|
||||
account_analytic = Analytic.browse(line.get('analytic_account_id'))
|
||||
res = Product._anglo_saxon_sale_move_lines(
|
||||
line['name'], product, product.uom_id, line['quantity'], price_unit,
|
||||
fiscal_position=order.fiscal_position_id,
|
||||
account_analytic=account_analytic)
|
||||
if res:
|
||||
line1, line2 = res
|
||||
line1 = Product._convert_prepared_anglosaxon_line(line1, order.partner_id)
|
||||
insert_data('counter_part', {
|
||||
'name': line1['name'],
|
||||
'account_id': line1['account_id'],
|
||||
'credit': line1['credit'] or 0.0,
|
||||
'debit': line1['debit'] or 0.0,
|
||||
'partner_id': line1['partner_id']
|
||||
|
||||
})
|
||||
|
||||
line2 = Product._convert_prepared_anglosaxon_line(line2, order.partner_id)
|
||||
insert_data('counter_part', {
|
||||
'name': line2['name'],
|
||||
'account_id': line2['account_id'],
|
||||
'credit': line2['credit'] or 0.0,
|
||||
'debit': line2['debit'] or 0.0,
|
||||
'partner_id': line2['partner_id']
|
||||
})
|
||||
|
||||
for order in self.filtered(lambda o: not o.account_move or o.state == 'paid'):
|
||||
current_company = order.sale_journal.company_id
|
||||
account_def = IrProperty.get(
|
||||
@@ -335,6 +370,9 @@ class PosOrder(models.Model):
|
||||
|
||||
order.write({'state': 'done', 'account_move': move.id})
|
||||
|
||||
if self and order.company_id.anglo_saxon_accounting:
|
||||
add_anglosaxon_lines(grouped_data)
|
||||
|
||||
all_lines = []
|
||||
for group_key, group_data in grouped_data.iteritems():
|
||||
for value in group_data:
|
||||
|
||||
@@ -4,3 +4,4 @@
|
||||
import test_point_of_sale_flow
|
||||
import test_frontend
|
||||
import test_point_of_sale_ui
|
||||
import test_anglo_saxon
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import openerp
|
||||
import time
|
||||
from openerp import fields
|
||||
|
||||
from openerp.tests import common
|
||||
|
||||
|
||||
class TestAngloSaxonCommon(common.TransactionCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestAngloSaxonCommon, self).setUp()
|
||||
self.PosMakePayment = self.env['pos.make.payment']
|
||||
self.PosOrder = self.env['pos.order']
|
||||
self.Statement = self.env['account.bank.statement']
|
||||
self.company = self.env.ref('base.main_company')
|
||||
self.product = self.env.ref('product.product_product_3')
|
||||
self.partner = self.env.ref('base.res_partner_1')
|
||||
self.category = self.env.ref('product.product_category_1')
|
||||
self.category = self.category.copy({'name': 'New category','property_valuation': 'real_time'})
|
||||
account_type_rcv = self.env['account.account.type'].create({'name': 'RCV type', 'type': 'receivable'})
|
||||
account_type_oth = self.env['account.account.type'].create({'name': 'RCV type', 'type': 'other'})
|
||||
self.account = self.env['account.account'].create({'name': 'Receivable', 'code': 'RCV00' , 'user_type_id': account_type_rcv.id, 'reconcile': True})
|
||||
account_expense = self.env['account.account'].create({'name': 'Expense', 'code': 'EXP00' , 'user_type_id': account_type_oth.id, 'reconcile': True})
|
||||
account_output = self.env['account.account'].create({'name': 'Output', 'code': 'OUT00' , 'user_type_id': account_type_oth.id, 'reconcile': True})
|
||||
self.partner.property_account_receivable_id = self.account
|
||||
self.category.property_account_income_categ_id = self.account
|
||||
self.category.property_account_expense_categ_id = account_expense
|
||||
self.category.property_stock_account_input_categ_id = self.account
|
||||
self.category.property_stock_account_output_categ_id = account_output
|
||||
self.category.property_stock_valuation_account_id = self.account
|
||||
self.category.property_stock_journal = self.env['account.journal'].create({'name': 'Stock journal', 'type': 'sale', 'code': 'STK00'})
|
||||
self.pos_config = self.env.ref('point_of_sale.pos_config_main')
|
||||
self.pos_config = self.pos_config.copy({'name': 'New POS config'})
|
||||
self.product = self.product.copy({'name': 'New product','standard_price': 100})
|
||||
self.company.anglo_saxon_accounting = True
|
||||
self.product.categ_id = self.category
|
||||
sale_journal = self.env['account.journal'].create({'name': 'POS journal', 'type': 'sale', 'code': 'POS00'})
|
||||
self.pos_config.journal_id = sale_journal
|
||||
self.cash_journal = self.env['account.journal'].create({'name': 'CASH journal', 'type': 'cash', 'code': 'CSH00'})
|
||||
|
||||
|
||||
class TestAngloSaxonFlow(TestAngloSaxonCommon):
|
||||
|
||||
def test_create_account_move_line(self):
|
||||
# This test will check that the correct journal entries are created when a product in real time valuation
|
||||
# is sold in a company using anglo-saxon
|
||||
self.pos_config.open_session_cb()
|
||||
self.pos_config.current_session_id.write({'journal_ids': [(6, 0, [self.cash_journal.id])]})
|
||||
self.cash_journal.loss_account_id = self.account
|
||||
self.pos_statement = self.Statement.create({
|
||||
'balance_start': 0.0,
|
||||
'balance_end_real': 0.0,
|
||||
'date': time.strftime('%Y-%m-%d'),
|
||||
'journal_id': self.cash_journal.id,
|
||||
'company_id': self.company.id,
|
||||
'name': 'pos session test',
|
||||
})
|
||||
self.pos_config.current_session_id.write({'statement_ids': [(6, 0, [self.pos_statement.id])]})
|
||||
|
||||
# I create a PoS order with 1 unit of New product at 450 EUR
|
||||
self.pos_order_pos0 = self.PosOrder.create({
|
||||
'company_id': self.company.id,
|
||||
'partner_id': self.partner.id,
|
||||
'pricelist_id': self.company.partner_id.property_product_pricelist.id,
|
||||
'session_id': self.pos_config.current_session_id.id,
|
||||
'lines': [(0, 0, {
|
||||
'name': "OL/0001",
|
||||
'product_id': self.product.id,
|
||||
'price_unit': 450,
|
||||
'discount': 0.0,
|
||||
'qty': 1.0
|
||||
})]
|
||||
})
|
||||
|
||||
# I make a payment to fully pay the order
|
||||
context_make_payment = {"active_ids": [self.pos_order_pos0.id], "active_id": self.pos_order_pos0.id}
|
||||
self.pos_make_payment_0 = self.PosMakePayment.with_context(context_make_payment).create({
|
||||
'amount': 450.0,
|
||||
'journal_id': self.cash_journal.id,
|
||||
})
|
||||
|
||||
# I click on the validate button to register the payment.
|
||||
context_payment = {'active_id': self.pos_order_pos0.id}
|
||||
self.pos_make_payment_0.with_context(context_payment).check()
|
||||
|
||||
# I check that the order is marked as paid
|
||||
self.assertEqual(self.pos_order_pos0.state, 'paid', 'Order should be in paid state.')
|
||||
|
||||
# I close the current session to generate the journal entries
|
||||
self.pos_config.current_session_id.action_pos_session_close()
|
||||
|
||||
# I test that the generated journal entries are correct.
|
||||
account_output = self.category.property_stock_account_output_categ_id
|
||||
expense_account = self.category.property_account_expense_categ_id
|
||||
aml = self.pos_order_pos0.account_move.line_ids
|
||||
aml_output = aml.filtered(lambda l: l.account_id.id == account_output.id)
|
||||
aml_expense = aml.filtered(lambda l: l.account_id.id == expense_account.id)
|
||||
self.assertEqual(aml_output.credit, self.product.standard_price, "Cost of Good Sold entry missing or mismatching")
|
||||
self.assertEqual(aml_expense.debit, self.product.standard_price, "Cost of Good Sold entry missing or mismatching")
|
||||
@@ -388,7 +388,7 @@ class ProductTemplate(models.Model):
|
||||
for tmpl_id in self.with_context(active_test=False):
|
||||
# adding an attribute with only one value should not recreate product
|
||||
# write this attribute on every product to make sure we don't lose them
|
||||
variant_alone = tmpl_id.attribute_line_ids.filtered(lambda line: len(line.value_ids) == 1).mapped('value_ids')
|
||||
variant_alone = tmpl_id.attribute_line_ids.filtered(lambda line: line.attribute_id.create_variant and len(line.value_ids) == 1).mapped('value_ids')
|
||||
for value_id in variant_alone:
|
||||
updated_products = tmpl_id.product_variant_ids.filtered(lambda product: value_id.attribute_id not in product.mapped('attribute_value_ids.attribute_id'))
|
||||
updated_products.write({'attribute_value_ids': [(4, value_id.id)]})
|
||||
|
||||
@@ -4,10 +4,10 @@
|
||||
from . import common
|
||||
from odoo.tests.common import TransactionCase
|
||||
|
||||
class TestVariants(TransactionCase):
|
||||
class TestVariantsSearch(TransactionCase):
|
||||
|
||||
def setUp(self):
|
||||
res = super(TestVariants, self).setUp()
|
||||
res = super(TestVariantsSearch, self).setUp()
|
||||
self.size_attr = self.env['product.attribute'].create({'name': 'Size'})
|
||||
self.size_attr_value_s = self.env['product.attribute.value'].create({'name': 'S', 'attribute_id': self.size_attr.id})
|
||||
self.size_attr_value_m = self.env['product.attribute.value'].create({'name': 'M', 'attribute_id': self.size_attr.id})
|
||||
@@ -152,3 +152,181 @@ class TestVariants(common.TestProductCommon):
|
||||
'value_ids': [(4, self.size_attr_value_l.id)],
|
||||
})]
|
||||
})
|
||||
|
||||
|
||||
class TestVariantsNoCreate(common.TestProductCommon):
|
||||
|
||||
def setUp(self):
|
||||
super(TestVariantsNoCreate, self).setUp()
|
||||
self.size = self.env['product.attribute'].create({
|
||||
'name': 'Size',
|
||||
'create_variant': False,
|
||||
'value_ids': [(0, 0, {'name': 'S'}), (0, 0, {'name': 'M'}), (0, 0, {'name': 'L'})],
|
||||
})
|
||||
self.size_S = self.size.value_ids[0]
|
||||
self.size_M = self.size.value_ids[1]
|
||||
self.size_L = self.size.value_ids[2]
|
||||
|
||||
def test_create_mono(self):
|
||||
""" create a product with a 'nocreate' attribute with a single value """
|
||||
template = self.env['product.template'].create({
|
||||
'name': 'Sofa',
|
||||
'uom_id': self.uom_unit.id,
|
||||
'uom_po_id': self.uom_unit.id,
|
||||
'attribute_line_ids': [(0, 0, {
|
||||
'attribute_id': self.size.id,
|
||||
'value_ids': [(4, self.size_S.id)],
|
||||
})],
|
||||
})
|
||||
self.assertEqual(len(template.product_variant_ids), 1)
|
||||
self.assertFalse(template.product_variant_ids.attribute_value_ids)
|
||||
|
||||
def test_update_mono(self):
|
||||
""" modify a product with a 'nocreate' attribute with a single value """
|
||||
template = self.env['product.template'].create({
|
||||
'name': 'Sofa',
|
||||
'uom_id': self.uom_unit.id,
|
||||
'uom_po_id': self.uom_unit.id,
|
||||
})
|
||||
self.assertEqual(len(template.product_variant_ids), 1)
|
||||
|
||||
template.write({
|
||||
'attribute_line_ids': [(0, 0, {
|
||||
'attribute_id': self.size.id,
|
||||
'value_ids': [(4, self.size_S.id)],
|
||||
})],
|
||||
})
|
||||
self.assertEqual(len(template.product_variant_ids), 1)
|
||||
self.assertFalse(template.product_variant_ids.attribute_value_ids)
|
||||
|
||||
def test_create_multi(self):
|
||||
""" create a product with a 'nocreate' attribute with several values """
|
||||
template = self.env['product.template'].create({
|
||||
'name': 'Sofa',
|
||||
'uom_id': self.uom_unit.id,
|
||||
'uom_po_id': self.uom_unit.id,
|
||||
'attribute_line_ids': [(0, 0, {
|
||||
'attribute_id': self.size.id,
|
||||
'value_ids': [(6, 0, self.size.value_ids.ids)],
|
||||
})],
|
||||
})
|
||||
self.assertEqual(len(template.product_variant_ids), 1)
|
||||
self.assertFalse(template.product_variant_ids.attribute_value_ids)
|
||||
|
||||
def test_update_multi(self):
|
||||
""" modify a product with a 'nocreate' attribute with several values """
|
||||
template = self.env['product.template'].create({
|
||||
'name': 'Sofa',
|
||||
'uom_id': self.uom_unit.id,
|
||||
'uom_po_id': self.uom_unit.id,
|
||||
})
|
||||
self.assertEqual(len(template.product_variant_ids), 1)
|
||||
|
||||
template.write({
|
||||
'attribute_line_ids': [(0, 0, {
|
||||
'attribute_id': self.size.id,
|
||||
'value_ids': [(6, 0, self.size.value_ids.ids)],
|
||||
})],
|
||||
})
|
||||
self.assertEqual(len(template.product_variant_ids), 1)
|
||||
self.assertFalse(template.product_variant_ids.attribute_value_ids)
|
||||
|
||||
def test_create_mixed_mono(self):
|
||||
""" create a product with regular and 'nocreate' attributes """
|
||||
template = self.env['product.template'].create({
|
||||
'name': 'Sofa',
|
||||
'uom_id': self.uom_unit.id,
|
||||
'uom_po_id': self.uom_unit.id,
|
||||
'attribute_line_ids': [
|
||||
(0, 0, { # no variants for this one
|
||||
'attribute_id': self.size.id,
|
||||
'value_ids': [(4, self.size_S.id)],
|
||||
}),
|
||||
(0, 0, { # two variants for this one
|
||||
'attribute_id': self.prod_att_1.id,
|
||||
'value_ids': [(4, self.prod_attr1_v1.id), (4, self.prod_attr1_v2.id)],
|
||||
}),
|
||||
],
|
||||
})
|
||||
self.assertEqual(len(template.product_variant_ids), 2)
|
||||
self.assertEqual(
|
||||
{variant.attribute_value_ids for variant in template.product_variant_ids},
|
||||
{self.prod_attr1_v1, self.prod_attr1_v2},
|
||||
)
|
||||
|
||||
def test_update_mixed_mono(self):
|
||||
""" modify a product with regular and 'nocreate' attributes """
|
||||
template = self.env['product.template'].create({
|
||||
'name': 'Sofa',
|
||||
'uom_id': self.uom_unit.id,
|
||||
'uom_po_id': self.uom_unit.id,
|
||||
})
|
||||
self.assertEqual(len(template.product_variant_ids), 1)
|
||||
|
||||
template.write({
|
||||
'attribute_line_ids': [
|
||||
(0, 0, { # no variants for this one
|
||||
'attribute_id': self.size.id,
|
||||
'value_ids': [(4, self.size_S.id)],
|
||||
}),
|
||||
(0, 0, { # two variants for this one
|
||||
'attribute_id': self.prod_att_1.id,
|
||||
'value_ids': [(4, self.prod_attr1_v1.id), (4, self.prod_attr1_v2.id)],
|
||||
}),
|
||||
],
|
||||
})
|
||||
self.assertEqual(len(template.product_variant_ids), 2)
|
||||
self.assertEqual(
|
||||
{variant.attribute_value_ids for variant in template.product_variant_ids},
|
||||
{self.prod_attr1_v1, self.prod_attr1_v2},
|
||||
)
|
||||
|
||||
def test_create_mixed_multi(self):
|
||||
""" create a product with regular and 'nocreate' attributes """
|
||||
template = self.env['product.template'].create({
|
||||
'name': 'Sofa',
|
||||
'uom_id': self.uom_unit.id,
|
||||
'uom_po_id': self.uom_unit.id,
|
||||
'attribute_line_ids': [
|
||||
(0, 0, { # no variants for this one
|
||||
'attribute_id': self.size.id,
|
||||
'value_ids': [(6, 0, self.size.value_ids.ids)],
|
||||
}),
|
||||
(0, 0, { # two variants for this one
|
||||
'attribute_id': self.prod_att_1.id,
|
||||
'value_ids': [(4, self.prod_attr1_v1.id), (4, self.prod_attr1_v2.id)],
|
||||
}),
|
||||
],
|
||||
})
|
||||
self.assertEqual(len(template.product_variant_ids), 2)
|
||||
self.assertEqual(
|
||||
{variant.attribute_value_ids for variant in template.product_variant_ids},
|
||||
{self.prod_attr1_v1, self.prod_attr1_v2},
|
||||
)
|
||||
|
||||
def test_update_mixed_multi(self):
|
||||
""" modify a product with regular and 'nocreate' attributes """
|
||||
template = self.env['product.template'].create({
|
||||
'name': 'Sofa',
|
||||
'uom_id': self.uom_unit.id,
|
||||
'uom_po_id': self.uom_unit.id,
|
||||
})
|
||||
self.assertEqual(len(template.product_variant_ids), 1)
|
||||
|
||||
template.write({
|
||||
'attribute_line_ids': [
|
||||
(0, 0, { # no variants for this one
|
||||
'attribute_id': self.size.id,
|
||||
'value_ids': [(6, 0, self.size.value_ids.ids)],
|
||||
}),
|
||||
(0, 0, { # two variants for this one
|
||||
'attribute_id': self.prod_att_1.id,
|
||||
'value_ids': [(4, self.prod_attr1_v1.id), (4, self.prod_attr1_v2.id)],
|
||||
}),
|
||||
],
|
||||
})
|
||||
self.assertEqual(len(template.product_variant_ids), 2)
|
||||
self.assertEqual(
|
||||
{variant.attribute_value_ids for variant in template.product_variant_ids},
|
||||
{self.prod_attr1_v1, self.prod_attr1_v2},
|
||||
)
|
||||
|
||||
@@ -377,6 +377,7 @@ class InventoryLine(models.Model):
|
||||
def write(self, values):
|
||||
values.pop('product_name', False)
|
||||
res = super(InventoryLine, self).write(values)
|
||||
return res
|
||||
|
||||
@api.model
|
||||
def create(self, values):
|
||||
|
||||
@@ -591,7 +591,7 @@ class QuantPackage(models.Model):
|
||||
@api.one
|
||||
@api.depends('parent_id', 'children_ids')
|
||||
def _compute_ancestor_ids(self):
|
||||
self.ancestor_ids = self.env['stock.quant.package'].search(['id', 'parent_of', self.id]).ids
|
||||
self.ancestor_ids = self.env['stock.quant.package'].search([('id', 'parent_of', self.id)]).ids
|
||||
|
||||
@api.multi
|
||||
@api.depends('parent_id', 'children_ids', 'quant_ids.package_id')
|
||||
|
||||
@@ -28,54 +28,15 @@ class AccountInvoice(models.Model):
|
||||
"""
|
||||
inv = i_line.invoice_id
|
||||
company_currency = inv.company_id.currency_id
|
||||
price_unit = i_line._get_anglo_saxon_price_unit()
|
||||
if inv.currency_id != company_currency:
|
||||
currency_id = inv.currency_id.id
|
||||
amount_currency = i_line._get_price(company_currency, price_unit)
|
||||
else:
|
||||
currency_id = False
|
||||
amount_currency = False
|
||||
|
||||
if i_line.product_id.type == 'product' and i_line.product_id.valuation == 'real_time':
|
||||
fpos = i_line.invoice_id.fiscal_position_id
|
||||
accounts = i_line.product_id.product_tmpl_id.get_product_accounts(fiscal_pos=fpos)
|
||||
# debit account dacc will be the output account
|
||||
dacc = accounts['stock_output'].id
|
||||
# credit account cacc will be the expense account
|
||||
cacc = accounts['expense'].id
|
||||
if dacc and cacc:
|
||||
price_unit = i_line._get_anglo_saxon_price_unit()
|
||||
if inv.currency_id != company_currency:
|
||||
currency_id = inv.currency_id.id
|
||||
amount_currency = i_line._get_price(company_currency, price_unit)
|
||||
else:
|
||||
currency_id = False
|
||||
amount_currency = False
|
||||
return [
|
||||
{
|
||||
'type': 'src',
|
||||
'name': i_line.name[:64],
|
||||
'price_unit': price_unit,
|
||||
'quantity': i_line.quantity,
|
||||
'price': price_unit * i_line.quantity,
|
||||
'currency_id': currency_id,
|
||||
'amount_currency': amount_currency,
|
||||
'account_id':dacc,
|
||||
'product_id':i_line.product_id.id,
|
||||
'uom_id':i_line.uom_id.id,
|
||||
'account_analytic_id': i_line.account_analytic_id.id,
|
||||
'analytic_tag_ids': i_line.analytic_tag_ids.ids and [(6, 0, i_line.analytic_tag_ids.ids)] or False,
|
||||
},
|
||||
|
||||
{
|
||||
'type': 'src',
|
||||
'name': i_line.name[:64],
|
||||
'price_unit': price_unit,
|
||||
'quantity': i_line.quantity,
|
||||
'price': -1 * price_unit * i_line.quantity,
|
||||
'currency_id': currency_id,
|
||||
'amount_currency': -1 * amount_currency,
|
||||
'account_id':cacc,
|
||||
'product_id':i_line.product_id.id,
|
||||
'uom_id':i_line.uom_id.id,
|
||||
'account_analytic_id': i_line.account_analytic_id.id,
|
||||
'analytic_tag_ids': i_line.analytic_tag_ids.ids and [(6, 0, i_line.analytic_tag_ids.ids)] or False,
|
||||
},
|
||||
]
|
||||
return []
|
||||
return self.env['product.product']._anglo_saxon_sale_move_lines(i_line.name, i_line.product_id, i_line.uom_id, i_line.quantity, price_unit, currency=currency_id, amount_currency=amount_currency, fiscal_position=inv.fiscal_position_id, account_analytic=i_line.account_analytic_id, analytic_tags=i_line.analytic_tag_ids)
|
||||
|
||||
|
||||
class AccountInvoiceLine(models.Model):
|
||||
@@ -83,11 +44,7 @@ class AccountInvoiceLine(models.Model):
|
||||
|
||||
def _get_anglo_saxon_price_unit(self):
|
||||
self.ensure_one()
|
||||
price = self.product_id.standard_price
|
||||
if not self.uom_id or self.product_id.uom_id == self.uom_id:
|
||||
return price
|
||||
else:
|
||||
return self.product_id.uom_id._compute_price(price, self.uom_id)
|
||||
return self.product_id._get_anglo_saxon_price_unit(uom=self.uom_id)
|
||||
|
||||
def _get_price(self, company_currency, price_unit):
|
||||
if self.invoice_id.currency_id.id != company_currency.id:
|
||||
|
||||
@@ -141,6 +141,70 @@ class ProductProduct(models.Model):
|
||||
self.write({'standard_price': new_price})
|
||||
return True
|
||||
|
||||
@api.model
|
||||
def _anglo_saxon_sale_move_lines(self, name, product, uom, qty, price_unit, currency=False, amount_currency=False, fiscal_position=False, account_analytic=False, analytic_tags=False):
|
||||
"""Prepare dicts describing new journal COGS journal items for a product sale.
|
||||
|
||||
Returns a dict that should be passed to `_convert_prepared_anglosaxon_line()` to
|
||||
obtain the creation value for the new journal items.
|
||||
|
||||
:param Model product: a product.product record of the product being sold
|
||||
:param Model uom: a product.uom record of the UoM of the sale line
|
||||
:param Integer qty: quantity of the product being sold
|
||||
:param Integer price_unit: unit price of the product being sold
|
||||
:param Model currency: a res.currency record from the order of the product being sold
|
||||
:param Interger amount_currency: unit price in the currency from the order of the product being sold
|
||||
:param Model fiscal_position: a account.fiscal.position record from the order of the product being sold
|
||||
:param Model account_analytic: a account.account.analytic record from the line of the product being sold
|
||||
"""
|
||||
|
||||
if product.type == 'product' and product.valuation == 'real_time':
|
||||
accounts = product.product_tmpl_id.get_product_accounts(fiscal_pos=fiscal_position)
|
||||
# debit account dacc will be the output account
|
||||
dacc = accounts['stock_output'].id
|
||||
# credit account cacc will be the expense account
|
||||
cacc = accounts['expense'].id
|
||||
if dacc and cacc:
|
||||
return [
|
||||
{
|
||||
'type': 'src',
|
||||
'name': name[:64],
|
||||
'price_unit': price_unit,
|
||||
'quantity': qty,
|
||||
'price': price_unit * qty,
|
||||
'currency_id': currency and currency.id,
|
||||
'amount_currency': amount_currency,
|
||||
'account_id': dacc,
|
||||
'product_id': product.id,
|
||||
'uom_id': uom.id,
|
||||
'account_analytic_id': account_analytic and account_analytic.id,
|
||||
'analytic_tag_ids': analytic_tags and analytic_tag.ids and [(6, 0, analytic_tag.ids)] or False,
|
||||
},
|
||||
|
||||
{
|
||||
'type': 'src',
|
||||
'name': name[:64],
|
||||
'price_unit': price_unit,
|
||||
'quantity': qty,
|
||||
'price': -1 * price_unit * qty,
|
||||
'currency_id': currency and currency.id,
|
||||
'amount_currency': -1 * amount_currency,
|
||||
'account_id': cacc,
|
||||
'product_id': product.id,
|
||||
'uom_id': uom.id,
|
||||
'account_analytic_id': account_analytic and account_analytic.id,
|
||||
'analytic_tag_ids': analytic_tags and analytic_tags.ids and [(6, 0, analytic_tags.ids)] or False,
|
||||
},
|
||||
]
|
||||
return []
|
||||
|
||||
@api.model
|
||||
def _get_anglo_saxon_price_unit(self, uom=False):
|
||||
price = self.standard_price
|
||||
if not uom or self.uom_id.id == uom.id:
|
||||
return price
|
||||
return self.uom_id._compute_price(self.uom_id.id, price, to_uom_id=uom.id)
|
||||
|
||||
|
||||
class ProductCategory(models.Model):
|
||||
_inherit = 'product.category'
|
||||
|
||||
@@ -1010,7 +1010,9 @@ data.Editor = Class.extend({
|
||||
this.$target.after($clone);
|
||||
this.buildingBlock.call_for_all_snippets($clone, function (editor, $snippet) {
|
||||
for (var i in editor.styles) {
|
||||
editor.styles[i].on_clone($snippet);
|
||||
editor.styles[i].on_clone($snippet, {
|
||||
isCurrent: ($snippet.is($clone)),
|
||||
});
|
||||
}
|
||||
});
|
||||
return false;
|
||||
|
||||
@@ -9,17 +9,12 @@ var getMatchedCSSRules = function (a) {
|
||||
var sheets = document.styleSheets;
|
||||
for(var i = sheets.length-1; i >= 0 ; i--) {
|
||||
var rules;
|
||||
if (sheets[i].hasOwnProperty('rules')) {
|
||||
rules = sheets[i].rules;
|
||||
} else {
|
||||
//try...catch because Firefox not able to enumerate document.styleSheets[].cssRules[] for cross-domain stylesheets.
|
||||
try {
|
||||
rules = sheets[i].cssRules;
|
||||
} catch(e) {
|
||||
console.warn("Can't read the css rules of: " + sheets[i].href, e);
|
||||
continue;
|
||||
}
|
||||
rules = sheets[i].cssRules;
|
||||
// try...catch because browser may not able to enumerate rules for cross-domain sheets
|
||||
try {
|
||||
rules = sheets[i].rules || sheets[i].cssRules;
|
||||
} catch (e) {
|
||||
console.warn("Can't read the css rules of: " + sheets[i].href, e);
|
||||
continue;
|
||||
}
|
||||
if (rules) {
|
||||
for(var r = rules.length-1; r >= 0; r--) {
|
||||
@@ -131,6 +126,20 @@ var getMatchedCSSRules = function (a) {
|
||||
delete style['text-decoration-style'];
|
||||
}
|
||||
|
||||
// text-align inheritance does not seem to get past <td> elements on some
|
||||
// mail clients
|
||||
if (style['text-align'] === 'inherit') {
|
||||
var $el = $(a).parent();
|
||||
do {
|
||||
var align = $el.css('text-align');
|
||||
if (_.indexOf(['left', 'right', 'center', 'justify'], align) >= 0) {
|
||||
style['text-align'] = align;
|
||||
break;
|
||||
}
|
||||
$el = $el.parent();
|
||||
} while (!$el.is('html'));
|
||||
}
|
||||
|
||||
return style;
|
||||
};
|
||||
|
||||
|
||||
@@ -540,16 +540,12 @@ var getCssSelectors = function (filter) {
|
||||
var sheets = document.styleSheets;
|
||||
for(var i = 0; i < sheets.length; i++) {
|
||||
var rules;
|
||||
if (sheets[i].rules) {
|
||||
rules = sheets[i].rules;
|
||||
} else {
|
||||
//try...catch because Firefox not able to enumerate document.styleSheets[].cssRules[] for cross-domain stylesheets.
|
||||
try {
|
||||
rules = sheets[i].cssRules;
|
||||
} catch(e) {
|
||||
console.warn("Can't read the css rules of: " + sheets[i].href, e);
|
||||
continue;
|
||||
}
|
||||
// try...catch because browser may not able to enumerate rules for cross-domain stylesheets
|
||||
try {
|
||||
rules = sheets[i].rules || sheets[i].cssRules;
|
||||
} catch(e) {
|
||||
console.warn("Can't read the css rules of: " + sheets[i].href, e);
|
||||
continue;
|
||||
}
|
||||
if (rules) {
|
||||
for(var r = 0; r < rules.length; r++) {
|
||||
|
||||
@@ -263,9 +263,13 @@ options.registry["margin-x"] = options.registry.marginAndResize.extend({
|
||||
|
||||
return this.grid;
|
||||
},
|
||||
on_clone: function ($clone) {
|
||||
var _class = $clone.attr("class").replace(/\s*(col-lg-offset-|col-md-offset-)([0-9-]+)/g, '');
|
||||
$clone.attr("class", _class);
|
||||
on_clone: function ($clone, options) {
|
||||
// Below condition is added to remove offset of target element only
|
||||
// and not its children to avoid design alteration of a container / block.
|
||||
if (options.isCurrent) {
|
||||
var _class = $clone.attr("class").replace(/\s*(col-lg-offset-|col-md-offset-)([0-9-]+)/g, '');
|
||||
$clone.attr("class", _class);
|
||||
}
|
||||
return false;
|
||||
},
|
||||
on_resize: function (compass, beginClass, current) {
|
||||
|
||||
@@ -890,7 +890,7 @@
|
||||
<a t-attf-href="#" class="mb8 input-group-addon js_add_cart_json hidden-xs">
|
||||
<i class="fa fa-minus"></i>
|
||||
</a>
|
||||
<input type="text" class="js_quantity form-control quantity" t-att-data-line-id="line.id" t-att-data-product-id="line.product_id.id" t-att-value="int(line.product_uom_qty)" />
|
||||
<input type="text" class="js_quantity form-control quantity" t-att-data-line-id="line.id" t-att-data-product-id="line.product_id.id" t-att-value="int(line.product_uom_qty) == line.product_uom_qty and int(line.product_uom_qty) or line.product_uom_qty" />
|
||||
<a t-attf-href="#" class="mb8 input-group-addon float_left js_add_cart_json hidden-xs">
|
||||
<i class="fa fa-plus"></i>
|
||||
</a>
|
||||
@@ -969,7 +969,7 @@
|
||||
<span t-esc="line.product_id.with_context(display_default_code=False).display_name" class="h6" />
|
||||
</a>
|
||||
<br />
|
||||
<small>Qty: <t t-esc="int(line.product_uom_qty)" /></small>
|
||||
<small>Qty: <t t-esc="int(line.product_uom_qty) == line.product_uom_qty and int(line.product_uom_qty) or line.product_uom_qty" /></small>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
@@ -1350,7 +1350,7 @@
|
||||
</div>
|
||||
</td>
|
||||
<td class='td-qty'>
|
||||
<div t-esc="line.product_uom_qty" />
|
||||
<div t-esc="int(line.product_uom_qty) == line.product_uom_qty and int(line.product_uom_qty) or line.product_uom_qty" />
|
||||
</td>
|
||||
<td class="text-center td-price">
|
||||
<span t-field="line.price_unit" style="white-space: nowrap;" t-options="{'widget': 'monetary','from_currency': website_sale_order.pricelist_id.currency_id,'display_currency': website.currency_id}" />
|
||||
@@ -1516,7 +1516,7 @@
|
||||
</td>
|
||||
<td class='td-qty'>
|
||||
<div id="quote_qty">
|
||||
<span t-field="line.product_uom_qty"/>
|
||||
<span t-esc="int(line.product_uom_qty) == line.product_uom_qty and int(line.product_uom_qty) or line.product_uom_qty"/>
|
||||
<span class='hidden-xs' t-field="line.product_uom"/>
|
||||
</div>
|
||||
</td>
|
||||
|
||||
@@ -449,7 +449,7 @@ class IrMailServer(models.Model):
|
||||
message['To'] = x_forge_to
|
||||
|
||||
# Do not actually send emails in testing mode!
|
||||
if getattr(threading.currentThread(), 'testing', False):
|
||||
if getattr(threading.currentThread(), 'testing', False) or self.env.registry.in_test_mode():
|
||||
_test_logger.info("skip sending email in test mode")
|
||||
return message['Message-Id']
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ def keep_query(*keep_params, **additional_params):
|
||||
if not keep_params and not additional_params:
|
||||
keep_params = ('*',)
|
||||
params = additional_params.copy()
|
||||
qs_keys = request.httprequest.args.keys()
|
||||
qs_keys = request.httprequest.args.keys() if request else []
|
||||
for keep_param in keep_params:
|
||||
for param in fnmatch.filter(qs_keys, keep_param):
|
||||
if param not in additional_params and param in qs_keys:
|
||||
|
||||
@@ -116,7 +116,8 @@ class MigrationManager(object):
|
||||
lst.sort()
|
||||
return lst
|
||||
|
||||
parsed_installed_version = parse_version(getattr(pkg, 'load_version', pkg.installed_version) or '')
|
||||
installed_version = getattr(pkg, 'load_version', pkg.installed_version) or ''
|
||||
parsed_installed_version = parse_version(installed_version)
|
||||
current_version = parse_version(convert_version(pkg.data['version']))
|
||||
|
||||
versions = _get_migration_versions(pkg)
|
||||
@@ -153,7 +154,7 @@ class MigrationManager(object):
|
||||
except AttributeError:
|
||||
_logger.error('module %(addon)s: Each %(stage)s-migration file must have a "migrate(cr, installed_version)" function' % strfmt)
|
||||
else:
|
||||
migrate(self.cr, pkg.installed_version)
|
||||
migrate(self.cr, installed_version)
|
||||
finally:
|
||||
if fp:
|
||||
fp.close()
|
||||
|
||||
Reference in New Issue
Block a user