[MERGE] forward port of branch 8.0 up to 86f98d5

This commit is contained in:
Christophe Simonis
2015-08-21 17:01:17 +02:00
25 changed files with 274 additions and 67 deletions
+2 -1
View File
@@ -542,7 +542,8 @@ class account_bank_statement_line(osv.osv):
('reconcile_id', '=', False), ('reconcile_id', '=', False),
('state', '=', 'valid'), ('state', '=', 'valid'),
('account_id.reconcile', '=', True), ('account_id.reconcile', '=', True),
('id', 'not in', excluded_ids)] ('id', 'not in', excluded_ids),
('partner_id', 'in', (False, st_line.partner_id.id))]
return domain return domain
def get_reconciliation_proposition(self, cr, uid, st_line, excluded_ids=None, context=None): def get_reconciliation_proposition(self, cr, uid, st_line, excluded_ids=None, context=None):
+1
View File
@@ -742,6 +742,7 @@ class account_invoice(models.Model):
else: else:
line['currency_id'] = False line['currency_id'] = False
line['amount_currency'] = False line['amount_currency'] = False
line['price'] = self.currency_id.round(line['price'])
line['ref'] = ref line['ref'] = ref
if self.type in ('out_invoice','in_refund'): if self.type in ('out_invoice','in_refund'):
total += line['price'] total += line['price']
@@ -691,6 +691,7 @@ class account_analytic_account(osv.osv):
'fiscal_position': fpos_id, 'fiscal_position': fpos_id,
'payment_term': partner_payment_term, 'payment_term': partner_payment_term,
'company_id': contract.company_id.id or False, 'company_id': contract.company_id.id or False,
'user_id': contract.manager_id.id or uid,
} }
return invoice return invoice
@@ -2,5 +2,7 @@ id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_account_analytic_plan,account.analytic.plan,model_account_analytic_plan,account.group_account_user,1,1,1,1 access_account_analytic_plan,account.analytic.plan,model_account_analytic_plan,account.group_account_user,1,1,1,1
access_account_analytic_plan_line,account.analytic.plan.line,model_account_analytic_plan_line,account.group_account_user,1,1,1,1 access_account_analytic_plan_line,account.analytic.plan.line,model_account_analytic_plan_line,account.group_account_user,1,1,1,1
access_account_analytic_plan_instance,account.analytic.plan.instance,model_account_analytic_plan_instance,account.group_account_user,1,1,1,1 access_account_analytic_plan_instance,account.analytic.plan.instance,model_account_analytic_plan_instance,account.group_account_user,1,1,1,1
access_account_analytic_plan_instance_group_user,account.analytic.plan.instance,model_account_analytic_plan_instance,base.group_user,1,0,0,0
access_account_analytic_plan_instance_line,account.analytic.plan.instance.line,model_account_analytic_plan_instance_line,account.group_account_user,1,1,1,1 access_account_analytic_plan_instance_line,account.analytic.plan.instance.line,model_account_analytic_plan_instance_line,account.group_account_user,1,1,1,1
access_account_analytic_plan_instance_line_group_user,account.analytic.plan.instance.line,model_account_analytic_plan_instance_line,base.group_user,1,0,0,0
access_account_analytic_plan_line_invoice,account.analytic.plan.line.invoice,model_account_analytic_plan_line,account.group_account_user,1,1,1,1 access_account_analytic_plan_line_invoice,account.analytic.plan.line.invoice,model_account_analytic_plan_line,account.group_account_user,1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_account_analytic_plan account.analytic.plan model_account_analytic_plan account.group_account_user 1 1 1 1
3 access_account_analytic_plan_line account.analytic.plan.line model_account_analytic_plan_line account.group_account_user 1 1 1 1
4 access_account_analytic_plan_instance account.analytic.plan.instance model_account_analytic_plan_instance account.group_account_user 1 1 1 1
5 access_account_analytic_plan_instance_group_user account.analytic.plan.instance model_account_analytic_plan_instance base.group_user 1 0 0 0
6 access_account_analytic_plan_instance_line account.analytic.plan.instance.line model_account_analytic_plan_instance_line account.group_account_user 1 1 1 1
7 access_account_analytic_plan_instance_line_group_user account.analytic.plan.instance.line model_account_analytic_plan_instance_line base.group_user 1 0 0 0
8 access_account_analytic_plan_line_invoice account.analytic.plan.line.invoice model_account_analytic_plan_line account.group_account_user 1 1 1 1
@@ -277,7 +277,10 @@ class account_analytic_line(osv.osv):
for (key_id, company_id, currency_id), analytic_lines in invoice_grouping.items(): for (key_id, company_id, currency_id), analytic_lines in invoice_grouping.items():
# key_id is an account.analytic.account # key_id is an account.analytic.account
partner = analytic_lines[0].account_id.partner_id # will be the same for every line account = analytic_lines[0].account_id
partner = account.partner_id # will be the same for every line
if (not partner) or not (currency_id):
raise UserError(_('Contract incomplete. Please fill in the Customer and Pricelist fields for %s.') % (account.name))
curr_invoice = self._prepare_cost_invoice(cr, uid, partner, company_id, currency_id, analytic_lines, context=context) curr_invoice = self._prepare_cost_invoice(cr, uid, partner, company_id, currency_id, analytic_lines, context=context)
invoice_context = dict(context, invoice_context = dict(context,
@@ -292,8 +295,6 @@ class account_analytic_line(osv.osv):
invoice_lines_grouping = {} invoice_lines_grouping = {}
for analytic_line in analytic_lines: for analytic_line in analytic_lines:
account = analytic_line.account_id account = analytic_line.account_id
if (not partner) or not (account.pricelist_id):
raise UserError(_('Contract incomplete. Please fill in the Customer and Pricelist fields for %s.') % (account.name))
if not analytic_line.to_invoice: if not analytic_line.to_invoice:
raise UserError(_('Trying to invoice non invoiceable line for %s.') % (analytic_line.product_id.name)) raise UserError(_('Trying to invoice non invoiceable line for %s.') % (analytic_line.product_id.name))
+14 -13
View File
@@ -1,9 +1,22 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<openerp> <openerp>
<data noupdate="1">
<!-- Those properties are in a noupdate section in the stock module -->
<!-- No need to create them if they have been deleted -->
<record id="stock.property_stock_account_input_categ" model="ir.property" forcecreate="False">
<field name="fields_id" search="[('model','=','product.category'),('name','=','property_stock_account_input_categ')]"/>
<field eval="'account.account,'+str(ref('chart2141_en'))" model="account.account" name="value"/>
<field eval="'product.category,'+str(ref('product.product_category_all'))" model="product.category" name="res_id"/>
</record>
<record id="stock.property_stock_account_output_categ" model="ir.property" forcecreate="False">
<field name="fields_id" search="[('model','=','product.category'),('name','=','property_stock_account_output_categ')]"/>
<field eval="'account.account,'+str(ref('chart1145_en'))" model="account.account" name="value"/>
<field eval="'product.category,'+str(ref('product.product_category_all'))" model="product.category" name="res_id"/>
</record>
</data>
<data> <data>
<!-- Chart template --> <!-- Chart template -->
<record id="ca_en_chart_template_en" model="account.chart.template"> <record id="ca_en_chart_template_en" model="account.chart.template">
<field name="name">Canada - Chart of Accounts for english-speaking provinces</field> <field name="name">Canada - Chart of Accounts for english-speaking provinces</field>
<field name="account_root_id" ref="chart0_en"/> <field name="account_root_id" ref="chart0_en"/>
@@ -16,18 +29,6 @@
<field name="currency_id" ref="base.CAD"/> <field name="currency_id" ref="base.CAD"/>
</record> </record>
<record id="stock.property_stock_account_input_categ" model="ir.property">
<field name="fields_id" search="[('model','=','product.category'),('name','=','property_stock_account_input_categ')]"/>
<field eval="'account.account,'+str(ref('chart2141_en'))" model="account.account" name="value"/>
<field eval="'product.category,'+str(ref('product.product_category_all'))" model="product.category" name="res_id"/>
</record>
<record id="stock.property_stock_account_output_categ" model="ir.property">
<field name="fields_id" search="[('model','=','product.category'),('name','=','property_stock_account_output_categ')]"/>
<field eval="'account.account,'+str(ref('chart1145_en'))" model="account.account" name="value"/>
<field eval="'product.category,'+str(ref('product.product_category_all'))" model="product.category" name="res_id"/>
</record>
<record id="property_stock_valuation_account_id_en" model="ir.property"> <record id="property_stock_valuation_account_id_en" model="ir.property">
<field name="name">property_stock_valuation_account_id</field> <field name="name">property_stock_valuation_account_id</field>
<field name="fields_id" search="[('model','=','product.category'),('name','=','property_stock_valuation_account_id')]"/> <field name="fields_id" search="[('model','=','product.category'),('name','=','property_stock_valuation_account_id')]"/>
+14 -12
View File
@@ -1,5 +1,19 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<openerp> <openerp>
<data noupdate="1">
<!-- Those properties are in a noupdate section in the stock module -->
<!-- No need to create them if they have been deleted -->
<record id="stock.property_stock_account_input_categ" model="ir.property" forcecreate="False">
<field name="fields_id" search="[('model','=','product.category'),('name','=','property_stock_account_input_categ')]"/>
<field eval="'account.account,'+str(ref('chart2171_fr'))" model="account.account" name="value"/>
<field eval="'product.category,'+str(ref('product.product_category_all'))" model="product.category" name="res_id"/>
</record>
<record id="stock.property_stock_account_output_categ" model="ir.property" forcecreate="False">
<field name="fields_id" search="[('model','=','product.category'),('name','=','property_stock_account_output_categ')]"/>
<field eval="'account.account,'+str(ref('chart1145_fr'))" model="account.account" name="value"/>
<field eval="'product.category,'+str(ref('product.product_category_all'))" model="product.category" name="res_id"/>
</record>
</data>
<data> <data>
<!-- Chart template --> <!-- Chart template -->
@@ -15,18 +29,6 @@
<field name="currency_id" ref="base.CAD"/> <field name="currency_id" ref="base.CAD"/>
</record> </record>
<record id="stock.property_stock_account_input_categ" model="ir.property">
<field name="fields_id" search="[('model','=','product.category'),('name','=','property_stock_account_input_categ')]"/>
<field eval="'account.account,'+str(ref('chart2171_fr'))" model="account.account" name="value"/>
<field eval="'product.category,'+str(ref('product.product_category_all'))" model="product.category" name="res_id"/>
</record>
<record id="stock.property_stock_account_output_categ" model="ir.property">
<field name="fields_id" search="[('model','=','product.category'),('name','=','property_stock_account_output_categ')]"/>
<field eval="'account.account,'+str(ref('chart1145_fr'))" model="account.account" name="value"/>
<field eval="'product.category,'+str(ref('product.product_category_all'))" model="product.category" name="res_id"/>
</record>
<record id="property_stock_valuation_account_id" model="ir.property"> <record id="property_stock_valuation_account_id" model="ir.property">
<field name="name">property_stock_valuation_account_id</field> <field name="name">property_stock_valuation_account_id</field>
<field name="fields_id" search="[('model','=','product.category'),('name','=','property_stock_valuation_account_id')]"/> <field name="fields_id" search="[('model','=','product.category'),('name','=','property_stock_valuation_account_id')]"/>
@@ -33,6 +33,7 @@ class TestMassMailing(osv.TransientModel):
'body_html': '', 'body_html': '',
'notification': True, 'notification': True,
'mailing_id': mailing.id, 'mailing_id': mailing.id,
'attachment_ids': [(4, attachment.id) for attachment in mailing.attachment_ids],
} }
mail_mail_obj = Mail.browse(cr, uid, Mail.create(cr, uid, mail_values, context=context), context=context) mail_mail_obj = Mail.browse(cr, uid, Mail.create(cr, uid, mail_values, context=context), context=context)
unsubscribe_url = Mail._get_unsubscribe_url(cr, uid, mail_mail_obj, test_mail, context=context) unsubscribe_url = Mail._get_unsubscribe_url(cr, uid, mail_mail_obj, test_mail, context=context)
@@ -0,0 +1,10 @@
# KEYBOARD CONFIGURATION FILE
# Consult the keyboard(5) manual page.
XKBMODEL="pc105"
XKBLAYOUT="us"
XKBVARIANT=""
XKBOPTIONS=""
BACKSPACE="guess"
@@ -31,7 +31,7 @@ apt-get -y autoremove
apt-get update apt-get update
apt-get -y dist-upgrade apt-get -y dist-upgrade
PKGS_TO_INSTALL="adduser postgresql-client python python-dateutil python-decorator python-docutils python-feedparser python-imaging python-jinja2 python-ldap python-libxslt1 python-lxml python-mako python-mock python-openid python-passlib python-psutil python-psycopg2 python-pybabel python-pychart python-pydot python-pyparsing python-pypdf python-reportlab python-requests python-simplejson python-tz python-unittest2 python-vatnumber python-vobject python-werkzeug python-xlwt python-yaml postgresql python-gevent python-serial python-pip python-dev localepurge vim mc mg" PKGS_TO_INSTALL="adduser postgresql-client python python-dateutil python-decorator python-docutils python-feedparser python-imaging python-jinja2 python-ldap python-libxslt1 python-lxml python-mako python-mock python-openid python-passlib python-psutil python-psycopg2 python-pybabel python-pychart python-pydot python-pyparsing python-pypdf python-reportlab python-requests python-simplejson python-tz python-unittest2 python-vatnumber python-vobject python-werkzeug python-xlwt python-yaml postgresql python-gevent python-serial python-pip python-dev localepurge vim mc mg screen"
apt-get -y install ${PKGS_TO_INSTALL} apt-get -y install ${PKGS_TO_INSTALL}
@@ -48,14 +48,22 @@ pip install pyusb==1.0.0b1
pip install qrcode pip install qrcode
pip install evdev pip install evdev
groupadd usbusers
usermod -a -G usbusers pi
usermod -a -G lp pi usermod -a -G lp pi
sudo -u postgres createuser -s pi sudo -u postgres createuser -s pi
mkdir /var/log/odoo mkdir /var/log/odoo
chown pi:pi /var/log/odoo chown pi:pi /var/log/odoo
echo "* * * * * rm /var/run/odoo/sessions/*" | crontab -
update-rc.d odoo defaults update-rc.d odoo defaults
# https://www.raspberrypi.org/forums/viewtopic.php?p=79249
# to not have "setting up console font and keymap" during boot take ages
setupcon
# create dirs for ramdisks # create dirs for ramdisks
create_ramdisk_dir () { create_ramdisk_dir () {
mkdir "${1}_ram" mkdir "${1}_ram"
@@ -0,0 +1,2 @@
SUBSYSTEM=="usb", GROUP="usbusers", MODE="0660"
SUBSYSTEMS=="usb", GROUP="usbusers", MODE="0660"
@@ -0,0 +1,53 @@
#
set meta-flag on
set convert-meta off
set input-meta on
set output-meta on
set show-all-if-ambiguous on
set visible-stats on
set bell-style none
# set bell-style visible
#"\M-k": kill-whole-line
#----------------------------------------------------------
# arrows left/right/up/down
"\e[C": forward-char
"\e[D": backward-char
"\e[A": previous-history
"\e[B": next-history
# ---------------------------------------------------------
# pgup/pgdn
"\e[6~": history-search-forward
"\e[5~": history-search-backward
#----------------------------------------------------------
# insert,delete
#"\e[2~": quoted-insert
"\e[3~": delete-char
# esc+delete, esc+backspace
"\e\e[3~": kill-word
"\e\C-h": backward-kill-word
#----------------------------------------------------------
# ctrl left/right (rxvt)
"\eOc": forward-word
"\eOd": backward-word
# ctrl left/right (xterm)
"\e[1;5C": forward-word
"\e[1;5D": backward-word
# ctrl left/right (unknown)
#"\e[5C": forward-word
#"\e[5D": backward-word
#"\e\e[C": forward-word
#"\e\e[D": backward-word
# ---------------------------------------------------------
# home/end (linux console)
"\e[1~": beginning-of-line
"\e[4~": end-of-line
# home/end (rxvt)
"\e[7~": beginning-of-line
"\e[8~": end-of-line
# home/end (xterm, freebsd console)
"\e[H": beginning-of-line
"\e[F": end-of-line
# home/end (non RH/Debian xterm, unknown)
"\eOH": beginning-of-line
"\eOF": end-of-line
"\eOw": end-of-line
@@ -0,0 +1,27 @@
set all&
set autoindent
set backspace=2
set nobackup
set nocompatible
set noerrorbells
set noexpandtab
set expandtab
set number
set hidden
set history=500
set hlsearch
set ignorecase
set modeline
set ruler
set shiftwidth=4
set scrolloff=5
set showcmd
set showmode
set tabstop=4
set textwidth=0
set visualbell
set t_vb=
set wrap
set list
set listchars=tab:~.,trail:.,extends:>,precedes:<
set viminfo="NONE"
+11 -1
View File
@@ -737,8 +737,12 @@ class purchase_order(osv.osv):
#we don't round the price_unit, as we may want to store the standard price with more digits than allowed by the currency #we don't round the price_unit, as we may want to store the standard price with more digits than allowed by the currency
price_unit = self.pool.get('res.currency').compute(cr, uid, order.currency_id.id, order.company_id.currency_id.id, price_unit, round=False, context=context) price_unit = self.pool.get('res.currency').compute(cr, uid, order.currency_id.id, order.company_id.currency_id.id, price_unit, round=False, context=context)
res = [] res = []
if order.location_id.usage == 'customer':
name = order_line.product_id.name
else:
name = order_line.name or ''
move_template = { move_template = {
'name': order_line.name or '', 'name': name,
'product_id': order_line.product_id.id, 'product_id': order_line.product_id.id,
'product_uom': order_line.product_uom.id, 'product_uom': order_line.product_uom.id,
'product_uos': order_line.product_uom.id, 'product_uos': order_line.product_uom.id,
@@ -1470,6 +1474,9 @@ class procurement_order(osv.osv):
return qty, price return qty, price
def update_origin_po(self, cr, uid, po, proc, context=None):
pass
def _get_grouping_dicts(self, cr, uid, ids, context=None): def _get_grouping_dicts(self, cr, uid, ids, context=None):
""" """
It will group the procurements according to the pos they should go into. That way, lines going to the same It will group the procurements according to the pos they should go into. That way, lines going to the same
@@ -1586,6 +1593,9 @@ class procurement_order(osv.osv):
self.message_post(cr, uid, [proc.id], body=_("Purchase line created and linked to an existing Purchase Order"), context=context) self.message_post(cr, uid, [proc.id], body=_("Purchase line created and linked to an existing Purchase Order"), context=context)
po_obj.write(cr, uid, [add_purchase], {'order_line': line_values},context=context) po_obj.write(cr, uid, [add_purchase], {'order_line': line_values},context=context)
for procurment in procuremnts:
self.update_origin_po(cr, uid, po, procurement, context=context)
# Create new purchase orders # Create new purchase orders
partner_obj = self.pool.get("res.partner") partner_obj = self.pool.get("res.partner")
@@ -8,7 +8,7 @@
<field name="inherit_id" ref="purchase.purchase_order_line_form"/> <field name="inherit_id" ref="purchase.purchase_order_line_form"/>
<field name="arch" type="xml"> <field name="arch" type="xml">
<field name="account_analytic_id" position="replace"> <field name="account_analytic_id" position="replace">
<field name="analytics_id" context="{'journal_id':parent.journal_id, 'model': 'purchase_order'}" domain="[('plan_id','&lt;&gt;',False)]"/> <field name="analytics_id" groups="purchase.group_analytic_accounting" context="{'journal_id':parent.journal_id, 'model': 'purchase_order'}" domain="[('plan_id','&lt;&gt;',False)]"/>
</field> </field>
</field> </field>
</record> </record>
@@ -19,7 +19,7 @@
<field name="inherit_id" ref="purchase.purchase_order_form"/> <field name="inherit_id" ref="purchase.purchase_order_form"/>
<field name="arch" type="xml"> <field name="arch" type="xml">
<field name="account_analytic_id" position="replace"> <field name="account_analytic_id" position="replace">
<field name="analytics_id" context="{'journal_id':parent.journal_id}" domain="[('plan_id','&lt;&gt;',False)]"/> <field name="analytics_id" groups="purchase.group_analytic_accounting" context="{'journal_id':parent.journal_id}" domain="[('plan_id','&lt;&gt;',False)]"/>
</field> </field>
</field> </field>
</record> </record>
+3 -3
View File
@@ -8,7 +8,7 @@
<field name="inherit_id" ref="product.product_product_tree_view"/> <field name="inherit_id" ref="product.product_product_tree_view"/>
<field name="arch" type="xml"> <field name="arch" type="xml">
<tree position="attributes"> <tree position="attributes">
<attribute name="colors">red:virtual_available&lt;0;blue:virtual_available&gt;=0 and state in ('draft', 'end', 'obsolete');black:virtual_available&gt;=0 and state not in ('draft', 'end', 'obsolete')</attribute> <attribute name="colors" translation="off">red:virtual_available&lt;0;blue:virtual_available&gt;=0 and state in ('draft', 'end', 'obsolete');black:virtual_available&gt;=0 and state not in ('draft', 'end', 'obsolete')</attribute>
</tree> </tree>
<field name="price" position="after"> <field name="price" position="after">
<field name="qty_available"/> <field name="qty_available"/>
@@ -23,7 +23,7 @@
<field name="inherit_id" ref="product.product_template_tree_view"/> <field name="inherit_id" ref="product.product_template_tree_view"/>
<field name="arch" type="xml"> <field name="arch" type="xml">
<tree position="attributes"> <tree position="attributes">
<attribute name="colors">red:virtual_available&lt;0;blue:virtual_available&gt;=0 and state in ('draft', 'end', 'obsolete');black:virtual_available&gt;=0 and state not in ('draft', 'end', 'obsolete')</attribute> <attribute name="colors" translation="off">red:virtual_available&lt;0;blue:virtual_available&gt;=0 and state in ('draft', 'end', 'obsolete');black:virtual_available&gt;=0 and state not in ('draft', 'end', 'obsolete')</attribute>
</tree> </tree>
<field name="uom_id" position="after"> <field name="uom_id" position="after">
<field name="qty_available"/> <field name="qty_available"/>
@@ -145,7 +145,7 @@
</group> </group>
</group> </group>
<field name="product_manager" position="attributes"> <field name="product_manager" position="attributes">
<attribute name="context">{'default_groups_ref': ['base.group_user', 'base.group_sale_manager', 'stock.group_stock_manager']}</attribute> <attribute name="context" translation="off">{'default_groups_ref': ['base.group_user', 'base.group_sale_manager', 'stock.group_stock_manager']}</attribute>
</field> </field>
<group name="procurement" position="after" > <group name="procurement" position="after" >
<group string="Supply Chain Information" attrs="{'invisible': [('type', '=', 'service')]}" groups="base.group_user"> <group string="Supply Chain Information" attrs="{'invisible': [('type', '=', 'service')]}" groups="base.group_user">
@@ -43,3 +43,12 @@ class purchase_order(models.Model):
if not self.env.context.get('no_invoice_policy_check'): if not self.env.context.get('no_invoice_policy_check'):
self._check_invoice_policy() self._check_invoice_policy()
super(purchase_order, self).wkf_confirm_order() super(purchase_order, self).wkf_confirm_order()
class procurement_order(models.Model):
_inherit = 'procurement.order'
@api.model
def update_origin_po(self, po, proc):
super(procurement_order, self).update_origin_po(po, proc)
if proc.sale_line_id and not (proc.origin in po.origin):
po.sudo().write({'origin': po.origin+', '+proc.origin})
+1 -1
View File
@@ -315,7 +315,7 @@ def login_redirect():
# built the redirect url, keeping all the query parameters of the url # built the redirect url, keeping all the query parameters of the url
redirect_url = '%s?%s' % (request.httprequest.base_url, werkzeug.urls.url_encode(request.params)) redirect_url = '%s?%s' % (request.httprequest.base_url, werkzeug.urls.url_encode(request.params))
return """<html><head><script> return """<html><head><script>
window.location = '%sredirect=' + encodeURIComponent("%s"); window.location = '%sredirect=' + encodeURIComponent("%s" + location.hash);
</script></head></html> </script></head></html>
""" % (url, redirect_url) """ % (url, redirect_url)
@@ -357,10 +357,6 @@ body > nav.navbar {
} }
/* ----- BOOTSTRAP FIX ----- */ /* ----- BOOTSTRAP FIX ----- */
.col-md-12 {
float: left;
width: 100%;
}
@-moz-document url-prefix() { @-moz-document url-prefix() {
.table .img-responsive { .table .img-responsive {
@@ -251,10 +251,6 @@ body > nav.navbar
/* ----- BOOTSTRAP FIX ----- */ /* ----- BOOTSTRAP FIX ----- */
.col-md-12
float: left
width: 100%
@-moz-document url-prefix() @-moz-document url-prefix()
.table .img-responsive .table .img-responsive
width: 100% width: 100%
+17
View File
@@ -0,0 +1,17 @@
Russia, 2015-08-14
IT Libertas agrees to the terms of the Odoo Corporate Contributor License
Agreement v1.0.
I declare that I am authorized and able to make this agreement and sign this
declaration.
Signed,
IT Libertas LLC info@itlibertas.com https://github.com/itlibertas
List of contributors:
Denis Baranov baranov@itlibertas.com https://github.com/barachka
Tatiana Deribina deribina@itlibertas.com https://github.com/Sapfiriana
Aleksandr Shevelev shevelev@itlibertas.com https://github.com/alexalv
+11
View File
@@ -0,0 +1,11 @@
France, 2015-08-18
I hereby agree to the terms of the Odoo Individual Contributor License
Agreement v1.0.
I declare that I am authorized and able to make this agreement and sign this
declaration.
Signed,
Florian Hatat github@hatat.me https://github.com/flh
+3 -5
View File
@@ -108,11 +108,9 @@ class ir_http(osv.AbstractModel):
datas = attach[0]['datas'] or '' datas = attach[0]['datas'] or ''
name = attach[0]['name'] name = attach[0]['name']
if not datas: if (not datas and name != request.httprequest.path and
if name.startswith(('http://', 'https://', '/')): name.startswith(('http://', 'https://', '/'))):
return werkzeug.utils.redirect(name, 301) return werkzeug.utils.redirect(name, 301)
else:
return werkzeug.wrappers.Response(status=204) # NO CONTENT
response = werkzeug.wrappers.Response() response = werkzeug.wrappers.Response()
server_format = openerp.tools.misc.DEFAULT_SERVER_DATETIME_FORMAT server_format = openerp.tools.misc.DEFAULT_SERVER_DATETIME_FORMAT
+56 -4
View File
@@ -5,6 +5,18 @@ import openerp.tests.common as common
class test_search(common.TransactionCase): class test_search(common.TransactionCase):
def patch_order(self, model, order):
m_e = self.env[model]
m_r = self.registry(model)
old_order = m_e._order
@self.addCleanup
def cleanup():
m_r._order = type(m_e)._order = old_order
m_r._order = type(m_e)._order = order
def test_00_search_order(self): def test_00_search_order(self):
registry, cr, uid = self.registry, self.cr, self.uid registry, cr, uid = self.registry, self.cr, self.uid
@@ -96,15 +108,13 @@ class test_search(common.TransactionCase):
self.assertEqual(test_user_ids, expected_ids, 'search on res_users did not provide expected ids or expected order') self.assertEqual(test_user_ids, expected_ids, 'search on res_users did not provide expected ids or expected order')
# Do: order on many2one, but not by specifying in order parameter of search, but by overriding _order of res_users # Do: order on many2one, but not by specifying in order parameter of search, but by overriding _order of res_users
old_order = users_obj._order self.patch_order('res.users', 'country_id desc, name asc, login desc')
users_obj._order = 'country_id desc, name asc, login desc'
user_ids = users_obj.search(cr, search_user, []) user_ids = users_obj.search(cr, search_user, [])
expected_ids = [search_user, c, b, a] expected_ids = [search_user, c, b, a]
test_user_ids = filter(lambda x: x in expected_ids, user_ids) test_user_ids = filter(lambda x: x in expected_ids, user_ids)
self.assertEqual(test_user_ids, expected_ids, 'search on res_users did not provide expected ids or expected order') self.assertEqual(test_user_ids, expected_ids, 'search on res_users did not provide expected ids or expected order')
users_obj._order = old_order
def test_11_indirect_inherits_m2order(self): def test_11_indirect_inherits_m2o_order(self):
registry, cr, uid = self.registry, self.cr, self.uid registry, cr, uid = self.registry, self.cr, self.uid
Cron = registry('ir.cron') Cron = registry('ir.cron')
Users = registry('res.users') Users = registry('res.users')
@@ -119,6 +129,48 @@ class test_search(common.TransactionCase):
expected_ids = [cron_ids[l] for l in 'ABC'] expected_ids = [cron_ids[l] for l in 'ABC']
self.assertEqual(ids, expected_ids) self.assertEqual(ids, expected_ids)
def test_12_m2o_order_loop_self(self):
registry, cr, uid = self.registry, self.cr, self.uid
Cats = registry('ir.module.category')
ids = {}
def create(name, **kw):
ids[name] = Cats.create(cr, uid, dict(kw, name=name))
self.patch_order('ir.module.category', 'parent_id desc, name')
create('A')
create('B', parent_id=ids['A'])
create('C', parent_id=ids['A'])
create('D')
create('E', parent_id=ids['D'])
create('F', parent_id=ids['D'])
expected_order = [ids[x] for x in 'ADEFBC']
domain = [('id', 'in', ids.values())]
search_result = Cats.search(cr, uid, domain)
self.assertEqual(search_result, expected_order)
def test_13_m2o_order_loop_multi(self):
Users = self.env['res.users']
# will sort by login desc of the creator, then by name
self.patch_order('res.partner', 'create_uid, name')
self.patch_order('res.users', 'partner_id, login desc')
kw = dict(groups_id=[(6, 0, [self.ref('base.group_system'),
self.ref('base.group_partner_manager')])])
u1 = Users.create(dict(name='Q', login='m', **kw)).id
u2 = Users.sudo(user=u1).create(dict(name='B', login='f', **kw)).id
u3 = Users.create(dict(name='C', login='c', **kw)).id
u4 = Users.sudo(user=u2).create(dict(name='D', login='z', **kw)).id
expected_order = [u2, u4, u3, u1]
domain = [('id', 'in', [u1, u2, u3, u4])]
search_result = list(Users.search(domain)._ids)
self.assertEqual(search_result, expected_order)
if __name__ == '__main__': if __name__ == '__main__':
unittest2.main() unittest2.main()
+21 -13
View File
@@ -4536,7 +4536,7 @@ class BaseModel(object):
apply_rule(rule_where_clause, rule_where_clause_params, rule_tables, apply_rule(rule_where_clause, rule_where_clause_params, rule_tables,
parent_model=inherited_model) parent_model=inherited_model)
def _generate_m2o_order_by(self, alias, order_field, query, reverse_direction): def _generate_m2o_order_by(self, alias, order_field, query, reverse_direction, seen):
""" """
Add possibly missing JOIN to ``query`` and generate the ORDER BY clause for m2o fields, Add possibly missing JOIN to ``query`` and generate the ORDER BY clause for m2o fields,
either native m2o fields or function/related fields that are stored, including either native m2o fields or function/related fields that are stored, including
@@ -4546,18 +4546,18 @@ class BaseModel(object):
""" """
if order_field not in self._columns and order_field in self._inherit_fields: if order_field not in self._columns and order_field in self._inherit_fields:
# also add missing joins for reaching the table containing the m2o field # also add missing joins for reaching the table containing the m2o field
qualified_field = self._inherits_join_calc(alias, order_field, query)
order_field_column = self._inherit_fields[order_field][2] order_field_column = self._inherit_fields[order_field][2]
qualified_field = self._inherits_join_calc(alias, order_field, query)
alias, order_field = qualified_field.replace('"', '').split('.', 1)
else: else:
qualified_field = '"%s"."%s"' % (alias, order_field)
order_field_column = self._columns[order_field] order_field_column = self._columns[order_field]
assert order_field_column._type == 'many2one', 'Invalid field passed to _generate_m2o_order_by()' assert order_field_column._type == 'many2one', 'Invalid field passed to _generate_m2o_order_by()'
if not order_field_column._classic_write and not getattr(order_field_column, 'store', False): if not order_field_column._classic_write and not getattr(order_field_column, 'store', False):
_logger.debug("Many2one function/related fields must be stored " \ _logger.debug("Many2one function/related fields must be stored "
"to be used as ordering fields! Ignoring sorting for %s.%s", "to be used as ordering fields! Ignoring sorting for %s.%s",
self._name, order_field) self._name, order_field)
return return []
# figure out the applicable order_by for the m2o # figure out the applicable order_by for the m2o
dest_model = self.pool[order_field_column._obj] dest_model = self.pool[order_field_column._obj]
@@ -4568,12 +4568,14 @@ class BaseModel(object):
# Join the dest m2o table if it's not joined yet. We use [LEFT] OUTER join here # Join the dest m2o table if it's not joined yet. We use [LEFT] OUTER join here
# as we don't want to exclude results that have NULL values for the m2o # as we don't want to exclude results that have NULL values for the m2o
src_table, src_field = qualified_field.replace('"', '').split('.', 1) join = (alias, dest_model._table, order_field, 'id', order_field)
dst_alias, dst_alias_statement = query.add_join((src_table, dest_model._table, src_field, 'id', src_field), implicit=False, outer=True) dst_alias, dst_alias_statement = query.add_join(join, implicit=False, outer=True)
return dest_model._generate_order_by_inner(dst_alias, m2o_order, query, return dest_model._generate_order_by_inner(dst_alias, m2o_order, query,
reverse_direction=reverse_direction) reverse_direction=reverse_direction, seen=seen)
def _generate_order_by_inner(self, alias, order_spec, query, reverse_direction=False): def _generate_order_by_inner(self, alias, order_spec, query, reverse_direction=False, seen=None):
if seen is None:
seen = set()
order_by_elements = [] order_by_elements = []
self._check_qorder(order_spec) self._check_qorder(order_spec)
for order_part in order_spec.split(','): for order_part in order_spec.split(','):
@@ -4594,7 +4596,10 @@ class BaseModel(object):
inner_clauses = ['"%s"."%s"' % (alias, order_field)] inner_clauses = ['"%s"."%s"' % (alias, order_field)]
add_dir = True add_dir = True
elif order_column._type == 'many2one': elif order_column._type == 'many2one':
inner_clauses = self._generate_m2o_order_by(alias, order_field, query, do_reverse) key = (self._name, order_column._obj, order_field)
if key not in seen:
seen.add(key)
inner_clauses = self._generate_m2o_order_by(alias, order_field, query, do_reverse, seen)
else: else:
continue # ignore non-readable or "non-joinable" fields continue # ignore non-readable or "non-joinable" fields
elif order_field in self._inherit_fields: elif order_field in self._inherit_fields:
@@ -4604,7 +4609,10 @@ class BaseModel(object):
inner_clauses = [self._inherits_join_calc(alias, order_field, query)] inner_clauses = [self._inherits_join_calc(alias, order_field, query)]
add_dir = True add_dir = True
elif order_column._type == 'many2one': elif order_column._type == 'many2one':
inner_clauses = self._generate_m2o_order_by(alias, order_field, query, do_reverse) key = (parent_obj._name, order_column._obj, order_field)
if key not in seen:
seen.add(key)
inner_clauses = self._generate_m2o_order_by(alias, order_field, query, do_reverse, seen)
else: else:
continue # ignore non-readable or "non-joinable" fields continue # ignore non-readable or "non-joinable" fields
else: else: