[MERGE] forward port branch saas-10 up to e1bd405

This commit is contained in:
Christophe Simonis
2016-09-09 15:14:39 +02:00
40 changed files with 709 additions and 101 deletions
+1 -1
View File
@@ -282,7 +282,7 @@
<field name="code"/> <field name="code"/>
<field name="refund_sequence" attrs="{'invisible': [('type', 'not in', ['sale', 'purchase'])]}" groups="base.group_no_one"/> <field name="refund_sequence" attrs="{'invisible': [('type', 'not in', ['sale', 'purchase'])]}" groups="base.group_no_one"/>
<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="{'required': [('refund_sequence', '=', True)], '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
@@ -441,7 +441,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 = (category_id.journal_id.type == 'purchase' or category_id.journal_id.type == 'sale' and 1) or -1 sign = (category_id.journal_id.type == 'purchase' or 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))
prec = self.env['decimal.precision'].precision_get('Account') prec = self.env['decimal.precision'].precision_get('Account')
move_line_1 = { move_line_1 = {
'name': asset_name, 'name': asset_name,
@@ -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
@@ -669,7 +669,7 @@ var chat_manager = {
// post a message in a channel or execute a command // post a message in a channel or execute a command
return ChannelModel.call(data.command ? 'execute_command' : 'message_post', [options.channel_id], _.extend(msg, { return ChannelModel.call(data.command ? 'execute_command' : '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',
command: data.command, command: data.command,
})); }));
+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
@@ -73,7 +73,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">
@@ -100,9 +100,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):
+1
View File
@@ -59,6 +59,7 @@ Main Features
'views/report_statement.xml', 'views/report_statement.xml',
'views/report_usersproduct.xml', 'views/report_usersproduct.xml',
'views/report_userlabel.xml', 'views/report_userlabel.xml',
'views/report_saledetails.xml',
'views/point_of_sale.xml', 'views/point_of_sale.xml',
'views/point_of_sale_dashboard.xml', 'views/point_of_sale_dashboard.xml',
], ],
+7
View File
@@ -26,3 +26,10 @@ class PosController(http.Controller):
} }
return request.render('point_of_sale.index', qcontext={'db_info': json.dumps(db_info)}) return request.render('point_of_sale.index', qcontext={'db_info': json.dumps(db_info)})
@http.route('/pos/sale_details_report', type='http', auth='user')
def print_sale_details(self, date_start=False, date_stop=False, **kw):
r = request.env['report.point_of_sale.report_saledetails']
pdf = request.env['report'].with_context(date_start=date_start, date_stop=date_stop).get_pdf(r, 'point_of_sale.report_saledetails')
pdfhttpheaders = [('Content-Type', 'application/pdf'), ('Content-Length', len(pdf))]
return request.make_response(pdf, headers=pdfhttpheaders)
+103 -1
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.
import logging import logging
from datetime import timedelta
from functools import partial from functools import partial
import psycopg2 import psycopg2
@@ -8,7 +9,7 @@ import psycopg2
from odoo import api, fields, models, tools, _ from odoo import api, fields, models, tools, _
from odoo.tools import float_is_zero from odoo.tools import float_is_zero
from odoo.exceptions import UserError from odoo.exceptions import UserError
from odoo.http import request
import odoo.addons.decimal_precision as dp import odoo.addons.decimal_precision as dp
_logger = logging.getLogger(__name__) _logger = logging.getLogger(__name__)
@@ -734,3 +735,104 @@ class PosOrderLine(models.Model):
def _get_tax_ids_after_fiscal_position(self): def _get_tax_ids_after_fiscal_position(self):
for line in self: for line in self:
line.tax_ids_after_fiscal_position = line.order_id.fiscal_position_id.map_tax(line.tax_ids) line.tax_ids_after_fiscal_position = line.order_id.fiscal_position_id.map_tax(line.tax_ids)
class ReportSaleDetails(models.AbstractModel):
_name = 'report.point_of_sale.report_saledetails'
@api.model
def get_sale_details(self, date_start=False, date_stop=False, company=False):
""" Serialise the orders of the day information
params: date_start, date_stop string representing the day of order
"""
if not company:
company = self.env.user.company_id
today = fields.Datetime.from_string(fields.Date.context_today(self))
if date_start:
date_start = fields.Datetime.from_string(date_start)
else:
# start by default today 00:00:00
date_start = today
if date_stop:
# set time to 23:59:59
date_stop = fields.Datetime.from_string(date_stop) + timedelta(days=1, seconds=-1)
else:
# stop by default today 23:59:59
date_stop = today + timedelta(days=1, seconds=-1)
# avoid a date_stop smaller than date_start
date_stop = max(date_stop, date_start)
date_start = fields.Datetime.to_string(date_start)
date_stop = fields.Datetime.to_string(date_stop)
orders = self.env['pos.order'].search([('date_order', '>=', date_start), ('date_order', '<=', date_stop),
('state', 'in', ['paid','invoiced','done']),
('company_id', '=', company.id)])
user_currency = self.env.user.company_id.currency_id
total = 0.0
products_sold = {}
taxes = {}
for order in orders:
if user_currency != order.pricelist_id.currency_id:
total += order.pricelist_id.currency_id.compute(order.amount_total, user_currency)
else:
total += order.amount_total
currency = order.session_id.currency_id
for line in order.lines:
key = (line.product_id, line.price_unit, line.discount)
products_sold.setdefault(key, 0.0)
products_sold[key] += line.qty
if line.tax_ids_after_fiscal_position:
line_taxes = line.tax_ids_after_fiscal_position.compute_all(line.price_unit * (1-(line.discount or 0.0)/100.0), currency, line.qty, product=line.product_id, partner=line.order_id.partner_id or False)
for tax in line_taxes['taxes']:
taxes.setdefault(tax['id'], {'name': tax['name'], 'total':0.0})
taxes[tax['id']]['total'] += tax['amount']
st_line_ids = self.env["account.bank.statement.line"].search([('pos_statement_id', 'in', orders.ids)]).ids
if st_line_ids:
self.env.cr.execute("""
SELECT aj.name, sum(amount) total
FROM account_bank_statement_line AS absl,
account_bank_statement AS abs,
account_journal AS aj
WHERE absl.statement_id = abs.id
AND abs.journal_id = aj.id
AND absl.id IN %s
GROUP BY aj.name
""", (tuple(st_line_ids),))
payments = self.env.cr.dictfetchall()
else:
payments = []
return {
'total_paid': user_currency.round(total),
'payments': payments,
'company_name': self.env.user.company_id.name,
'taxes': taxes.values(),
'products': [{
'product_id': product.id,
'product_name': product.name[:20],
'code': product.default_code,
'quantity': qty,
'price_unit': price_unit,
'discount': discount,
'uom': product.uom_id.name
} for (product, price_unit, discount), qty in products_sold.items()]
}
@api.multi
def render_html(self, data=None):
company = request.env.user.company_id
date_start = self.env.context.get('date_start', False)
date_stop = self.env.context.get('date_stop', False)
data = dict(data or {}, date_start=date_start, date_stop=date_stop)
data.update(self.get_sale_details(date_start, date_stop, company))
return self.env['report'].render('point_of_sale.report_saledetails', data)
@@ -406,6 +406,23 @@ var ProxyStatusWidget = StatusWidget.extend({
}, },
}); });
/* --------- The Sale Details --------- */
// Generates a report to print the sales of the
// day on a ticket
var SaleDetailsButton = PosBaseWidget.extend({
template: 'SaleDetailsButton',
start: function(){
var self = this;
this.$el.click(function(){
self.pos.proxy.print_sale_details();
});
},
});
/*--------------------------------------*\ /*--------------------------------------*\
| THE CHROME | | THE CHROME |
\*======================================*/ \*======================================*/
@@ -662,6 +679,11 @@ var Chrome = PosBaseWidget.extend({
'name': 'order_selector', 'name': 'order_selector',
'widget': OrderSelectorWidget, 'widget': OrderSelectorWidget,
'replace': '.placeholder-OrderSelectorWidget', 'replace': '.placeholder-OrderSelectorWidget',
},{
'name': 'sale_details',
'widget': SaleDetailsButton,
'append': '.pos-rightheader',
'condition': function(){ return this.pos.config.use_proxy; },
},{ },{
'name': 'proxy_status', 'name': 'proxy_status',
'widget': ProxyStatusWidget, 'widget': ProxyStatusWidget,
@@ -769,6 +791,7 @@ return {
HeaderButtonWidget: HeaderButtonWidget, HeaderButtonWidget: HeaderButtonWidget,
OrderSelectorWidget: OrderSelectorWidget, OrderSelectorWidget: OrderSelectorWidget,
ProxyStatusWidget: ProxyStatusWidget, ProxyStatusWidget: ProxyStatusWidget,
SaleDetailsButton: SaleDetailsButton,
StatusWidget: StatusWidget, StatusWidget: StatusWidget,
SynchNotificationWidget: SynchNotificationWidget, SynchNotificationWidget: SynchNotificationWidget,
UsernameWidget: UsernameWidget, UsernameWidget: UsernameWidget,
@@ -2,8 +2,10 @@ odoo.define('point_of_sale.devices', function (require) {
"use strict"; "use strict";
var core = require('web.core'); var core = require('web.core');
var Model = require('web.DataModel');
var Session = require('web.Session'); var Session = require('web.Session');
var QWeb = core.qweb;
var _t = core._t; var _t = core._t;
// the JobQueue schedules a sequence of 'jobs'. each job is // the JobQueue schedules a sequence of 'jobs'. each job is
@@ -434,6 +436,23 @@ var ProxyDevice = core.Class.extend(core.mixins.PropertiesMixin,{
send_printing_job(); send_printing_job();
}, },
print_sale_details: function() {
var self = this;
new Model('report.point_of_sale.report_saledetails').call('get_sale_details').then(function(result){
var env = {
company: self.pos.company,
pos: self.pos,
products: result.products,
payments: result.payments,
taxes: result.taxes,
total_paid: result.total_paid,
date: (new Date()).toLocaleString(),
};
var report = QWeb.render('SaleDetailsReport', env);
self.print_receipt(report);
})
},
// asks the proxy to log some information, as with the debug.log you can provide several arguments. // asks the proxy to log some information, as with the debug.log you can provide several arguments.
log: function(){ log: function(){
return this.message('log',{'arguments': _.toArray(arguments)}); return this.message('log',{'arguments': _.toArray(arguments)});
+9 -4
View File
@@ -1361,13 +1361,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'){
@@ -1401,7 +1402,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));
@@ -0,0 +1,63 @@
odoo.define('point_of_sale.report', function (require) {
"use strict";
var chat_manager = require('mail.chat_manager');
var composer = require('mail.composer');
var ChatThread = require('mail.ChatThread');
var utils = require('mail.utils');
var config = require('web.config');
var core = require('web.core');
var data = require('web.data');
var Dialog = require('web.Dialog');
var framework = require('web.framework');
var Model = require('web.Model');
var pyeval = require('web.pyeval');
var SearchView = require('web.SearchView');
var ActionManager = require('web.ActionManager');
var ControlPanelMixin = require('web.ControlPanelMixin');
var datepicker = require('web.datepicker');
var Widget = require('web.Widget');
var QWeb = core.qweb;
var _t = core._t;
// TODO replace in v10 with proper wizard
var PosDetailsWidget = Widget.extend(ControlPanelMixin, {
template: 'WizardSaleDetailsReport',
events: {
"click .js_generate": "renderReport",
},
renderElement: function() {
this._super();
var current_date = new moment();
this.dp_start = new datepicker.DateWidget(this);
this.dp_start.insertBefore(this.$el.find('.js_date_start'));
this.dp_start.set_value(current_date.format('YYYY-MM-DD'));
this.dp_stop = new datepicker.DateWidget(this);
this.dp_stop.insertAfter(this.$el.find('.js_date_stop'));
this.dp_stop.set_value(current_date.format('YYYY-MM-DD'));
},
renderReport: function() {
var start = this.dp_start.get_value();
var stop = this.dp_stop.get_value();
var url = '/pos/sale_details_report?date_start='+start+'&date_stop='+stop;
window.open(url, '_blank');
},
});
core.action_registry.add('report_pos_details', PosDetailsWidget);
return PosDetailsWidget;
});
@@ -38,7 +38,7 @@ var PosBaseWidget = Widget.extend({
var currency = (this.pos && this.pos.currency) ? this.pos.currency : {symbol:'$', position: 'after', rounding: 0.01, decimals: 2}; var currency = (this.pos && this.pos.currency) ? this.pos.currency : {symbol:'$', position: 'after', rounding: 0.01, decimals: 2};
var decimals = currency.decimals; var decimals = currency.decimals;
if (precision && (typeof this.pos.dp[precision]) !== undefined) { if (precision && this.pos.dp[precision] !== undefined) {
decimals = this.pos.dp[precision]; decimals = this.pos.dp[precision];
} }
+116
View File
@@ -84,6 +84,14 @@
</div> </div>
</t> </t>
<t t-name="SaleDetailsButton">
<div class="oe_status">
<div class="js_connected oe_icon">
<i class='fa fa-fw fa-print'></i>
</div>
</div>
</t>
<t t-name="HeaderButtonWidget"> <t t-name="HeaderButtonWidget">
<div class="header-button"> <div class="header-button">
<t t-esc="widget.label" /> <t t-esc="widget.label" />
@@ -839,6 +847,114 @@
</receipt> </receipt>
</t> </t>
<t t-name="SaleDetailsReport">
<receipt align='center' width='40' value-thousands-separator='' >
<t t-if='pos.company_logo_base64'>
<img t-att-src='pos.company_logo_base64' />
<br/>
</t>
<t t-if='!pos.company_logo_base64'>
<h1><t t-esc='company.name' /></h1>
<br/>
</t>
<br /><br />
<!-- Orderlines -->
<div class='orderlines' line-ratio='0.6'>
<t t-foreach='products' t-as='line'>
<line>
<left><t t-esc='line.product_name' /></left>
<right>
<value value-decimals='2' value-autoint='on'>
<t t-esc='line.quantity' />
</value>
<t t-if='line.uom !== "Unit(s)"'>
<t t-esc='line.uom' />
</t>
</right>
<right>
<value><t t-esc='line.price_unit' /></value>
</right>
</line>
<t t-if='line.discount !== 0'>
<line indent='1'><left>Discount: <t t-esc='line.discount' />%</left></line>
</t>
</t>
</div>
<br/>
<line><left>------------------------</left></line>
<br/>
<line>
<left>Payments:</left>
</line>
<line t-foreach='payments' t-as='payment'>
<left><t t-esc='payment.name' /></left>
<right>
<t t-esc='payment.total'/>
</right>
</line>
<br/>
<line><left>------------------------</left></line>
<br/>
<line>
<left>Taxes:</left>
</line>
<line t-foreach='taxes' t-as='taxe'>
<left><t t-esc='taxe.name' /></left>
<right>
<t t-esc='taxe.total'/>
</right>
</line>
<br/>
<line><left>------------------------</left></line>
<br/>
<line>
<left>Total:</left>
<right>
<t t-esc='total_paid'/>
</right>
</line>
<br/>
<div font='b'>
<div><t t-esc='date' /></div>
</div>
</receipt>
</t>
<t t-name="WizardSaleDetailsReport">
<div class="o_form_view"><div class="oe_form_sheetbg o_form_sheet_bg"><div class="oe_form_sheet oe_form_sheet_width o_form_sheet">
<div class="pos_details_dates">
<h3>Select Dates for the report</h3>
<table class="table-condensed">
<tr>
<td><strong>Start date</strong></td>
<td><div class="js_date_start"></div></td>
</tr>
<tr>
<td><strong>Stop date</strong></td>
<td><div class="js_date_stop"></div></td>
</tr>
</table>
<p>
<button class="js_generate">Generate report</button>
</p>
</div>
</div></div></div>
</t>
<t t-name="PopupWidget"> <t t-name="PopupWidget">
<div class="modal-dialog"> <div class="modal-dialog">
<div class="popup popup-alert"> <div class="popup popup-alert">
@@ -17,5 +17,11 @@
<field name="report_name">point_of_sale.report_invoice</field> <field name="report_name">point_of_sale.report_invoice</field>
</record> </record>
<record id="sale_details_report" model="ir.actions.report.xml">
<field name="name">Sale Details</field>
<field name="model">report.point_of_sale.details</field>
<field name="report_type">qweb-pdf</field>
<field name="report_name">point_of_sale.report_saledetails</field>
</record>
</odoo> </odoo>
@@ -3,6 +3,8 @@
<template id="assets_backend" name="hr assets" inherit_id="web.assets_backend"> <template id="assets_backend" name="hr assets" inherit_id="web.assets_backend">
<xpath expr="." position="inside"> <xpath expr="." position="inside">
<link rel="stylesheet" href="/point_of_sale/static/src/less/pos_dashboard.less"/> <link rel="stylesheet" href="/point_of_sale/static/src/less/pos_dashboard.less"/>
<script type="text/javascript" src="/point_of_sale/static/src/js/report.js"></script>
</xpath> </xpath>
</template> </template>
</odoo> </odoo>
@@ -68,5 +68,11 @@
<field name="context">{'group_by_no_leaf':1,'group_by':[]}</field> <field name="context">{'group_by_no_leaf':1,'group_by':[]}</field>
</record> </record>
<record id="action_report_pos_details" model="ir.actions.client">
<field name="name">Sale Details</field>
<field name="tag">report_pos_details</field>
</record>
<menuitem id="menu_report_pos_order_all" name="Orders" action="action_report_pos_order_all" parent="menu_point_rep" sequence="3"/> <menuitem id="menu_report_pos_order_all" name="Orders" action="action_report_pos_order_all" parent="menu_point_rep" sequence="3"/>
<menuitem id="menu_report_order_details" name="Sale Details" action="action_report_pos_details" parent="menu_point_rep" sequence="4"/>
</odoo> </odoo>
@@ -0,0 +1,84 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<template id="report_saledetails">
<t t-call="report.html_container">
<t t-call="report.internal_layout">
<div class="page">
<div class="text-center">
<h2>Sale Details</h2>
<strong><t t-esc="date_start"/> - <t t-esc="date_stop"/></strong>
</div>
<!-- Orderlines -->
<h3>Products</h3>
<table class="table table-condensed">
<thead><tr>
<th>Product</th>
<th>Quantity</th>
<th>Price Unit</th>
</tr></thead>
<tbody>
<tr t-foreach='products' t-as='line'>
<td><t t-esc="line['product_name']" /></td>
<td>
<t t-esc="line['quantity']" />
<t t-if='line["uom"] != "Unit(s)"'>
<t t-esc='line["uom"]' />
</t>
</td>
<td>
<t t-esc='line["price_unit"]' />
<t t-if='line["discount"] != 0'>
Disc: <t t-esc='line["discount"]' />%
</t>
</td>
</tr>
</tbody>
</table>
<br/>
<h3>Payments</h3>
<table class="table table-condensed">
<thead><tr>
<th>Name</th>
<th>Total</th>
</tr></thead>
<tbody>
<tr t-foreach='payments' t-as='payment'>
<td><t t-esc="payment['name']" /></td>
<td><t t-esc="payment['total']" /></td>
</tr>
</tbody>
</table>
<br/>
<h3>Taxes</h3>
<table class="table table-condensed">
<thead><tr>
<th>Name</th>
<th>Total</th>
</tr></thead>
<tbody>
<tr t-foreach='taxes' t-as='tax'>
<td><t t-esc="tax['name']" /></td>
<td><t t-esc="tax['total']" /></td>
</tr>
</tbody>
</table>
<br/>
<br/>
<strong>Total: <t t-esc='total_paid'/></strong>
</div>
</t>
</t>
</template>
</data>
</openerp>
+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});
@@ -529,9 +529,7 @@ var KanbanView = View.extend({
active_model: this.model, active_model: this.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) {
@@ -162,7 +157,7 @@ options.registry.carousel = options.registry.slider.extend({
}, },
start : function () { start : function () {
var self = this; var self = this;
this._super(); this._super.apply(this, arguments);
// set background and prepare to clean for save // set background and prepare to clean for save
var add_class = function (c) { var add_class = function (c) {
@@ -170,11 +165,17 @@ options.registry.carousel = options.registry.slider.extend({
return self._class || ""; return self._class || "";
}; };
this.$target.on('slid.bs.carousel', function () { this.$target.on('slid.bs.carousel', function () {
if(self.editor && self.editor.styles.background) {
self.editor.styles.background.$target = self.$target.find(".item.active");
self.editor.styles.background.set_active();
}
self.$target.carousel("pause"); self.$target.carousel("pause");
if (!self.editor) return;
_.each(["background", "background_position", "colorpicker"], function (opt_name) {
var s_option = self.editor.styles[opt_name];
if (!s_option) return;
s_option.$target = self.$target.find(".item.active");
s_option.set_active();
s_option.$target.trigger("snippet-option-change", [s_option]);
});
}); });
this.$target.trigger('slid.bs.carousel'); this.$target.trigger('slid.bs.carousel');
}, },
@@ -376,7 +377,7 @@ options.registry.marginAndResize = options.Class.extend({
this.$overlay.attr("class", overlay_class); this.$overlay.attr("class", overlay_class);
}, },
/* on_resize /* on_resize
* called when the box is resizing and the class change, before the cover_target * called when the box is resizing and the class change, before the cover_target
* @compass: resize direction : 'n', 's', 'e', 'w' * @compass: resize direction : 'n', 's', 'e', 'w'
+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
@@ -1462,7 +1462,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
@@ -1473,7 +1473,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)