[MERGE] forward port branch 9.0 up to af87c57

This commit is contained in:
Christophe Simonis
2016-09-09 11:49:48 +02:00
29 changed files with 267 additions and 93 deletions
+1 -1
View File
@@ -273,7 +273,7 @@
<field name="code" attrs="{'readonly': ['|',('type', 'in', ['bank','cash']),('sequence_id','!=',False)], 'required': [('type', 'not in', ['bank', 'cash'])]}"/> <field name="code" attrs="{'readonly': ['|',('type', 'in', ['bank','cash']),('sequence_id','!=',False)], 'required': [('type', 'not in', ['bank', 'cash'])]}"/>
<field name="refund_sequence" attrs="{'invisible': [('type', 'not in', ['sale', 'purchase'])]}"/> <field name="refund_sequence" attrs="{'invisible': [('type', 'not in', ['sale', 'purchase'])]}"/>
<field name="sequence_id" required="0" attrs="{'readonly': 1}" groups="base.group_no_one"/> <field name="sequence_id" required="0" attrs="{'readonly': 1}" groups="base.group_no_one"/>
<field name="refund_sequence_id" attrs="{'invisible': ['|',('type', 'not in', ['sale', 'purchase']), ('refund_sequence', '!=', True)]}" required="0" groups="base.group_no_one"/> <field name="refund_sequence_id" attrs="{'invisible': ['|',('type', 'not in', ['sale', 'purchase']), ('refund_sequence', '!=', True)], 'required': [('type', 'in', ['sale', 'purchase']), ('refund_sequence', '=', True)]}" groups="base.group_no_one"/>
</group> </group>
<group> <group>
<field name="default_debit_account_id" domain="[('deprecated', '=', False)]" /> <field name="default_debit_account_id" domain="[('deprecated', '=', False)]" />
+1 -1
View File
@@ -411,7 +411,7 @@ class AccountAssetDepreciationLine(models.Model):
current_currency = line.asset_id.currency_id current_currency = line.asset_id.currency_id
amount = current_currency.compute(line.amount, company_currency) amount = current_currency.compute(line.amount, company_currency)
sign = (line.asset_id.category_id.journal_id.type == 'purchase' or line.asset_id.category_id.journal_id.type == 'sale' and 1) or -1 sign = (line.asset_id.category_id.journal_id.type == 'purchase' or line.asset_id.category_id.journal_id.type == 'sale' and 1) or -1
asset_name = line.asset_id.name + ' (%s/%s)' % (line.sequence, line.asset_id.method_number) asset_name = line.asset_id.name + ' (%s/%s)' % (line.sequence, len(line.asset_id.depreciation_line_ids))
reference = line.asset_id.code reference = line.asset_id.code
journal_id = line.asset_id.category_id.journal_id.id journal_id = line.asset_id.category_id.journal_id.id
partner_id = line.asset_id.partner_id.id partner_id = line.asset_id.partner_id.id
@@ -51,15 +51,15 @@ class AccountInvoiceLine(models.Model):
@api.one @api.one
def asset_create(self): def asset_create(self):
if self.asset_category_id and self.asset_category_id.method_number > 1: if self.asset_category_id:
vals = { vals = {
'name': self.name, 'name': self.name,
'code': self.invoice_id.number or False, 'code': self.invoice_id.number or False,
'category_id': self.asset_category_id.id, 'category_id': self.asset_category_id.id,
'value': self.price_subtotal, 'value': self.price_subtotal_signed,
'partner_id': self.invoice_id.partner_id.id, 'partner_id': self.invoice_id.partner_id.id,
'company_id': self.invoice_id.company_id.id, 'company_id': self.invoice_id.company_id.id,
'currency_id': self.invoice_id.currency_id.id, 'currency_id': self.invoice_id.company_currency_id.id,
'date': self.asset_start_date or self.invoice_id.date_invoice, 'date': self.asset_start_date or self.invoice_id.date_invoice,
'invoice_id': self.invoice_id.id, 'invoice_id': self.invoice_id.id,
} }
+67 -28
View File
@@ -35,30 +35,57 @@ class ResCompany(models.Model):
class AccountPartialReconcileCashBasis(models.Model): class AccountPartialReconcileCashBasis(models.Model):
_inherit = 'account.partial.reconcile' _inherit = 'account.partial.reconcile'
def create_tax_cash_basis_entry(self, value_before_reconciliation): def _check_tax_exigible(self, line):
#Search in account_move if we have any taxes account move lines """ Function overwritten in account_tax_exigible to make sure we consider only the lines having tax_exigible set to False """
return False
def _get_tax_cash_basis_lines(self, value_before_reconciliation):
# Search in account_move if we have any taxes account move lines
tax_group = {} tax_group = {}
total_by_cash_basis_account = {} total_by_cash_basis_account = {}
for move in (self.debit_move_id.move_id, self.credit_move_id.move_id):
for line in move.line_ids:
if line.tax_line_id and line.tax_line_id.use_cash_basis:
#amount to write is the current cash_basis amount minus the one before the reconciliation
matched_percentage = value_before_reconciliation[move.id]
amount = (line.credit_cash_basis - line.debit_cash_basis) - (line.credit - line.debit) * matched_percentage
#group by line account
acc = line.account_id.id
if tax_group.get(acc, False):
tax_group[acc] += amount
else:
tax_group[acc] = amount
#Group by cash basis account
acc = line.tax_line_id.cash_basis_account.id
if total_by_cash_basis_account.get(acc, False):
total_by_cash_basis_account[acc] += amount
else:
total_by_cash_basis_account[acc] = amount
line_to_create = [] line_to_create = []
for k,v in tax_group.items(): move_date = self.debit_move_id.date
for move in (self.debit_move_id.move_id, self.credit_move_id.move_id):
if move_date < move.date:
move_date = move.date
for line in move.line_ids:
#TOCHECK: normal and cash basis taxes shoudn't be mixed together (on the same invoice line for example) as it will
#create reporting issues. Not sure of the behavior to implement in that case, though.
# amount to write is the current cash_basis amount minus the one before the reconciliation
matched_percentage = value_before_reconciliation[move.id]
amount = (line.credit_cash_basis - line.debit_cash_basis) - (line.credit - line.debit) * matched_percentage
if not self._check_tax_exigible(line):
if line.tax_line_id and line.tax_line_id.use_cash_basis:
# group by line account
acc = line.account_id.id
if tax_group.get(acc, False):
tax_group[acc] += amount
else:
tax_group[acc] = amount
# Group by cash basis account and tax
acc = line.tax_line_id.cash_basis_account.id
key = (acc, line.tax_line_id.id)
if key in total_by_cash_basis_account:
total_by_cash_basis_account[key] += amount
else:
total_by_cash_basis_account[key] = amount
if any([tax.use_cash_basis for tax in line.tax_ids]):
for tax in line.tax_ids:
line_to_create.append((0, 0, {
'name': '/',
'debit': line.debit_cash_basis - line.debit * matched_percentage,
'credit': line.credit_cash_basis - line.credit * matched_percentage,
'account_id': line.account_id.id,
'tax_ids': [(6, 0, [tax.id])],
}))
line_to_create.append((0, 0, {
'name': '/',
'credit': line.debit_cash_basis - line.debit * matched_percentage,
'debit': line.credit_cash_basis - line.credit * matched_percentage,
'account_id': line.account_id.id,
}))
for k, v in tax_group.items():
line_to_create.append((0, 0, { line_to_create.append((0, 0, {
'name': '/', 'name': '/',
'debit': v if v > 0 else 0.0, 'debit': v if v > 0 else 0.0,
@@ -66,27 +93,39 @@ class AccountPartialReconcileCashBasis(models.Model):
'account_id': k, 'account_id': k,
})) }))
#Create counterpart vals # Create counterpart vals
for k,v in total_by_cash_basis_account.items(): for key, v in total_by_cash_basis_account.items():
k, tax_id = key
line_to_create.append((0, 0, { line_to_create.append((0, 0, {
'name': '/', 'name': '/',
'debit': abs(v) if v < 0 else 0.0, 'debit': abs(v) if v < 0 else 0.0,
'credit': v if v > 0 else 0.0, 'credit': v if v > 0 else 0.0,
'account_id': k, 'account_id': k,
'tax_line_id': tax_id,
})) }))
return line_to_create, move_date
#Create move def create_tax_cash_basis_entry(self, value_before_reconciliation):
line_to_create, move_date = self._get_tax_cash_basis_lines(value_before_reconciliation)
if len(line_to_create) > 0: if len(line_to_create) > 0:
#Check if company_journal for cash basis is set if not, raise exception # Check if company_journal for cash basis is set if not, raise exception
if not self.company_id.tax_cash_basis_journal_id: if not self.company_id.tax_cash_basis_journal_id:
raise UserError(_('There is no tax cash basis journal defined ' \ raise UserError(_('There is no tax cash basis journal defined ' \
'for this company: "%s" \nConfigure it in Accounting/Configuration/Settings') % \ 'for this company: "%s" \nConfigure it in Accounting/Configuration/Settings') % \
(self.company_id.name)) (self.company_id.name))
move = self.env['account.move'].create({ move_vals = {
'journal_id': self.company_id.tax_cash_basis_journal_id.id, 'journal_id': self.company_id.tax_cash_basis_journal_id.id,
'line_ids': line_to_create, 'line_ids': line_to_create,
'tax_cash_basis_rec_id': self.id}) 'tax_cash_basis_rec_id': self.id
#post move }
# The move date should be the maximum date between payment and invoice (in case
# of payment in advance). However, we should make sure the move date is not
# recorded after the period lock date as the tax statement for this period is
# probably already sent to the estate.
if move_date > self.company_id.period_lock_date:
move_vals['date'] = move_date
move = self.env['account.move'].with_context(dont_create_taxes=True).create(move_vals)
# post move
move.post() move.post()
@api.model @api.model
+46
View File
@@ -0,0 +1,46 @@
import models
from openerp import SUPERUSER_ID
def _migration_script(cr, registry):
#1. set the tax_exigible at True on all account.move lines not related to a tax in cash basis
cr.execute('''UPDATE account_move_line SET tax_exigible='t' ''')
cr.execute('''SELECT id FROM account_tax WHERE use_cash_basis = 't' ''')
tax_ids = tuple([x[0] for x in cr.fetchall()])
if tax_ids:
cr.execute('''UPDATE account_move_line SET tax_exigible='f' WHERE id IN (SELECT id FROM account_move_line WHERE tax_line_id IN %s OR id IN (SELECT account_move_line_id FROM account_move_line_account_tax_rel WHERE account_tax_id IN %s))''', (tax_ids, tax_ids))
#2. Fix the moves already created by the tax case based feature
# create account.move.lines for tax_line_id and tax_ids that weren't correctly set/created at the time of reconciliation
existing_move = registry['account.move'].search(cr, SUPERUSER_ID, [('tax_cash_basis_rec_id', '!=', False)])
for m in registry['account.move'].browse(cr, SUPERUSER_ID, existing_move):
partial_rec = m.tax_cash_basis_rec_id
for move in (partial_rec.debit_move_id.move_id, partial_rec.credit_move_id.move_id):
matched_percentage = partial_rec.amount and (partial_rec.amount / move.amount) or 0.0
for l in move.line_ids:
if l.tax_exigible:
continue
move_to_fix = registry['account.move'].search(cr, SUPERUSER_ID, [('tax_cash_basis_rec_id', '=', partial_rec.id)])[0]
amount = (l.debit - l.credit) * matched_percentage
registry['account.move.line'].create(cr, SUPERUSER_ID, {
'name': '/',
'debit': amount > 0 and amount or 0.0,
'credit': amount < 0 and -amount or 0.0,
'account_id': l.account_id.id,
'tax_line_id': l.tax_line_id and l.tax_line_id.id or False,
'tax_ids': l.tax_ids and [(6, 0, l.tax_ids.ids)] or [],
'tax_exigible': True,
'move_id': move_to_fix,
}, apply_taxes=False, context={'check_move_validity': False})
registry['account.move.line'].create(cr, SUPERUSER_ID, {
'name': '/',
'debit': -amount > 0 and -amount or 0.0,
'credit': -amount < 0 and amount or 0.0,
'account_id': l.account_id.id,
'move_id': move_to_fix,
'tax_exigible': True,
}, context={'check_move_validity': False})
return
@@ -0,0 +1,17 @@
# -*- encoding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
{
'name': 'Tax exigible',
'version': '1.0',
'category': 'Accounting',
'description': """
This is a bugfix-module backporting 39d68ac and adding the concept of tax exigibility on journal items.
""",
'depends': ['account_tax_cash_basis'],
'data': [],
'test': [],
'demo': [],
'auto_install': True,
'installable': True,
'post_init_hook': '_migration_script',
}
@@ -0,0 +1 @@
import account_move
@@ -0,0 +1,35 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from openerp import fields, models, api
class AccountMoveLine(models.Model):
_inherit = "account.move.line"
tax_exigible = fields.Boolean(string='Appears in VAT report', default=True,
help="Technical field used to mark a tax line as exigible in the vat report or not (only exigible journal items are displayed). By default all new journal items are directly exigible, but with the module account_tax_cash_basis, some will become exigible only when the payment is recorded.")
@api.model
def create(self, vals, apply_taxes=True):
taxes = False
if vals.get('tax_line_id'):
taxes = [{'use_cash_basis': self.env['account.tax'].browse(vals['tax_line_id']).use_cash_basis}]
if vals.get('tax_ids'):
taxes = self.env['account.move.line'].resolve_2many_commands('tax_ids', vals['tax_ids'])
if taxes and any([tax['use_cash_basis'] for tax in taxes]) and not vals.get('tax_exigible'):
vals['tax_exigible'] = False
return super(AccountMoveLine, self).create(vals, apply_taxes=apply_taxes)
class AccountPartialReconcileCashBasis(models.Model):
_inherit = 'account.partial.reconcile'
def _check_tax_exigible(self, line):
return line.tax_exigible
def _get_tax_cash_basis_lines(self, value_before_reconciliation):
lines, move_date = super(AccountPartialReconcileCashBasis, self)._get_tax_cash_basis_lines(value_before_reconciliation)
for i in range(len(lines)):
vals = lines[i][2]
vals['tax_exigible'] = True
lines[i] = (0, 0, vals)
return lines, move_date
+2 -1
View File
@@ -8,6 +8,7 @@ from openerp.http import request
class link_tracker(http.Controller): class link_tracker(http.Controller):
@http.route('/r/<string:code>', type='http', auth='none', website=True) @http.route('/r/<string:code>', type='http', auth='none', website=True)
def full_url_redirect(self, code, **post): def full_url_redirect(self, code, **post):
request.env['link.tracker.click'].add_click(code, request.httprequest.remote_addr, request.session['geoip'].get('country_code'), stat_id=False) country_code = request.session.geoip and request.session.geoip.get('country_code') or False
request.env['link.tracker.click'].add_click(code, request.httprequest.remote_addr, country_code, stat_id=False)
redirect_url = request.env['link.tracker'].get_url_from_code(code) redirect_url = request.env['link.tracker'].get_url_from_code(code)
return werkzeug.utils.redirect(redirect_url or '', 301) return werkzeug.utils.redirect(redirect_url or '', 301)
+1 -1
View File
@@ -639,7 +639,7 @@ var chat_manager = {
// post a message in a channel // post a message in a channel
return ChannelModel.call('message_post', [options.channel_id], _.extend(msg, { return ChannelModel.call('message_post', [options.channel_id], _.extend(msg, {
message_type: 'comment', message_type: 'comment',
content_subtype: 'plaintext', content_subtype: 'html',
subtype: 'mail.mt_comment', subtype: 'mail.mt_comment',
})); }));
} }
+25
View File
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details. # Part of Odoo. See LICENSE file for full copyright and licensing details.
from openerp import api, _
from openerp.osv import fields, osv from openerp.osv import fields, osv
import openerp.addons.decimal_precision as dp import openerp.addons.decimal_precision as dp
@@ -104,3 +105,27 @@ class mrp_product_produce(osv.osv_memory):
self.pool.get('mrp.production').action_produce(cr, uid, production_id, self.pool.get('mrp.production').action_produce(cr, uid, production_id,
data.product_qty, data.mode, data, context=context) data.product_qty, data.mode, data, context=context)
return {} return {}
@api.onchange('consume_lines')
def _onchange_consume_lines(self):
'''
The purpose of the method is to warn the user if we plan to consume more than one unit of
a product with unique serial number.
'''
for product in self.consume_lines.mapped('product_id'):
if product.tracking != 'serial':
continue
qty_by_lot = {}
lines = self.consume_lines.filtered(lambda r: r.product_id == product)
for line in lines:
qty_by_lot.setdefault(line.lot_id, 0.0)
qty_by_lot[line.lot_id] += line.product_qty
if qty_by_lot[line.lot_id] > 1.0:
warning_mess = {
'title': _('Issue with lot quantity!'),
'message' : _('You plan to consume more than 1.00 unit of product %s with unique lot number %s') % \
(product.name, line.lot_id.name)
}
return {'warning': warning_mess}
+3 -3
View File
@@ -49,7 +49,7 @@
<field name="open"/> <field name="open"/>
<field name="memo"/> <field name="memo"/>
<field name="date_done"/> <field name="date_done"/>
<field name="message_follower_ids"/> <field name="message_partner_ids"/>
<field name="tag_ids"/> <field name="tag_ids"/>
<templates> <templates>
<t t-name="kanban-box"> <t t-name="kanban-box">
@@ -76,9 +76,9 @@
<!-- title --> <!-- title -->
<field name="name"/> <field name="name"/>
</span> </span>
<t t-if="record.message_follower_ids.raw_value.length &gt; 1"> <t t-if="record.message_partner_ids.raw_value.length &gt; 1">
<div class="clearfix"></div> <div class="clearfix"></div>
<t t-foreach="record.message_follower_ids.raw_value" t-as="follower"> <t t-foreach="record.message_partner_ids.raw_value" t-as="follower">
<img t-att-src="kanban_image('res.partner', 'image_small', follower)" width="24" height="24" class="oe_kanban_avatar pull-right" t-att-data-member_id="follower"/> <img t-att-src="kanban_image('res.partner', 'image_small', follower)" width="24" height="24" class="oe_kanban_avatar pull-right" t-att-data-member_id="follower"/>
</t> </t>
<div class="clearfix"></div> <div class="clearfix"></div>
+3 -3
View File
@@ -36,9 +36,9 @@ class AcquirerAdyen(osv.Model):
return providers return providers
_columns = { _columns = {
'adyen_merchant_account': fields.char('Merchant Account', required_if_provider='adyen'), 'adyen_merchant_account': fields.char('Merchant Account', required_if_provider='adyen', groups='base.group_user'),
'adyen_skin_code': fields.char('Skin Code', required_if_provider='adyen'), 'adyen_skin_code': fields.char('Skin Code', required_if_provider='adyen', groups='base.group_user'),
'adyen_skin_hmac_key': fields.char('Skin HMAC Key', required_if_provider='adyen'), 'adyen_skin_hmac_key': fields.char('Skin HMAC Key', required_if_provider='adyen', groups='base.group_user'),
} }
def _adyen_generate_merchant_sig_sha256(self, acquirer, inout, values): def _adyen_generate_merchant_sig_sha256(self, acquirer, inout, values):
+2 -2
View File
@@ -30,8 +30,8 @@ class PaymentAcquirerAuthorize(models.Model):
providers.append(['authorize', 'Authorize.Net']) providers.append(['authorize', 'Authorize.Net'])
return providers return providers
authorize_login = fields.Char(string='API Login Id', required_if_provider='authorize') authorize_login = fields.Char(string='API Login Id', required_if_provider='authorize', groups='base.group_user')
authorize_transaction_key = fields.Char(string='API Transaction Key', required_if_provider='authorize') authorize_transaction_key = fields.Char(string='API Transaction Key', required_if_provider='authorize', groups='base.group_user')
def _authorize_generate_hashing(self, values): def _authorize_generate_hashing(self, values):
data = '^'.join([ data = '^'.join([
+2 -2
View File
@@ -44,8 +44,8 @@ class AcquirerBuckaroo(osv.Model):
return providers return providers
_columns = { _columns = {
'brq_websitekey': fields.char('WebsiteKey', required_if_provider='buckaroo'), 'brq_websitekey': fields.char('WebsiteKey', required_if_provider='buckaroo', groups='base.group_user'),
'brq_secretkey': fields.char('SecretKey', required_if_provider='buckaroo'), 'brq_secretkey': fields.char('SecretKey', required_if_provider='buckaroo', groups='base.group_user'),
} }
def _buckaroo_generate_digital_sign(self, acquirer, inout, values): def _buckaroo_generate_digital_sign(self, acquirer, inout, values):
+5 -5
View File
@@ -47,11 +47,11 @@ class PaymentAcquirerOgone(osv.Model):
return providers return providers
_columns = { _columns = {
'ogone_pspid': fields.char('PSPID', required_if_provider='ogone'), 'ogone_pspid': fields.char('PSPID', required_if_provider='ogone', groups='base.group_user'),
'ogone_userid': fields.char('API User ID', required_if_provider='ogone'), 'ogone_userid': fields.char('API User ID', required_if_provider='ogone', groups='base.group_user'),
'ogone_password': fields.char('API User Password', required_if_provider='ogone'), 'ogone_password': fields.char('API User Password', required_if_provider='ogone', groups='base.group_user'),
'ogone_shakey_in': fields.char('SHA Key IN', size=32, required_if_provider='ogone'), 'ogone_shakey_in': fields.char('SHA Key IN', size=32, required_if_provider='ogone', groups='base.group_user'),
'ogone_shakey_out': fields.char('SHA Key OUT', size=32, required_if_provider='ogone'), 'ogone_shakey_out': fields.char('SHA Key OUT', size=32, required_if_provider='ogone', groups='base.group_user'),
'ogone_alias_usage': fields.char('Alias Usage', help="""If you want to use Ogone Aliases, 'ogone_alias_usage': fields.char('Alias Usage', help="""If you want to use Ogone Aliases,
this default Alias Usage will be presented to this default Alias Usage will be presented to
the customer as the reason you want to the customer as the reason you want to
+7 -7
View File
@@ -39,17 +39,17 @@ class AcquirerPaypal(osv.Model):
return providers return providers
_columns = { _columns = {
'paypal_email_account': fields.char('Paypal Email ID', required_if_provider='paypal'), 'paypal_email_account': fields.char('Paypal Email ID', required_if_provider='paypal', groups='base.group_user'),
'paypal_seller_account': fields.char( 'paypal_seller_account': fields.char(
'Paypal Merchant ID', 'Paypal Merchant ID', groups='base.group_user',
help='The Merchant ID is used to ensure communications coming from Paypal are valid and secured.'), help='The Merchant ID is used to ensure communications coming from Paypal are valid and secured.'),
'paypal_use_ipn': fields.boolean('Use IPN', help='Paypal Instant Payment Notification'), 'paypal_use_ipn': fields.boolean('Use IPN', help='Paypal Instant Payment Notification', groups='base.group_user'),
# Server 2 server # Server 2 server
'paypal_api_enabled': fields.boolean('Use Rest API'), 'paypal_api_enabled': fields.boolean('Use Rest API'),
'paypal_api_username': fields.char('Rest API Username'), 'paypal_api_username': fields.char('Rest API Username', groups='base.group_user'),
'paypal_api_password': fields.char('Rest API Password'), 'paypal_api_password': fields.char('Rest API Password', groups='base.group_user'),
'paypal_api_access_token': fields.char('Access Token'), 'paypal_api_access_token': fields.char('Access Token', groups='base.group_user'),
'paypal_api_access_token_validity': fields.datetime('Access Token Validity'), 'paypal_api_access_token_validity': fields.datetime('Access Token Validity', groups='base.group_user'),
} }
_defaults = { _defaults = {
+1 -1
View File
@@ -32,7 +32,7 @@ class SipsController(http.Controller):
sips = acquirer_obj.search([('provider', '=', 'sips')], limit=1) sips = acquirer_obj.search([('provider', '=', 'sips')], limit=1)
security = sips._sips_generate_shasign(post) security = sips.sudo()._sips_generate_shasign(post)
if security == post['Seal']: if security == post['Seal']:
_logger.debug('Sips: validated data') _logger.debug('Sips: validated data')
res = tx_obj.sudo().form_feedback(post, 'sips') res = tx_obj.sudo().form_feedback(post, 'sips')
+2 -2
View File
@@ -38,8 +38,8 @@ class AcquirerSips(models.Model):
_inherit = 'payment.acquirer' _inherit = 'payment.acquirer'
# Fields # Fields
sips_merchant_id = fields.Char('SIPS API User Password', sips_merchant_id = fields.Char('SIPS API User Password',
required_if_provider='sips') required_if_provider='sips', groups='base.group_user')
sips_secret = fields.Char('SIPS Secret', size=64, required_if_provider='sips') sips_secret = fields.Char('SIPS Secret', size=64, required_if_provider='sips', groups='base.group_user')
# Methods # Methods
def _get_sips_urls(self, environment): def _get_sips_urls(self, environment):
+9 -4
View File
@@ -1337,13 +1337,14 @@ exports.Orderline = Backbone.Model.extend({
}, },
compute_all: function(taxes, price_unit, quantity, currency_rounding) { compute_all: function(taxes, price_unit, quantity, currency_rounding) {
var self = this; var self = this;
var total_excluded = round_pr(price_unit * quantity, currency_rounding);
var total_included = total_excluded;
var base = total_excluded;
var list_taxes = []; var list_taxes = [];
var currency_rounding_bak = currency_rounding;
if (this.pos.company.tax_calculation_rounding_method == "round_globally"){ if (this.pos.company.tax_calculation_rounding_method == "round_globally"){
currency_rounding = currency_rounding * 0.00001; currency_rounding = currency_rounding * 0.00001;
} }
var total_excluded = round_pr(price_unit * quantity, currency_rounding);
var total_included = total_excluded;
var base = total_excluded;
_(taxes).each(function(tax) { _(taxes).each(function(tax) {
tax = self._map_tax_fiscal_position(tax); tax = self._map_tax_fiscal_position(tax);
if (tax.amount_type === 'group'){ if (tax.amount_type === 'group'){
@@ -1377,7 +1378,11 @@ exports.Orderline = Backbone.Model.extend({
} }
} }
}); });
return {taxes: list_taxes, total_excluded: total_excluded, total_included: total_included}; return {
taxes: list_taxes,
total_excluded: round_pr(total_excluded, currency_rounding_bak),
total_included: round_pr(total_included, currency_rounding_bak)
};
}, },
get_all_prices: function(){ get_all_prices: function(){
var price_unit = this.get_unit_price() * (1.0 - (this.get_discount() / 100.0)); var price_unit = this.get_unit_price() * (1.0 - (this.get_discount() / 100.0));
+1
View File
@@ -232,6 +232,7 @@ class sale_order_line(osv.osv):
result['warning'] = warning result['warning'] = warning
return result return result
@api.multi
@api.onchange('product_id') @api.onchange('product_id')
def product_id_change(self): def product_id_change(self):
warning = self.onchange_product_id_warning() warning = self.onchange_product_id_warning()
+1 -1
View File
@@ -98,7 +98,7 @@ def ensure_db(redirect='/web/database/selector'):
# If the db is taken out of a query parameter, it will be checked against # If the db is taken out of a query parameter, it will be checked against
# `http.db_filter()` in order to ensure it's legit and thus avoid db # `http.db_filter()` in order to ensure it's legit and thus avoid db
# forgering that could lead to xss attacks. # forgering that could lead to xss attacks.
db = request.params.get('db') db = request.params.get('db') and request.params.get('db').strip()
# Ensure db is legit # Ensure db is legit
if db and db not in http.db_filter([db]): if db and db not in http.db_filter([db]):
@@ -710,6 +710,13 @@ var DataSetSearch = DataSet.extend({
this._length -= (before - this.ids.length); this._length -= (before - this.ids.length);
} }
}, },
add_ids: function(ids, at) {
var before = this.ids.length;
this._super(ids, at);
if(this._length){
this._length += (this.ids.length - before);
}
},
unlink: function(ids, callback, error_callback) { unlink: function(ids, callback, error_callback) {
var self = this; var self = this;
return this._super(ids).done(function() { return this._super(ids).done(function() {
@@ -116,6 +116,7 @@ var KanbanColumn = Widget.extend({
} else { } else {
// adding record to this column // adding record to this column
self.records.push(record); self.records.push(record);
self.dataset.add_ids([record.id]);
record.setParent(self); record.setParent(self);
ui.item.addClass('o_updating'); ui.item.addClass('o_updating');
self.trigger_up('kanban_column_add_record', {record: record}); self.trigger_up('kanban_column_add_record', {record: record});
@@ -527,9 +527,7 @@ var KanbanView = View.extend({
active_ids: [event.target.id], active_ids: [event.target.id],
active_model: this.dataset.model, active_model: this.dataset.model,
}); });
this.do_execute_action(event.data, this.dataset, event.target.id).then(function () { this.do_execute_action(event.data, this.dataset, event.target.id, _.bind(self.reload_record, this, event.target));
self.reload_record(event.target);
});
}, },
/* /*
@@ -106,35 +106,30 @@ options.registry.slider = options.Class.extend({
},0); },0);
return $clone; return $clone;
}, },
remove_slide: function (type, value) { remove_slide: function (type) {
if(type !== "click") return; if (type !== "click" || this.remove_process) return;
if (this.remove_process) {
return;
}
var self = this; var self = this;
var new_index = 0;
var cycle = this.$inner.find('.item').length - 1; var $items = this.$inner.find('.item');
var index = this.$inner.find('.item.active').index(); var cycle = $items.length - 1;
var $active = $items.filter('.active');
var index = $active.index();
if (cycle > 0) { if (cycle > 0) {
this.remove_process = true; this.remove_process = true;
var $el = this.$inner.find('.item.active'); this.$target.on('slid.bs.carousel.slide_removal', function (event) {
self.$target.on('slid.bs.carousel', function (event) { $active.remove();
$el.remove();
self.$indicators.find("li:last").remove(); self.$indicators.find("li:last").remove();
self.$target.off('slid.bs.carousel'); self.$target.off('slid.bs.carousel.slide_removal');
self.rebind_event(); self.rebind_event();
self.remove_process = false; self.remove_process = false;
if (cycle == 1) { if (cycle === 1) {
self.on_remove_slide(event); self.$target.find('.carousel-control, .carousel-indicators').addClass("hidden");
} }
}); });
setTimeout(function () { _.defer(function () {
self.$target.carousel( index > 0 ? --index : cycle ); self.$target.carousel(index > 0 ? --index : cycle);
}, 500); });
} else {
this.$target.find('.carousel-control, .carousel-indicators').addClass("hidden");
} }
}, },
interval : function (type, value) { interval : function (type, value) {
+1 -1
View File
@@ -46,7 +46,7 @@ class website_payment(http.Controller):
partner_id = user.partner_id.id if user.partner_id.id != request.website.partner_id.id else False partner_id = user.partner_id.id if user.partner_id.id != request.website.partner_id.id else False
payment_form = acquirer.render(reference, float(amount), currency.id, values={'return_url': '/website_payment/confirm', 'partner_id': partner_id})[0] payment_form = acquirer.sudo().render(reference, float(amount), currency.id, values={'return_url': '/website_payment/confirm', 'partner_id': partner_id})[0]
values = { values = {
'reference': reference, 'reference': reference,
'acquirer': acquirer, 'acquirer': acquirer,
@@ -97,6 +97,9 @@ $('.oe_website_sale').each(function () {
$inputs.each(function () { $inputs.each(function () {
$(this).val( data[$(this).attr("name")] || "" ); $(this).val( data[$(this).attr("name")] || "" );
}); });
$selects.filter('[name="shipping_country_id"]').val(data['shipping_country_id']).change();
$selects.filter('[name="shipping_state_id"]').val(data['shipping_state_id']);
}); });
$(oe_website_sale).on("change", 'input[name="add_qty"]', function (event) { $(oe_website_sale).on("change", 'input[name="add_qty"]', function (event) {
@@ -332,7 +335,7 @@ $('.oe_website_sale').each(function () {
$('input.js_variant_change, select.js_variant_change', this).first().trigger('change'); $('input.js_variant_change, select.js_variant_change', this).first().trigger('change');
}); });
var state_options = $("select[name='state_id']:enabled option:not(:first)"); var state_options = $("select[name='state_id'] option:not(:first)");
$(oe_website_sale).on('change', "select[name='country_id']", function () { $(oe_website_sale).on('change', "select[name='country_id']", function () {
var select = $("select[name='state_id']"); var select = $("select[name='state_id']");
state_options.detach(); state_options.detach();
@@ -342,7 +345,7 @@ $('.oe_website_sale').each(function () {
}); });
$(oe_website_sale).find("select[name='country_id']").change(); $(oe_website_sale).find("select[name='country_id']").change();
var shipping_state_options = $("select[name='shipping_state_id']:enabled option:not(:first)"); var shipping_state_options = $("select[name='shipping_state_id'] option:not(:first)");
$(oe_website_sale).on('change', "select[name='shipping_country_id']", function () { $(oe_website_sale).on('change', "select[name='shipping_country_id']", function () {
var select = $("select[name='shipping_state_id']"); var select = $("select[name='shipping_state_id']");
shipping_state_options.detach(); shipping_state_options.detach();
+2 -2
View File
@@ -1460,7 +1460,7 @@ class BaseModel(object):
def _get_default_form_view(self, cr, user, context=None): def _get_default_form_view(self, cr, user, context=None):
""" Generates a default single-line form view using all fields """ Generates a default single-line form view using all fields
of the current model except the m2m and o2m ones. of the current model.
:param cr: database cursor :param cr: database cursor
:param int user: user id :param int user: user id
@@ -1471,7 +1471,7 @@ class BaseModel(object):
view = etree.Element('form', string=self._description) view = etree.Element('form', string=self._description)
group = etree.SubElement(view, 'group', col="4") group = etree.SubElement(view, 'group', col="4")
for fname, field in self._fields.iteritems(): for fname, field in self._fields.iteritems():
if field.automatic or field.type in ('one2many', 'many2many'): if field.automatic:
continue continue
etree.SubElement(group, 'field', name=fname) etree.SubElement(group, 'field', name=fname)