summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--addons/account/models/account.py4
-rw-r--r--addons/account/models/account_bank_statement.py1
-rw-r--r--addons/account/models/account_invoice.py54
-rw-r--r--addons/account/tests/test_account_supplier_invoice.py62
-rw-r--r--addons/account/views/web_planner_data.xml2
-rw-r--r--addons/account_asset/models/account_asset.py4
-rw-r--r--addons/account_cancel/views/account_views.xml3
-rw-r--r--addons/base_action_rule/models/base_action_rule.py9
-rw-r--r--addons/calendar/models/calendar.py2
-rw-r--r--addons/hr_holidays/models/hr.py2
-rw-r--r--addons/point_of_sale/static/src/js/db.js4
-rw-r--r--addons/point_of_sale/static/src/js/screens.js2
-rw-r--r--addons/pos_cache/models/pos_cache.py7
-rw-r--r--addons/purchase_requisition/models/purchase_requisition.py26
-rw-r--r--addons/web/static/src/js/views/form_common.js1
-rw-r--r--addons/website/tests/test_views.py21
-rw-r--r--odoo/addons/base/ir/ir_ui_view.py15
-rw-r--r--odoo/addons/base/tests/test_views.py8
18 files changed, 191 insertions, 36 deletions
diff --git a/addons/account/models/account.py b/addons/account/models/account.py
index b9d1565c9b6..45273bfbca8 100644
--- a/addons/account/models/account.py
+++ b/addons/account/models/account.py
@@ -773,6 +773,9 @@ class AccountTax(models.Model):
else:
total_included += tax_amount
+ # Keep base amount used for the current tax
+ tax_base = base
+
if tax.include_base_amount:
base += tax_amount
@@ -780,6 +783,7 @@ class AccountTax(models.Model):
'id': tax.id,
'name': tax.with_context(**{'lang': partner.lang} if partner else {}).name,
'amount': tax_amount,
+ 'base': tax_base,
'sequence': tax.sequence,
'account_id': tax.account_id.id,
'refund_account_id': tax.refund_account_id.id,
diff --git a/addons/account/models/account_bank_statement.py b/addons/account/models/account_bank_statement.py
index b338cef1743..204f0161a85 100644
--- a/addons/account/models/account_bank_statement.py
+++ b/addons/account/models/account_bank_statement.py
@@ -366,6 +366,7 @@ class AccountBankStatementLine(models.Model):
journal_entry_ids = fields.One2many('account.move', 'statement_line_id', 'Journal Entries', copy=False, readonly=True)
amount_currency = fields.Monetary(help="The amount expressed in an optional other currency if it is a multi-currency entry.")
currency_id = fields.Many2one('res.currency', string='Currency', help="The optional other currency if it is a multi-currency entry.")
+ state = fields.Selection(related='statement_id.state' , string='Status', readonly=True)
move_name = fields.Char(string='Journal Entry Name', readonly=True,
default=False, copy=False,
help="Technical field holding the number given to the journal entry, automatically set when the statement line is reconciled then stored to set the same number again if the line is cancelled, set to draft and re-processed again.")
diff --git a/addons/account/models/account_invoice.py b/addons/account/models/account_invoice.py
index f23ce323019..4e8e5c9c0e4 100644
--- a/addons/account/models/account_invoice.py
+++ b/addons/account/models/account_invoice.py
@@ -12,6 +12,9 @@ from odoo.tools.misc import formatLang
from odoo.exceptions import UserError, RedirectWarning, ValidationError
import odoo.addons.decimal_precision as dp
+import logging
+
+_logger = logging.getLogger(__name__)
# mapping invoice type to journal type
TYPE2JOURNAL = {
@@ -592,6 +595,25 @@ class AccountInvoice(models.Model):
else:
return self.env.ref('account.invoice_form').id
+ def _prepare_tax_line_vals(self, line, tax):
+ """ Prepare values to create an account.invoice.tax line
+
+ The line parameter is an account.invoice.line, and the
+ tax parameter is the output of account.tax.compute_all().
+ """
+ vals = {
+ 'invoice_id': self.id,
+ 'name': tax['name'],
+ 'tax_id': tax['id'],
+ 'amount': tax['amount'],
+ 'base': tax['base'],
+ 'manual': False,
+ 'sequence': tax['sequence'],
+ 'account_analytic_id': tax['analytic'] and line.account_analytic_id.id or False,
+ 'account_id': self.type in ('out_invoice', 'in_invoice') and (tax['account_id'] or line.account_id.id) or (tax['refund_account_id'] or line.account_id.id),
+ }
+ return vals
+
@api.multi
def get_taxes_values(self):
tax_grouped = {}
@@ -599,16 +621,7 @@ class AccountInvoice(models.Model):
price_unit = line.price_unit * (1 - (line.discount or 0.0) / 100.0)
taxes = line.invoice_line_tax_ids.compute_all(price_unit, self.currency_id, line.quantity, line.product_id, self.partner_id)['taxes']
for tax in taxes:
- val = {
- 'invoice_id': self.id,
- 'name': tax['name'],
- 'tax_id': tax['id'],
- 'amount': tax['amount'],
- 'manual': False,
- 'sequence': tax['sequence'],
- 'account_analytic_id': tax['analytic'] and line.account_analytic_id.id or False,
- 'account_id': self.type in ('out_invoice', 'in_invoice') and (tax['account_id'] or line.account_id.id) or (tax['refund_account_id'] or line.account_id.id),
- }
+ val = self._prepare_tax_line_vals(line, tax)
# If the taxes generate moves on the same financial account as the invoice line,
# propagate the analytic account from the invoice line to the tax line.
@@ -623,6 +636,7 @@ class AccountInvoice(models.Model):
tax_grouped[key] = val
else:
tax_grouped[key]['amount'] += val['amount']
+ tax_grouped[key]['base'] += val['base']
return tax_grouped
@api.multi
@@ -1332,15 +1346,19 @@ class AccountInvoiceTax(models.Model):
_order = 'sequence'
def _compute_base_amount(self):
+ tax_grouped = {}
+ for invoice in self.mapped('invoice_id'):
+ tax_grouped[invoice.id] = invoice.get_taxes_values()
for tax in self:
- base = 0.0
- for line in tax.invoice_id.invoice_line_ids:
- if tax.tax_id in line.invoice_line_tax_ids:
- price_unit = line.price_unit * (1 - (line.discount or 0.0) / 100.0)
- base += (line.invoice_line_tax_ids - tax.tax_id).compute_all(
- price_unit, line.invoice_id.currency_id, line.quantity, line.product_id, line.invoice_id.partner_id
- )['base']
- tax.base = base
+ key = self.env['account.tax'].browse(tax.tax_id.id).get_grouping_key({
+ 'tax_id': tax.tax_id.id,
+ 'account_id': tax.account_id.id,
+ 'account_analytic_id': tax.account_analytic_id.id,
+ })
+ if tax.invoice_id and key in tax_grouped[tax.invoice_id.id]:
+ tax.base = tax_grouped[tax.invoice_id.id][key]['base']
+ else:
+ _logger.warning('Tax Base Amount not computable probably due to a change in an underlying tax (%s).', tax.tax_id.name)
invoice_id = fields.Many2one('account.invoice', string='Invoice', ondelete='cascade', index=True)
name = fields.Char(string='Tax Description', required=True)
diff --git a/addons/account/tests/test_account_supplier_invoice.py b/addons/account/tests/test_account_supplier_invoice.py
index 671cfb2dcb0..232bd39ddc7 100644
--- a/addons/account/tests/test_account_supplier_invoice.py
+++ b/addons/account/tests/test_account_supplier_invoice.py
@@ -41,3 +41,65 @@ class TestAccountSupplierInvoice(AccountingTestCase):
#I cancel the account move which is in posted state and verifies that it gives warning message
with self.assertRaises(Warning):
invoice.move_id.button_cancel()
+
+ def test_supplier_invoice2(self):
+ tax_fixed = self.env['account.tax'].create({
+ 'sequence': 10,
+ 'name': 'Tax 10.0 (Fixed)',
+ 'amount': 10.0,
+ 'amount_type': 'fixed',
+ 'include_base_amount': True,
+ })
+ tax_percent_included_base_incl = self.env['account.tax'].create({
+ 'sequence': 20,
+ 'name': 'Tax 50.0% (Percentage of Price Tax Included)',
+ 'amount': 50.0,
+ 'amount_type': 'division',
+ 'include_base_amount': True,
+ })
+ tax_percentage = self.env['account.tax'].create({
+ 'sequence': 30,
+ 'name': 'Tax 20.0% (Percentage of Price)',
+ 'amount': 20.0,
+ 'amount_type': 'percent',
+ 'include_base_amount': False,
+ })
+ analytic_account = self.env['account.analytic.account'].create({
+ 'name': 'test account',
+ })
+
+ # Should be changed by automatic on_change later
+ invoice_account = self.env['account.account'].search([('user_type_id', '=', self.env.ref('account.data_account_type_receivable').id)], limit=1).id
+ invoice_line_account = self.env['account.account'].search([('user_type_id', '=', self.env.ref('account.data_account_type_expenses').id)], limit=1).id
+
+ invoice = self.env['account.invoice'].create({'partner_id': self.env.ref('base.res_partner_2').id,
+ 'account_id': invoice_account,
+ 'type': 'in_invoice',
+ })
+
+ invoice_line = self.env['account.invoice.line'].create({'product_id': self.env.ref('product.product_product_4').id,
+ 'quantity': 5.0,
+ 'price_unit': 100.0,
+ 'invoice_id': invoice.id,
+ 'name': 'product that cost 100',
+ 'account_id': invoice_line_account,
+ 'invoice_line_tax_ids': [(6, 0, [tax_fixed.id, tax_percent_included_base_incl.id, tax_percentage.id])],
+ 'account_analytic_id': analytic_account.id,
+ })
+ invoice.compute_taxes()
+
+ # check that Initially supplier bill state is "Draft"
+ self.assertTrue((invoice.state == 'draft'), "Initially vendor bill state is Draft")
+
+ #change the state of invoice to open by clicking Validate button
+ invoice.signal_workflow('invoice_open')
+
+ # Check if amount and corresponded base is correct for all tax scenarios given on a computational base
+ # Keep in mind that tax amount can be changed by the user at any time before validating (based on the invoice and tax laws applicable)
+ invoice_tax = invoice.tax_line_ids.sorted(key=lambda r: r.sequence)
+ self.assertEquals(invoice_tax.mapped('amount'), [50.0, 550.0, 220.0])
+ self.assertEquals(invoice_tax.mapped('base'), [500.0, 550.0, 1100.0])
+
+ #I cancel the account move which is in posted state and verifies that it gives warning message
+ with self.assertRaises(Warning):
+ invoice.move_id.button_cancel()
diff --git a/addons/account/views/web_planner_data.xml b/addons/account/views/web_planner_data.xml
index ff796c2859b..de159a85cf0 100644
--- a/addons/account/views/web_planner_data.xml
+++ b/addons/account/views/web_planner_data.xml
@@ -447,7 +447,7 @@
<span class="fa fa-copy fa-4x"></span>
<h3><strong>Contracts &amp; Subscriptions</strong></h3>
<p>Manage time &amp; material contracts or fixed-price recurring subscriptions.</p>
- <a t-att-href="prepare_backend_url('base.open_module_tree', 'form', 'account_analytic_analysis')"
+ <a t-att-href="prepare_backend_url('base.open_module_tree', 'form', 'sale_contract')"
class="btn odoo_purple">
<span class="fa fa-arrow-circle-o-down"/> Install Now
</a>
diff --git a/addons/account_asset/models/account_asset.py b/addons/account_asset/models/account_asset.py
index fff246a112c..942c54dfdf1 100644
--- a/addons/account_asset/models/account_asset.py
+++ b/addons/account_asset/models/account_asset.py
@@ -153,7 +153,7 @@ class AccountAssetAsset(models.Model):
else:
if self.method == 'linear':
amount = amount_to_depr / (undone_dotation_number - len(posted_depreciation_line_ids))
- if self.prorata and self.category_id.type == 'purchase':
+ if self.prorata:
amount = amount_to_depr / self.method_number
if sequence == 1:
days = (self.company_id.compute_fiscalyear_dates(depreciation_date)['date_to'] - depreciation_date).days + 1
@@ -174,7 +174,7 @@ class AccountAssetAsset(models.Model):
while depreciation_date <= end_date:
depreciation_date = date(depreciation_date.year, depreciation_date.month, depreciation_date.day) + relativedelta(months=+self.method_period)
undone_dotation_number += 1
- if self.prorata and self.category_id.type == 'purchase':
+ if self.prorata:
undone_dotation_number += 1
return undone_dotation_number
diff --git a/addons/account_cancel/views/account_views.xml b/addons/account_cancel/views/account_views.xml
index 41e8371efe1..54478d06960 100644
--- a/addons/account_cancel/views/account_views.xml
+++ b/addons/account_cancel/views/account_views.xml
@@ -53,7 +53,8 @@
<attribute name="options">{'reload_on_button': true}</attribute>
</field>
<xpath expr="//field[@name='bank_account_id']" position="after">
- <button name="button_cancel_reconciliation" attrs="{'invisible': [('journal_entry_ids', '=', [])]}" string="Cancel" type="object" icon="fa-undo text-warning"/>
+ <field name="state" invisible="1"/>
+ <button name="button_cancel_reconciliation" attrs="{'invisible': ['|',('journal_entry_ids', '=', []), ('state', '=', 'confirm')]}" string="Cancel" type="object" icon="fa fa-undo text-warning"/>
</xpath>
</field>
</record>
diff --git a/addons/base_action_rule/models/base_action_rule.py b/addons/base_action_rule/models/base_action_rule.py
index 9c0151fba53..7f13bf9f3ef 100644
--- a/addons/base_action_rule/models/base_action_rule.py
+++ b/addons/base_action_rule/models/base_action_rule.py
@@ -198,7 +198,14 @@ class BaseActionRule(models.Model):
# subscribe followers
if self.act_followers and hasattr(records, 'message_subscribe'):
- records.message_subscribe(self.act_followers.ids)
+ followers = self.env['mail.followers'].sudo().search(
+ [('res_model', '=', records._name),
+ ('res_id', 'in', records.ids),
+ ('partner_id', 'in', self.act_followers.ids),
+ ]
+ )
+ if not len(followers) == len(self.act_followers):
+ records.message_subscribe(self.act_followers.ids)
# execute server actions
if self.server_action_ids:
diff --git a/addons/calendar/models/calendar.py b/addons/calendar/models/calendar.py
index 01b5a2c714d..24b0aa71b33 100644
--- a/addons/calendar/models/calendar.py
+++ b/addons/calendar/models/calendar.py
@@ -1372,7 +1372,7 @@ class Meeting(models.Model):
if not self._context.get('dont_notify'):
if len(meeting.alarm_ids) > 0 or values.get('alarm_ids'):
partners_to_notify = meeting.partner_ids.ids
- event_attendees_changes = attendees_create and attendees_create[real_ids[0]]
+ event_attendees_changes = attendees_create and real_ids and attendees_create[real_ids[0]]
if event_attendees_changes:
partners_to_notify.append(event_attendees_changes['removed_partners'].ids)
self.env['calendar.alarm_manager'].notify_next_alarm(partners_to_notify)
diff --git a/addons/hr_holidays/models/hr.py b/addons/hr_holidays/models/hr.py
index fdec5c0ad5e..fe52be16430 100644
--- a/addons/hr_holidays/models/hr.py
+++ b/addons/hr_holidays/models/hr.py
@@ -213,5 +213,5 @@ class Employee(models.Model):
('date_from', '<=', today_end),
('date_to', '>=', today_start),
('type', '=', 'remove')
- ], ['employee_id'])
+ ])
return [('id', 'in', holidays.mapped('employee_id').ids)]
diff --git a/addons/point_of_sale/static/src/js/db.js b/addons/point_of_sale/static/src/js/db.js
index 62bf848aa44..c2b3fad52f0 100644
--- a/addons/point_of_sale/static/src/js/db.js
+++ b/addons/point_of_sale/static/src/js/db.js
@@ -410,6 +410,10 @@ var PosDB = core.Class.extend({
}
}
+ // Only necessary when we store a new, validated order. Orders
+ // that where already stored should already have been removed.
+ this.remove_unpaid_order(order);
+
orders.push({id: order_id, data: order});
this.save('orders',orders);
return order_id;
diff --git a/addons/point_of_sale/static/src/js/screens.js b/addons/point_of_sale/static/src/js/screens.js
index 68476c9f5e8..164fd5f7040 100644
--- a/addons/point_of_sale/static/src/js/screens.js
+++ b/addons/point_of_sale/static/src/js/screens.js
@@ -1895,7 +1895,7 @@ var PaymentScreenWidget = ScreenWidget.extend({
}
// if the change is too large, it's probably an input error, make the user confirm.
- if (!force_validation && (order.get_total_with_tax() * 1000 < order.get_total_paid())) {
+ if (!force_validation && order.get_total_with_tax() > 0 && (order.get_total_with_tax() * 1000 < order.get_total_paid())) {
this.gui.show_popup('confirm',{
title: _t('Please Confirm Large Amount'),
body: _t('Are you sure that the customer wants to pay') +
diff --git a/addons/pos_cache/models/pos_cache.py b/addons/pos_cache/models/pos_cache.py
index c082a8cfe94..fb0fb20641e 100644
--- a/addons/pos_cache/models/pos_cache.py
+++ b/addons/pos_cache/models/pos_cache.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
-
+import base64
from ast import literal_eval
from odoo import models, fields, api
@@ -29,7 +29,7 @@ class pos_cache(models.Model):
prod_ctx = prod_ctx.sudo(self.compute_user_id.id)
res = prod_ctx.read(self.get_product_fields())
datas = {
- 'cache': cPickle.dumps(res),
+ 'cache': base64.encodestring(cPickle.dumps(res)),
}
self.write(datas)
@@ -49,7 +49,8 @@ class pos_cache(models.Model):
self.product_fields = str(fields)
self.refresh_cache()
- return cPickle.loads(self.cache)
+ cache = base64.decodestring(self.cache)
+ return cPickle.loads(cache)
class pos_config(models.Model):
diff --git a/addons/purchase_requisition/models/purchase_requisition.py b/addons/purchase_requisition/models/purchase_requisition.py
index 0731b4456d5..86ad0cc1ca4 100644
--- a/addons/purchase_requisition/models/purchase_requisition.py
+++ b/addons/purchase_requisition/models/purchase_requisition.py
@@ -194,16 +194,16 @@ class PurchaseOrder(models.Model):
taxes_ids = line.product_id.supplier_taxes_id.filtered(lambda tax: tax.company_id == requisition.company_id).ids
# Compute quantity and price_unit
- if requisition.type_id.quantity_copy != 'copy':
- product_qty = 0
- price_unit = line.price_unit
- elif line.product_uom_id != line.product_id.uom_po_id:
+ if line.product_uom_id != line.product_id.uom_po_id:
product_qty = line.product_uom_id._compute_quantity(line.product_qty, line.product_id.uom_po_id)
price_unit = line.product_uom_id._compute_price(line.price_unit, line.product_id.uom_po_id)
else:
product_qty = line.product_qty
price_unit = line.price_unit
+ if requisition.type_id.quantity_copy != 'copy':
+ product_qty = 0
+
# Compute price_unit in appropriate currency
if requisition.company_id.currency_id != currency:
price_unit = requisition.company_id.currency_id.compute(price_unit, currency)
@@ -258,6 +258,24 @@ class PurchaseOrder(models.Model):
return result
+class PurchaseOrderLine(models.Model):
+ _inherit = "purchase.order.line"
+
+ @api.onchange('product_qty', 'product_uom')
+ def _onchange_quantity(self):
+ res = super(PurchaseOrderLine, self)._onchange_quantity()
+ if self.order_id.requisition_id:
+ for line in self.order_id.requisition_id.line_ids:
+ if line.product_id == self.product_id:
+ if line.product_uom_id != self.product_uom:
+ self.price_unit = self.env['product.uom']._compute_price(
+ line.product_uom_id.id, line.price_unit, to_uom_id=self.product_uom.id)
+ else:
+ self.price_unit = line.price_unit
+ break
+ return res
+
+
class ProductTemplate(models.Model):
_inherit = 'product.template'
diff --git a/addons/web/static/src/js/views/form_common.js b/addons/web/static/src/js/views/form_common.js
index 82c3bc8c4a7..65e94e671eb 100644
--- a/addons/web/static/src/js/views/form_common.js
+++ b/addons/web/static/src/js/views/form_common.js
@@ -430,6 +430,7 @@ var FormWidget = Widget.extend(InvisibilityChangerMixin, {
this.process_modifiers();
this._super();
this.$el.addClass(this.node.attrs["class"] || "");
+ this.$el.attr('style', this.node.attrs.style);
},
destroy: function() {
$.fn.tooltip('destroy');
diff --git a/addons/website/tests/test_views.py b/addons/website/tests/test_views.py
index 5199b8b9f46..1d963c68026 100644
--- a/addons/website/tests/test_views.py
+++ b/addons/website/tests/test_views.py
@@ -139,6 +139,24 @@ class TestViewSaving(common.TransactionCase):
Company = self.env['res.company']
View = self.env['ir.ui.view']
+ # create a view with an xmlid, like the file import would
+ with self.env.norecompute():
+ self.view_id = View.create(self.cr, self.uid, {
+ 'name': "Test View",
+ 'type': 'qweb',
+ 'arch': ET.tostring(self.arch, encoding='utf-8').decode('utf-8')
+ })
+ imd = self.env['ir.model.data'].create({
+ 'module': 'website',
+ 'name': 'test_view',
+ 'model': 'ir.ui.view',
+ 'res_id': self.view_id,
+ })
+
+ # the xml_id of the view should not be flagged as 'noupdate'
+ self.assertEqual(View.browse(self.cr, self.uid, self.view_id).model_data_id, imd)
+ self.assertFalse(imd.noupdate)
+
replacement = ET.tostring(h.DIV(
h.H3("Column 2"),
h.UL(
@@ -149,6 +167,9 @@ class TestViewSaving(common.TransactionCase):
), encoding='utf-8')
self.view_id.save(value=replacement, xpath='/div/div[2]')
+ # the xml_id of the view should be flagged as 'noupdate'
+ self.assertTrue(imd.noupdate)
+
company = Company.browse(1)
self.assertEqual(company.name, "Acme Corporation")
self.assertEqual(company.phone, "+12 3456789")
diff --git a/odoo/addons/base/ir/ir_ui_view.py b/odoo/addons/base/ir/ir_ui_view.py
index fb86fcf2601..10bf7c627b4 100644
--- a/odoo/addons/base/ir/ir_ui_view.py
+++ b/odoo/addons/base/ir/ir_ui_view.py
@@ -229,10 +229,11 @@ actual arch.
for view, view_wo_lang in zip(self, self.with_context(lang=None)):
view_wo_lang.arch = view.arch_base
+ @api.depends('write_date')
def _compute_model_data_id(self):
- # get the last ir_model_data record corresponding to self
+ # get the first ir_model_data record corresponding to self
domain = [('model', '=', 'ir.ui.view'), ('res_id', 'in', self.ids)]
- for data in self.env['ir.model.data'].search_read(domain, ['res_id']):
+ for data in self.env['ir.model.data'].search_read(domain, ['res_id'], order='id desc'):
view = self.browse(data['res_id'])
view.model_data_id = data['id']
@@ -343,7 +344,15 @@ actual arch.
values['name'] = "%s %s" % (values.get('model'), values['type'])
self.clear_caches()
- return super(View, self).create(self._compute_defaults(values))
+
+ if 'install_mode_data' in self._context:
+ # the view is created from a data file by installing a module; delay
+ # the recomputation of field 'model_data_id' until its xmlid record
+ # is created
+ with self.env.norecompute():
+ return super(View, self).create(self._compute_defaults(values))
+ else:
+ return super(View, self).create(self._compute_defaults(values))
@api.multi
def write(self, vals):
diff --git a/odoo/addons/base/tests/test_views.py b/odoo/addons/base/tests/test_views.py
index 38f30308fab..adbabdc48ad 100644
--- a/odoo/addons/base/tests/test_views.py
+++ b/odoo/addons/base/tests/test_views.py
@@ -12,6 +12,14 @@ from odoo.tests import common
from odoo.tools import mute_logger
+class ViewXMLID(common.TransactionCase):
+ def test_model_data_id(self):
+ """ Check whether views know their xmlid record. """
+ view = self.env.ref('base.view_company_form')
+ self.assertTrue(view)
+ self.assertTrue(view.model_data_id)
+ self.assertEqual(view.model_data_id.complete_name, 'base.view_company_form')
+
class ViewCase(common.TransactionCase):
def setUp(self):
super(ViewCase, self).setUp()