Merge remote-tracking branch 'odoo/master' into master-allow-translate-on-noedit-sle

This commit is contained in:
Simon Lejeune
2014-07-01 17:17:40 +02:00
29 changed files with 630 additions and 424 deletions
+2 -2
View File
@@ -714,7 +714,7 @@ class account_journal(osv.osv):
_name = "account.journal"
_description = "Journal"
_columns = {
'with_last_closing_balance' : fields.boolean('Opening With Last Closing Balance'),
'with_last_closing_balance': fields.boolean('Opening With Last Closing Balance', help="For cash or bank journal, this option should be unchecked when the starting balance should always set to 0 for new documents."),
'name': fields.char('Journal Name', required=True),
'code': fields.char('Code', size=5, required=True, help="The code will be displayed on reports."),
'type': fields.selection([('sale', 'Sale'),('sale_refund','Sale Refund'), ('purchase', 'Purchase'), ('purchase_refund','Purchase Refund'), ('cash', 'Cash'), ('bank', 'Bank and Checks'), ('general', 'General'), ('situation', 'Opening/Closing Situation')], 'Type', size=32, required=True,
@@ -745,7 +745,7 @@ class account_journal(osv.osv):
_defaults = {
'cash_control' : False,
'with_last_closing_balance' : False,
'with_last_closing_balance' : True,
'user_id': lambda self, cr, uid, context: uid,
'company_id': lambda self, cr, uid, c: self.pool.get('res.users').browse(cr, uid, uid, c).company_id.id,
}
+1 -1
View File
@@ -439,7 +439,7 @@
<field name="internal_account_id" domain="[('type','!=','view')]"/>
</group>
<group string="Miscellaneous">
<field name="with_last_closing_balance"/>
<field name="with_last_closing_balance" attrs="{'invisible': [('type', 'not in', ['bank', 'cash'])]}"/>
<field name="cash_control" attrs="{'invisible':[('type','not in', ('cash',))]}"/>
</group>
</group>
@@ -15,44 +15,63 @@
</field>
</record>
<!-- Custom reports (aka filters) -->
<record id="filter_invoice_salespersons" model="ir.filters">
<field name="name">By Salespersons</field>
<field name="model_id">account.invoice.report</field>
<field name="domain">[]</field>
<field name="user_id" eval="False"/>
<field name="context">{'group_by': ['date:month', 'user_id']}</field>
</record>
<record id="filter_invoice_product" model="ir.filters">
<field name="name">By Product</field>
<field name="model_id">account.invoice.report</field>
<field name="domain">[]</field>
<field name="user_id" eval="False"/>
<field name="context">{'group_by': ['date:month', 'product_id'], 'set_visible':True, 'residual_invisible':True}</field>
</record>
<record id="filter_invoice_product_category" model="ir.filters">
<field name="name">By Product Category</field>
<field name="model_id">account.invoice.report</field>
<field name="domain">[]</field>
<field name="user_id" eval="False"/>
<field name="context">{'group_by': ['date:month', 'categ_id'], 'residual_invisible':True}</field>
</record>
<record id="filter_invoice_refund" model="ir.filters">
<field name="name">By Refund</field>
<field name="model_id">account.invoice.report</field>
<field name="domain">[('type', '=', 'out_refund')]</field>
<field name="user_id" eval="False"/>
<field name="context">{'group_by': ['date:month', 'user_id']}</field>
</record>
<record id="filter_invoice_country" model="ir.filters">
<field name="name">By Country</field>
<field name="model_id">account.invoice.report</field>
<field name="domain">[]</field>
<field name="user_id" eval="False"/>
<field name="context">{'group_by': ['date:month', 'country_id']}</field>
</record>
<record id="view_account_invoice_report_search" model="ir.ui.view">
<field name="name">account.invoice.report.search</field>
<field name="model">account.invoice.report</field>
<field name="arch" type="xml">
<search string="Invoices Analysis">
<field name="date"/>
<filter icon="terp-go-year" string="Year" name="year" domain="['|',('date', '=', False),'&amp;',('date','&lt;=', time.strftime('%%Y-%%m-%%d')),('date','&gt;=',time.strftime('%%Y-01-01'))]" help="year"/>
<filter string="This Year" name="year" domain="['|', ('date', '=', False), '&amp;',('date','&lt;=', time.strftime('%%Y-12-31')),('date','&gt;=',time.strftime('%%Y-01-01'))]"/>
<separator/>
<filter string="Draft" icon="terp-document-new" domain="[('state','=','draft')]" help = "Draft Invoices"/>
<filter string="Pro-forma" icon="terp-gtk-media-pause" domain="['|', ('state','=','proforma'),('state','=','proforma2')]" help = "Pro-forma Invoices"/>
<filter string="Invoiced" name="current" icon="terp-check" domain="[('state','not in', ('draft','cancel','proforma','proforma2'))]" help = "Open and Paid Invoices"/>
<filter string="To Invoice" domain="[('state','=','draft')]" help = "Draft Invoices"/>
<filter string="Pro-forma" domain="['|', ('state','=','proforma'),('state','=','proforma2')]"/>
<filter string="Invoiced" name="current" domain="[('state','not in', ('draft','cancel','proforma','proforma2'))]"/>
<separator/>
<filter icon="terp-personal" string="Customer" name="customer" domain="['|', ('type','=','out_invoice'),('type','=','out_refund')]" help="Customer Invoices And Refunds"/>
<filter icon="terp-personal" string="Supplier" domain="['|', ('type','=','in_invoice'),('type','=','in_refund')]" help="Supplier Invoices And Refunds"/>
<filter string="Customer" name="customer" domain="['|', ('type','=','out_invoice'),('type','=','out_refund')]"/>
<filter string="Supplier" domain="['|', ('type','=','in_invoice'),('type','=','in_refund')]"/>
<separator/>
<filter icon="terp-dolar" string="Invoice" domain="['|', ('type','=','out_invoice'),('type','=','in_invoice')]" help="Customer And Supplier Invoices"/>
<filter icon="terp-dolar_ok!" string="Refund" domain="['|', ('type','=','out_refund'),('type','=','in_refund')]" help="Customer And Supplier Refunds"/>
<filter string="Invoice" domain="['|', ('type','=','out_invoice'),('type','=','in_invoice')]"/>
<filter string="Refund" domain="['|', ('type','=','out_refund'),('type','=','in_refund')]"/>
<field name="partner_id"/>
<field name="user_id" />
<field name="categ_id" filter_domain="[('categ_id', 'child_of', self)]"/>
<group expand="1" string="Group By">
<filter string="Partner" name="partner_id" context="{'group_by':'partner_id','residual_visible':True}"/>
<filter string="Commercial Partner" name="commercial_partner_id" context="{'group_by':'commercial_partner_id','residual_visible':True}"/>
<filter string="Commercial Partner's Country" name="country_id" context="{'group_by':'country_id'}"/>
<filter string="Salesperson" name='user' icon="terp-personal" context="{'group_by':'user_id'}"/>
<filter string="Due Month" icon="terp-go-today" context="{'group_by':'date_due'}"/>
<filter string="Period" icon="terp-go-month" context="{'group_by':'period_id'}"/>
<filter string="Product" icon="terp-accessories-archiver" context="{'group_by':'product_id','set_visible':True,'residual_invisible':True}"/>
<filter string="Category of Product" name="category_product" icon="terp-stock_symbol-selection" context="{'group_by':'categ_id','residual_invisible':True}"/>
<filter string="Status" icon="terp-stock_effects-object-colorize" context="{'group_by':'state'}"/>
<filter string="Type" icon="terp-stock_symbol-selection" context="{'group_by':'type'}"/>
<filter string="Journal" icon="terp-folder-orange" context="{'group_by':'journal_id'}"/>
<filter string="Account" icon="terp-folder-orange" context="{'group_by':'account_line_id'}"/>
<filter string="Company" icon="terp-go-home" context="{'group_by':'company_id'}" groups="base.group_multi_company"/>
<filter string="Invoice Date (day)" name="day" icon="terp-go-today" context="{'group_by':'date:day'}" help="Group by Invoice Date"/>
<filter string="Invoice Date (month)" name="month" icon="terp-go-month" context="{'group_by':'date:month'}" help="Group by month of Invoice Date"/>
<filter string="Invoice Date (year)" name="group_year" icon="terp-go-year" context="{'group_by':'date:year'}" help="Group by year of Invoice Date"/>
</group>
</search>
</field>
</record>
+21 -47
View File
@@ -222,13 +222,13 @@
<field name="create_date"/>
<field name="name"/>
<field name="contact_name"/>
<field name="country_id" invisible="context.get('invisible_country', True)"/>
<field name="country_id"/>
<field name="email_from"/>
<field name="phone"/>
<field name="stage_id"/>
<field name="user_id" invisible="1"/>
<field name="partner_id" invisible="1"/>
<field name="section_id" invisible="context.get('invisible_section', True)" groups="base.group_multi_salesteams"/>
<field name="section_id" groups="base.group_multi_salesteams"/>
<field name="probability" invisible="1"/>
<field name="type_id" invisible="1"/>
<field name="referred" invisible="1"/>
@@ -326,44 +326,31 @@
<search string="Search Leads">
<field name="name" string="Lead / Customer" filter_domain="['|','|','|',('partner_name','ilike',self),('email_from','ilike',self),('contact_name','ilike',self),('name','ilike',self)]"/>
<field name="categ_ids" string="Tag" filter_domain="[('categ_ids', 'ilike', self)]"/>
<field name="section_id" context="{'invisible_section': False}" groups="base.group_multi_salesteams"/>
<field name="section_id" groups="base.group_multi_salesteams"/>
<field name="user_id"/>
<field name="partner_id" operator="child_of"/>
<field name="create_date"/>
<field name="country_id" context="{'invisible_country': False}"/>
<field name="country_id"/>
<separator/>
<filter string="My Leads"
domain="[('user_id','=',uid)]"
help="Leads that are assigned to me"/>
<filter string="Unassigned" name="unassigned"
domain="[('user_id','=', False)]"
help="No salesperson"/>
<filter string="My Leads"
domain="[('user_id','=',uid)]" context="{'invisible_section': False}"
help="Leads that are assigned to me"/>
<filter string="My Team(s)" groups="base.group_multi_salesteams"
domain="[('section_id.member_ids', 'in', [uid])]" context="{'invisible_section': False}"
help="Leads that are assigned to any sales teams I am member of"/>
<filter string="Dead" name="dead"
domain="[('probability', '=', '0'), ('stage_id.fold', '=', True)]"/>
<filter string="Unread Messages" name="message_unread" domain="[('message_unread','=',True)]"/>
<separator />
<filter string="New Mail" name="message_unread" domain="[('message_unread','=',True)]"/>
<separator />
<filter string="Available for mass mailing"
name='not_opt_out' domain="[('opt_out', '=', False)]"
help="Leads that did not ask not to be included in mass mailing campaigns"/>
<separator />
<group expand="0" string="Group By">
<filter string="Salesperson" domain="[]" context="{'group_by':'user_id'}"/>
<filter string="Team" domain="[]" context="{'group_by':'section_id'}" groups="base.group_multi_salesteams"/>
<filter string="Sales Team" domain="[]" context="{'group_by':'section_id'}" groups="base.group_multi_salesteams"/>
<filter string="Stage" domain="[]" context="{'group_by':'stage_id'}"/>
<filter string="Customer" help="Partner" domain="[]" context="{'group_by':'partner_id'}"/>
<filter string="Country" domain="[]" context="{'group_by':'country_id'}"/>
<filter string="Referrer" domain="[]" context="{'group_by':'referred'}"/>
<filter string="Campaign" domain="[]" context="{'group_by':'type_id'}"/>
<filter string="Channel" domain="[]" context="{'group_by':'channel_id'}"/>
<filter string="Creation" domain="[]" context="{'group_by':'create_date'}"/>
<filter string="Last Post (weekly)" name="group_message_last_post" domain="[]" context="{'group_by':'message_last_post:week'}"/>
</group>
<group string="Display">
<filter string="Countries" context="{'invisible_country': False}" help="Countries"/>
<filter string="Sales Team" context="{'invisible_section': False}" domain="[]" help="Sales Team" groups="base.group_multi_salesteams"/>
<filter string="Expected Closing" domain="[]" context="{'group_by':'date_deadline:week'}"/>
<filter string="Last Message" name="group_message_last_post" domain="[]" context="{'group_by':'message_last_post:week'}"/>
</group>
</search>
</field>
@@ -523,7 +510,7 @@
<field name="create_date"/>
<field name="name" string="Opportunity"/>
<field name="partner_id" string="Customer"/>
<field name="country_id" invisible="context.get('invisible_country', True)"/>
<field name="country_id"/>
<field name="date_action"/>
<field name="title_action"/>
<field name="channel_id" invisible="1"/>
@@ -531,7 +518,7 @@
<field name="stage_id"/>
<field name="planned_revenue" sum="Expected Revenues"/>
<field name="probability" avg="Avg. of Probability"/>
<field name="section_id" invisible="context.get('invisible_section', True)" groups="base.group_multi_salesteams"/>
<field name="section_id" groups="base.group_multi_salesteams"/>
<field name="user_id"/>
<field name="referred" invisible="1"/>
<field name="priority" invisible="1"/>
@@ -551,7 +538,7 @@
<search string="Search Opportunities">
<field name="name" string="Opportunity" filter_domain="['|','|','|',('partner_id','ilike',self),('partner_name','ilike',self),('email_from','ilike',self),('name', 'ilike', self)]"/>
<field name="categ_ids" string="Tag" filter_domain="[('categ_ids', 'ilike', self)]"/>
<field name="section_id" context="{'invisible_section': False}" groups="base.group_multi_salesteams"/>
<field name="section_id" groups="base.group_multi_salesteams"/>
<field name="user_id"/>
<field name="partner_id" operator="child_of"/>
<field name="stage_id" domain="[]"/>
@@ -561,34 +548,21 @@
domain="[('probability', '=', 100), ('stage_id.fold', '=', True)]"/>
<filter string="Lost" name="lost"
domain="[('probability', '=', 0), ('stage_id.fold', '=', True)]"/>
<separator/>
<filter string="My Opportunities" name="assigned_to_me"
domain="[('user_id', '=', uid)]"
help="Opportunities that are assigned to me"/>
<filter string="Unassigned" name="unassigned"
domain="[('user_id','=', False)]" help="No salesperson"/>
<filter string="My Opportunities" name="assigned_to_me"
domain="[('user_id', '=', uid)]" context="{'invisible_section': False}"
help="Opportunities that are assigned to me"/>
<filter string="My Team(s)"
domain="[('section_id.member_ids', 'in', [uid])]" context="{'invisible_section': False}"
help="Opportunities that are assigned to any sales teams I am member of" groups="base.group_multi_salesteams"/>
<filter string="Unread Messages" name="message_unread" domain="[('message_unread','=',True)]"/>
<separator/>
<filter string="New Mail" name="message_unread" domain="[('message_unread','=',True)]"/>
<group expand="0" string="Group By" colspan="16">
<filter string="Salesperson" domain="[]" context="{'group_by':'user_id'}"/>
<filter string="Team" domain="[]" context="{'group_by':'section_id'}" groups="base.group_multi_salesteams"/>
<filter string="Stage" domain="[]" context="{'group_by':'stage_id'}"/>
<filter string="Customer" help="Partner" domain="[]" context="{'group_by':'partner_id'}"/>
<filter string="Country" domain="[]" context="{'group_by':'country_id'}"/>
<filter string="Priority" domain="[]" context="{'group_by':'priority'}"/>
<filter string="Expected Closing" domain="[]" context="{'group_by':'date_deadline'}"/>
<filter string="Referrer" domain="[]" context="{'group_by':'referred'}"/>
<filter string="Campaign" domain="[]" context="{'group_by':'type_id'}"/>
<filter string="Channel" domain="[]" context="{'group_by':'channel_id'}"/>
<filter string="Creation" domain="[]" context="{'group_by':'create_date'}"/>
<filter string="Last Update Month" domain="[]" context="{'group_by':'write_date'}"/>
<filter string="Last Post (weekly)" name="group_message_last_post" domain="[]" context="{'group_by':'message_last_post:week'}"/>
</group>
<group string="Display">
<filter string="Sales Team" context="{'invisible_section': False}" domain="[]" help="Sales Team" groups="base.group_multi_salesteams"/>
<filter string="Countries" context="{'invisible_country': False}" help="Countries"/>
<filter string="Last Message" name="group_message_last_post" domain="[]" context="{'group_by':'message_last_post:week'}"/>
</group>
</search>
</field>
+22 -22
View File
@@ -170,29 +170,29 @@
<field name="model">crm.phonecall</field>
<field name="arch" type="xml">
<search string="Search Phonecalls">
<field name="name" string="Phonecalls"/>
<field name="date"/>
<field name="state"/>
<separator/>
<filter icon="terp-gtk-go-back-rtl" string="To Do" name="current" domain="[('state','=','open')]"/>
<separator/>
<filter string="Unassigned Phonecalls" icon="terp-personal-" domain="[('user_id','=',False)]" help="Unassigned Phonecalls"/>
<filter string="Unread Messages" name="message_unread" domain="[('message_unread','=',True)]"/>
<separator/>
<filter string="Phone Calls Assigned to Me or My Team(s)" icon="terp-personal+" domain="['|', ('section_id.user_id','=',uid), ('user_id', '=', uid)]"
help="Phone Calls Assigned to the current user or with a team having the current user as team leader"/>
<field name="partner_id" operator="child_of"/>
<field name="user_id"/>
<field name="opportunity_id"/>
<field name="section_id" string="Sales Team"
<field name="name" string="Phonecalls"/>
<field name="date"/>
<field name="state"/>
<filter string="My Phonecalls" domain="[('user_id', '=', uid)]"/>
<filter string="My Team" domain="[('section_id.user_id', '=', uid)]"/>
<filter string="Unassigned" domain="[('user_id','=',False)]"/>
<separator/>
<filter string="To Do" name="current" domain="[('state','=','open')]"/>
<separator/>
<filter string="New Mail" name="message_unread" domain="[('message_unread','=',True)]"/>
<separator/>
<field name="partner_id" operator="child_of"/>
<field name="user_id"/>
<field name="opportunity_id"/>
<field name="section_id" string="Sales Team"
groups="base.group_multi_salesteams"/>
<group expand="0" string="Group By">
<filter string="Partner" icon="terp-partner" domain="[]" context="{'group_by':'partner_id'}"/>
<filter string="Responsible" icon="terp-personal" domain="[]" context="{'group_by':'user_id'}"/>
<filter string="Creation" icon="terp-go-month" help="Creation Date" domain="[]" context="{'group_by':'create_date'}"/>
<filter string="Calls Month" icon="terp-go-month" domain="[]" context="{'group_by':'date'}" help="Calls Date by Month"/>
</group>
</search>
<group expand="0" string="Group By">
<filter string="Partner" domain="[]" context="{'group_by':'partner_id'}"/>
<filter string="Responsible" domain="[]" context="{'group_by':'user_id'}"/>
<filter string="Creation" help="Creation Date" domain="[]" context="{'group_by':'create_date'}"/>
<filter string="Month" domain="[]" context="{'group_by':'date'}" help="Calls Date by Month"/>
</group>
</search>
</field>
</record>
+17 -28
View File
@@ -2,8 +2,7 @@
<openerp>
<data>
<!-- Leads by user and section Graph View -->
<!-- Leads by user and section Graph View -->
<record id="view_report_crm_lead_graph" model="ir.ui.view">
<field name="name">crm.lead.report.graph</field>
<field name="model">crm.lead.report</field>
@@ -38,8 +37,23 @@
</field>
</record>
<!-- Leads by user and section Search View -->
<!-- Custom reports (aka filters) -->
<record id="filter_leads_salesperson" model="ir.filters">
<field name="name">By Salespersons</field>
<field name="model_id">crm.lead.report</field>
<field name="domain">[]</field>
<field name="user_id" eval="False"/>
<field name="context">{'group_by': ['create_date:month', user_id']}</field>
</record>
<record id="filter_leads_country" model="ir.filters">
<field name="name">By Country</field>
<field name="model_id">crm.lead.report</field>
<field name="domain">[]</field>
<field name="user_id" eval="False"/>
<field name="context">{'group_by': ['create_date:month', 'country_id']}</field>
</record>
<!-- Leads by user and section Search View -->
<record id="view_report_crm_lead_filter" model="ir.ui.view">
<field name="name">crm.lead.report.select</field>
<field name="model">crm.lead.report</field>
@@ -54,11 +68,6 @@
domain="[('probability', '=', 100), ('stage_id.on_change', '=', 1)]"/>
<filter string="Lost" name="lost"
domain="[('probability', '=', 0), ('stage_id.sequence', '!=', 1)]"/>
<separator/>
<filter string="My Sales Team(s)" icon="terp-personal+" context="{'invisible_section': False}" domain="[('section_id.user_id','=',uid)]"
help="Leads/Opportunities that are assigned to one of the sale teams I manage" groups="base.group_multi_salesteams"/>
<separator/>
<filter icon="terp-personal" string="My Case(s)" help="Leads/Opportunities that are assigned to me" domain="[('user_id','=',uid)]"/>
<field name="section_id" context="{'invisible_section': False}"
groups="base.group_multi_salesteams"/>
<field name="user_id" string="Salesperson"/>
@@ -79,26 +88,6 @@
<field name="opening_date"/>
<field name="date_closed"/>
</group>
<group expand="1" string="Group By">
<filter string="Salesperson" domain="[]" context="{'group_by':'user_id'}" />
<filter string="Sales Team" domain="[]" context="{'group_by':'section_id'}" groups="base.group_multi_salesteams"/>
<filter string="Partner" context="{'group_by':'partner_id'}" />
<filter string="Country" context="{'group_by':'country_id'}" />
<filter string="Company" domain="[]" context="{'group_by':'company_id'}" groups="base.group_multi_company"/>
<filter string="Type" domain="[]" context="{'group_by':'type'}"/>
<filter string="Stage" domain="[]" context="{'group_by':'stage_id'}"/>
<filter string="Priority" domain="[]" context="{'group_by':'priority'}" />
<filter string="Campaign" domain="[]" context="{'group_by':'type_id'}" />
<filter string="Channel" domain="[]" context="{'group_by':'channel_id'}" />
<separator orientation="vertical" />
<filter string="Creation date (day)" domain="[]" context="{'group_by':'create_date:day'}"/>
<filter string="Creation date (week)" domain="[]" context="{'group_by':'create_date:week'}"/>
<filter string="Creation date (month)" domain="[]" context="{'group_by':'create_date:month'}" name="month"/>
<filter string="Creation date (year)" domain="[]" context="{'group_by':'create_date:year'}"/>
<separator orientation="vertical" />
<filter string="Exp. Closing" domain="[]" context="{'group_by':'date_deadline'}"/>
<filter string="Last Stage Update" context="{'group_by':'date_last_stage_update'}" />
</group>
</search>
</field>
</record>
+6 -9
View File
@@ -59,13 +59,10 @@
<field name="model">crm.lead</field>
<field name="inherit_id" ref="crm.view_crm_case_opportunities_filter"/>
<field name="arch" type="xml">
<filter string="Team" position="after">
<filter string="Assigned Partner" icon="terp-personal" domain="[]" context="{'group_by':'partner_assigned_id'}"/>
<filter string="Assigned Month" icon="terp-go-month"
domain="[]" context="{'group_by':'date_assign'}"/>
<filter string="Customer" position="after">
<filter string="Assigned Partner" domain="[]" context="{'group_by':'partner_assigned_id'}"/>
</filter>
<filter string="My Team(s)" position="after">
<filter name="unassigned" position="after">
<filter string="My Assigned Partners" domain="[('partner_assigned_id.user_id', '=', uid)]"/>
</filter>
<field name="partner_id" position="after">
@@ -131,10 +128,10 @@
<field name="model">crm.lead</field>
<field name="inherit_id" ref="crm.view_crm_case_leads_filter"/>
<field name="arch" type="xml">
<filter string="Team" position="after">
<filter string="Assigned Partner" icon="terp-personal" domain="[]" context="{'group_by':'partner_assigned_id'}"/>
<filter string="Customer" position="after">
<filter string="Assigned Partner" domain="[]" context="{'group_by':'partner_assigned_id'}"/>
</filter>
<filter string="My Team(s)" position="after">
<filter name="unassigned" position="after">
<filter string="My Assigned Partners" domain="[('partner_assigned_id.user_id', '=', uid)]"/>
</filter>
<field name="partner_id" position="after">
+3 -10
View File
@@ -193,14 +193,13 @@
<field name="arch" type="xml">
<search string="Search Applicants">
<field name="partner_name" filter_domain="['|','|',('name','ilike',self),('partner_name','ilike',self),('email_from','ilike',self)]" string="Subject / Applicant"/>
<filter string="Unassigned" domain="[('user_id', '=', False)]"/>
<filter string="My" domain="[('user_id', '=', uid)]"/>
<separator/>
<filter string="Unread Messages" name="message_unread" domain="[('message_unread','=',True)]"/>
<filter string="Unassigned" domain="[('user_id', '=', False)]"/>
<separator/>
<filter string="Next Actions" context="{'invisible_next_action':False, 'invisible_next_date':False}"
domain="[('date_action','&lt;&gt;',False)]" help="Filter and view on next actions and date"/>
<separator/>
<filter string="New Mail" name="message_unread" domain="[('message_unread','=',True)]"/>
<field name="job_id"/>
<field name="department_id"/>
<field name="user_id"/>
@@ -210,15 +209,9 @@
<separator/>
<group expand="0" string="Group By">
<filter string="Responsible" domain="[]" context="{'group_by':'user_id'}"/>
<filter string="Department" domain="[]" context="{'group_by':'department_id'}"/>
<filter string="Job" domain="[]" context="{'group_by':'job_id'}"/>
<filter string="Degree" domain="[]" context="{'group_by':'type_id'}"/>
<filter string="Availability" domain="[]" context="{'group_by':'availability'}"/>
<filter string="Appreciation" domain="[]" context="{'group_by':'priority'}"/>
<filter string="Last Stage" help="Match this group by with a specific stage filter in order to analyse the recruitment process" context="{'group_by':'last_stage_id'}"/>
<filter string="Stage" domain="[]" context="{'group_by':'stage_id'}"/>
<filter string="Source" domain="[]" context="{'group_by':'source_id'}"/>
<filter string="Creation Month" domain="[]" context="{'group_by':'create_date'}"/>
<filter string="Last Stage Update" context="{'group_by':'date_last_stage_update'}"/>
</group>
</search>
@@ -12,6 +12,29 @@
</field>
</record>
<!-- Custom reports (aka filters) -->
<record id="filter_recruitment_report_recruiter" model="ir.filters">
<field name="name">By Recruiter</field>
<field name="model_id">hr.recruitment.report</field>
<field name="domain">[]</field>
<field name="user_id" eval="False"/>
<field name="context">{'group_by': ['date_create:month', 'user_id']}</field>
</record>
<record id="filter_recruitment_report_job" model="ir.filters">
<field name="name">By Job</field>
<field name="model_id">hr.recruitment.report</field>
<field name="domain">[]</field>
<field name="user_id" eval="False"/>
<field name="context">{'group_by': ['date_create:month', 'job_id']}</field>
</record>
<record id="filter_recruitment_report_departmnet" model="ir.filters">
<field name="name">By Department</field>
<field name="model_id">hr.recruitment.report</field>
<field name="domain">[]</field>
<field name="user_id" eval="False"/>
<field name="context">{'group_by': ['date_create:month', 'department_id']}</field>
</record>
<record id="view_hr_recruitment_report_search" model="ir.ui.view">
<field name="name">hr.recruitment.report.search</field>
<field name="model">hr.recruitment.report</field>
@@ -20,11 +43,13 @@
<field name="job_id"/>
<field name="department_id"/>
<field name="user_id"/>
<filter string="New" domain="[('sequence', '=', 1)]"/>
<filter string="This Year" name="year" domain="[('date_create','&lt;=', time.strftime('%%Y-12-31')),('date_create','&gt;=',time.strftime('%%Y-01-01'))]"/>
<separator/>
<filter string="Unassigned" domain="[('user_id', '=', False)]"/>
<filter string="My" domain="[('user_id', '=', uid)]"/>
<separator/>
<filter string="New" domain="[('sequence', '=', 1)]"/>
<separator/>
<group expand="0" string="Extended Filters...">
<field name="priority"/>
<field name="stage_id"/>
@@ -32,21 +57,6 @@
<field name="date_create"/>
<field name="date_closed"/>
</group>
<group expand="1" string="Group By ...">
<filter string="Responsible" name='User' context="{'group_by':'user_id'}"/>
<filter string="Company" context="{'group_by':'company_id'}" groups="base.group_multi_company"/>
<filter string="Partner" context="{'group_by':'partner_id'}" />
<filter string="Jobs" name="job" context="{'group_by':'job_id'}"/>
<filter string="Department" name="department" context="{'group_by':'department_id'}"/>
<filter string="Degree" name="degree" context="{'group_by':'type_id'}"/>
<filter string="Last Stage" help="Match this group by with a specific stage filter in order to analyse the recruitment process" context="{'group_by':'last_stage_id'}"/>
<filter string="Stage" context="{'group_by':'stage_id'}" />
<filter string="Last Stage Update" context="{'group_by':'date_last_stage_update'}" />
<filter string="Creation Date (day)" context="{'group_by':'date_create:day'}" help="Creation Date"/>
<filter string="Creation Date (week)" context="{'group_by':'date_create:week'}" help="Creation Date"/>
<filter string="Creation Date (month)" context="{'group_by':'date_create:month'}" help="Creation Date"/>
<filter string="Creation Date (year)" context="{'group_by':'date_create:year'}" help="Creation Date"/>
</group>
</search>
</field>
</record>
@@ -56,7 +66,7 @@
<field name="res_model">hr.recruitment.report</field>
<field name="view_type">form</field>
<field name="view_mode">graph</field>
<field name="context">{'group_by_no_leaf':1,'group_by':[]}</field>
<field name="context">{'search_default_year': 1, 'group_by_no_leaf':1,'group_by':[]}</field>
<field name="search_view_id" ref="view_hr_recruitment_report_search"/>
</record>
<menuitem action="action_hr_recruitment_report_all" id="menu_hr_recruitment_report_all" parent="hr.menu_hr_reporting" sequence="0"/>
+13 -19
View File
@@ -18,29 +18,24 @@
<search string="Tasks">
<field name="name" string="Tasks"/>
<field name="categ_ids"/>
<filter string="Unassigned" name="unassigned" domain="[('user_id', '=', False)]"/>
<filter string="New" name="draft" domain="[('stage_id.sequence', '&lt;=', 1)]"/>
<separator/>
<filter string="My Tasks" domain="[('user_id','=',uid)]"/>
<separator/>
<filter string="Unread Messages" name="message_unread" domain="[('message_unread','=',True)]"/>
<separator/>
<filter string="Deadlines" context="{'deadline_visible': False}" domain="[('date_deadline','&lt;&gt;',False)]"
help="Show only tasks having a deadline"/>
<field name="partner_id"/>
<field name="project_id"/>
<field name="reviewer_id"/>
<field name="user_id"/>
<field name="stage_id" domain="[]"/>
<filter string="My Tasks" domain="[('user_id','=',uid)]"/>
<filter string="Unassigned" name="unassigned" domain="[('user_id', '=', False)]"/>
<separator/>
<filter string="New" name="draft" domain="[('stage_id.sequence', '&lt;=', 1)]"/>
<separator/>
<filter string="New Mail" name="message_unread" domain="[('message_unread','=',True)]"/>
<group expand="0" string="Group By">
<filter string="Reviewers" name="group_reviewer_id" domain="[]" context="{'group_by':'reviewer_id'}"/>
<filter string="Users" name="group_user_id" icon="terp-personal" domain="[]" context="{'group_by':'user_id'}"/>
<filter string="Project" name="group_project_id" icon="terp-folder-violet" domain="[]" context="{'group_by':'project_id'}"/>
<filter string="Stage" name="group_stage_id" icon="terp-stage" domain="[]" context="{'group_by':'stage_id'}"/>
<filter string="Last Stage Update" icon="terp-go-month" domain="[]" context="{'group_by':'date_last_stage_update'}"/>
<filter string="Deadline" icon="terp-gnome-cpu-frequency-applet+" domain="[]" context="{'group_by':'date_deadline'}"/>
<filter string="Start Month" icon="terp-go-month" domain="[]" context="{'group_by':'date_start'}"/>
<filter string="End Month" icon="terp-go-month" domain="[]" context="{'group_by':'date_end'}" groups="base.group_no_one"/>
<filter string="Last Message" name="group_message_last_post" domain="[]" context="{'group_by':'message_last_post:week'}"/>
</group>
</search>
</field>
@@ -190,15 +185,14 @@
<field name="arch" type="xml">
<search string="Search Project">
<field name="name" string="Project Name"/>
<filter icon="terp-mail-message-new" string="Unread Messages" name="message_unread" domain="[('message_unread','=',True)]"/>
<filter string="Open" name="Current" domain="[('state', '=','open')]"/>
<filter string="Pending" name="Pending" domain="[('state', '=','pending')]"/>
<filter string="Template" name="Template" domain="[('state', '=','template')]"/>
<separator/>
<filter icon="terp-check" string="Open" name="Current" domain="[('state', '=','open')]" help="Open Projects"/>
<filter icon="gtk-media-pause" string="Pending" name="Pending" domain="[('state', '=','pending')]" help="Pending Projects"/>
<filter icon="gtk-media-pause" string="Template" name="Template" domain="[('state', '=','template')]" help="Templates of Projects"/>
<filter string="Member" domain="['|',('user_id', '=', uid),('members', '=', uid)]"/>
<filter string="Manager" domain="[('user_id','=',uid)]"/>
<separator/>
<filter icon="terp-personal+" string="Member" domain="['|',('user_id', '=', uid),('members', '=', uid)]" help="Projects in which I am a member."/>
<separator/>
<filter string="Project(s) Manager" domain="[('user_id','=',uid)]" help="Projects in which I am a manager" icon="terp-personal"/>
<filter string="New Mail" name="message_unread" domain="[('message_unread','=',True)]"/>
<field name="user_id" string="Project Manager"/>
<field name="partner_id" string="Contact" filter_domain="[('partner_id', 'child_of', self)]"/>
<group expand="0" string="Group By">
+3
View File
@@ -30,6 +30,7 @@ class report_project_task_user(osv.osv):
_columns = {
'name': fields.char('Task Summary', readonly=True),
'user_id': fields.many2one('res.users', 'Assigned To', readonly=True),
'reviewer_id': fields.many2one('res.users', 'Reviewer', readonly=True),
'date_start': fields.date('Assignation Date', readonly=True),
'no_of_days': fields.integer('# of Days', size=128, readonly=True),
'date_end': fields.date('Ending Date', readonly=True),
@@ -71,6 +72,7 @@ class report_project_task_user(osv.osv):
-- sum(cast(to_char(date_trunc('day',t.date_end) - date_trunc('day',t.date_start),'DD') as int)) as no_of_days,
abs((extract('epoch' from (t.write_date-t.date_start)))/(3600*24)) as no_of_days,
t.user_id,
t.reviewer_id,
progress as progress,
t.project_id,
t.effective_hours as hours_effective,
@@ -103,6 +105,7 @@ class report_project_task_user(osv.osv):
date_deadline,
date_last_stage_update,
t.user_id,
t.reviewer_id,
t.project_id,
t.priority,
name,
+19 -14
View File
@@ -21,6 +21,22 @@
</field>
</record>
<!-- Custom reports (aka filters) -->
<record id="filter_task_report_responsible" model="ir.filters">
<field name="name">By Responsible</field>
<field name="model_id">report.project.task.user</field>
<field name="domain">[]</field>
<field name="user_id" eval="False"/>
<field name="context">{'group_by': ['project_id', 'user_id']}</field>
</record>
<record id="filter_task_report_reviewer" model="ir.filters">
<field name="name">By Reviewer</field>
<field name="model_id">report.project.task.user</field>
<field name="domain">[]</field>
<field name="user_id" eval="False"/>
<field name="context">{'group_by': ['project_id', 'reviewer_id']}</field>
</record>
<record id="view_task_project_user_search" model="ir.ui.view">
<field name="name">report.project.task.user.search</field>
<field name="model">report.project.task.user</field>
@@ -34,25 +50,14 @@
<field name="user_id"/>
<field name="partner_id" filter_domain="[('partner_id', 'child_of', self)]"/>
<field name="stage_id"/>
<filter string="Unassigned" name="unassigned" domain="[('user_id','=',False)]"/>
<filter string="New" name="new" domain="[('stage_id.sequence', '=', 1)]"/>
<separator/>
<filter string="My Task" domain="[('user_id','=',uid)]" />
<filter string="Unassigned" name="unassigned" domain="[('user_id','=',False)]"/>
<separator/>
<filter string="New" name="new" domain="[('stage_id.sequence', '&lt;=', 1)]"/>
<group expand="0" string="Extended Filters...">
<field name="priority"/>
<field name="company_id" groups="base.group_multi_company"/>
</group>
<group expand="1" string="Group By">
<filter string="Project" name="project" context="{'group_by':'project_id'}"/>
<filter string="Task" context="{'group_by':'name'}" />
<filter string="Contact" context="{'group_by':'partner_id'}" />
<filter string="Assigned to" name="User" context="{'group_by':'user_id'}" />
<filter string="Company" context="{'group_by':'company_id'}" groups="base.group_multi_company"/>
<filter string="Assignation date (day)" context="{'group_by':'date_start:day'}" help="Creation Date"/>
<filter string="Assignation date (month)" context="{'group_by':'date_start:month'}" help="Creation Date"/>
<filter string="Assignation date (year)" context="{'group_by':'date_start:year'}" help="Creation Date"/>
<filter string="Last Stage Update" context="{'group_by':'date_last_stage_update'}" help="Last Stage Update"/>
</group>
</search>
</field>
</record>
+7 -8
View File
@@ -142,24 +142,23 @@
<field name="name" string="Issue" filter_domain="['|', '|', '|', ('partner_id','child_of',self), ('description','ilike',self),('email_from','ilike',self),('name','ilike',self)]"/>
<field name="id"/>
<field name="partner_id" operator="child_of"/>
<filter string="Unassigned" name="unassigned" domain="[('user_id', '=', False)]"/>
<filter string="New" name="draft" domain="[('stage_id.sequence', '=', 1)]"/>
<separator/>
<filter string="Unread Messages" name="message_unread" domain="[('message_unread','=',True)]"/>
<separator/>
<field name="user_id"/>
<field name="project_id"/>
<field name="categ_ids"/>
<field name="stage_id" domain="[]"/>
<filter string="My Issues" domain="[('user_id','=',uid)]"/>
<filter string="Unassigned" name="unassigned" domain="[('user_id', '=', False)]"/>
<separator/>
<filter string="New" name="draft" domain="[('stage_id.sequence', '=', 1)]"/>
<separator/>
<filter string="New Mail" name="message_unread" domain="[('message_unread','=',True)]"/>
<group expand="0" string="Group By" >
<filter string="Responsible" name="group_user_id" icon="terp-personal" domain="[]" context="{'group_by':'user_id'}"/>
<filter string="Contact" name="group_partner_id" icon="terp-partner" domain="[]" context="{'group_by':'partner_id'}"/>
<filter string="Project" name="group_project_id" icon="terp-folder-violet" domain="[]" context="{'group_by':'project_id'}"/>
<filter string="Version" name="group_version" icon="terp-gtk-jump-to-rtl" domain="[]" context="{'group_by':'version_id'}"/>
<filter string="Priority" name="group_priority" icon="terp-rating-rated" domain="[]" context="{'group_by':'priority'}"/>
<filter string="Stage" name="group_stage_id" icon="terp-stage" domain="[]" context="{'group_by':'stage_id'}"/>
<filter string="Creation Month" name="group_create_date" icon="terp-go-month" domain="[]" context="{'group_by':'create_date'}"/>
<filter string="Last Post (weekly)" name="group_message_last_post" domain="[]" context="{'group_by':'message_last_post:week'}"/>
<filter string="Last Message" name="group_message_last_post" domain="[]" context="{'group_by':'message_last_post:week'}"/>
</group>
</search>
</field>
@@ -13,6 +13,22 @@
</field>
</record>
<!-- Custom reports (aka filters) -->
<record id="filter_issue_report_responsible" model="ir.filters">
<field name="name">By Responsible</field>
<field name="model_id">project.issue.report</field>
<field name="domain">[]</field>
<field name="user_id" eval="False"/>
<field name="context">{'group_by': ['project_id', 'user_id']}</field>
</record>
<record id="filter_issue_report_reviewer" model="ir.filters">
<field name="name">By Reviewer</field>
<field name="model_id">project.issue.report</field>
<field name="domain">[]</field>
<field name="user_id" eval="False"/>
<field name="context">{'group_by': ['project_id', 'reviewer_id']}</field>
</record>
<record id="view_project_issue_report_filter" model="ir.ui.view">
<field name="name">project.issue.report.select</field>
<field name="model">project.issue.report</field>
@@ -24,25 +40,10 @@
<field name="partner_id" filter_domain="[('partner_id', 'child_of', self)]"/>
<field name="version_id"/>
<field name="stage_id"/>
<filter string="My Task" domain="[('user_id','=',uid)]" />
<filter string="Unassigned" name="unassigned" domain="[('user_id','=',False)]"/>
<filter string="New" name="new" domain="[('stage_id.sequence', '=', 1)]"/>
<filter string="Done" name="done" domain="[('stage_id.fold', '=', True)]"
help="Tasks beloging to a folded stage"/>
<group expand="1" string="Group By">
<filter string="Assigned to" name="Responsible" icon="terp-personal" domain="[]" context="{'group_by':'user_id'}" />
<filter string="Contact" icon="terp-partner" context="{'group_by':'partner_id'}" />
<filter string="Sale Team" icon="terp-personal+" domain="[]" context="{'group_by':'section_id'}" groups="base.group_multi_salesteams"/>
<filter string="Project" name="project" icon="terp-folder-violet" context="{'group_by':'project_id'}" />
<filter string="Task" icon="terp-stock_align_left_24" domain="[]" context="{'group_by':'task_id'}"/>
<filter string="Version" icon="terp-stock_symbol-selection" domain="[]" context="{'group_by':'version_id'}"/>
<filter string="Priority" icon="terp-rating-rated" domain="[]" context="{'group_by':'priority'}" />
<filter string="Stage" icon="terp-stage" domain="[]" context="{'group_by':'stage_id'}"/>
<filter string="Company" icon="terp-go-home" domain="[]" context="{'group_by':'company_id'}" groups="base.group_multi_company"/>
<filter string="Day" icon="terp-go-today" domain="[]" context="{'group_by':'day'}" help="Creation Date"/>
<filter string="Month" icon="terp-go-month" domain="[]" context="{'group_by':'month'}" help="Creation Date"/>
<filter string="Year" icon="terp-go-year" domain="[]" context="{'group_by':'name'}" help="Creation Date"/>
<filter string="Last Stage Update" context="{'group_by':'date_last_stage_update'}"/>
</group>
<separator/>
<filter string="New" name="new" domain="[('stage_id.sequence', '&lt;=', 1)]"/>
</search>
</field>
</record>
@@ -41,8 +41,8 @@
<field name="model">project.project</field>
<field name="inherit_id" ref="project.view_project_project_filter"/>
<field name="arch" type="xml">
<xpath expr='//filter[@string="Project(s) Manager"]' position='after'>
<filter icon="terp-camera_test" string="Billable" domain="[('to_invoice','!=', False)]" help="Billable Project"/>
<xpath expr='//filter[@string="Template"]' position='after'>
<filter string="Billable" domain="[('to_invoice','!=', False)]"/>
</xpath>
</field>
</record>
+12 -16
View File
@@ -305,18 +305,16 @@
<field name="arch" type="xml">
<search string="Search Purchase Order">
<field name="name" string="Reference"/>
<filter icon="terp-document-new" name="draft" string="Quotations" domain="[('state','=','draft')]" help="Purchase order which are in draft state"/>
<filter icon="terp-check" name="approved" string="Approved" domain="[('state','in',('approved','done'))]" help="Approved purchase order"/>
<filter icon="terp-emblem-important" name="exception" string="Exception" domain="[('state','in',('except_invoice','except_picking'))]" help="Purchase order which are in the exception state"/>
<separator/>
<filter icon="terp-gtk-go-back-rtl" name="not_invoiced" string="Not Invoiced" domain="[('invoice_ids','=', False)]" help="Purchase orders that include lines not invoiced."/>
<field name="partner_id" operator="child_of"/>
<field name="product_id"/>
<field name="create_uid"/>
<filter name="draft" string="Quotations" domain="[('state','=','draft')]"/>
<filter name="approved" string="Approved" domain="[('state','in',('approved','done'))]"/>
<filter name="exception" string="Exception" domain="[('state','in',('except_invoice','except_picking'))]"/>
<separator/>
<filter name="not_invoiced" string="Not Invoiced" domain="[('invoice_ids','=', False)]" help="Purchase orders that include lines not invoiced."/>
<group expand="0" string="Group By">
<filter string="Supplier" icon="terp-partner" domain="[]" context="{'group_by':'partner_id'}"/>
<filter string="Source" icon="terp-gtk-jump-to-rtl" domain="[]" context="{'group_by':'origin'}"/>
<filter string="Status" icon="terp-stock_effects-object-colorize" domain="[]" context="{'group_by':'state'}"/>
<filter string="Order Date" icon="terp-go-month" domain="[]" context="{'group_by':'date_order'}"/>
<filter string="Expected Date" icon="terp-go-month" domain="[]" context="{'group_by':'minimum_planned_date'}"/>
</group>
@@ -330,20 +328,18 @@
<field name="arch" type="xml">
<search string="Search Purchase Order">
<field name="name" string="Reference"/>
<filter icon="terp-mail-message-new" string="Unread Messages" name="message_unread" domain="[('message_unread','=',True)]"/>
<separator/>
<filter icon="terp-document-new" name="draft" string="Quotations" domain="[('state','in',('draft','sent'))]" help="Purchase orders that haven't yet been confirmed"/>
<filter icon="terp-check" name="approved" string="Purchase Orders" domain="[('state','not in',('draft','cancel'))]" help="Approved purchase orders"/>
<filter icon="terp-emblem-important" name="exception" string="Exception" domain="[('state','in',('except_invoice','except_picking'))]" help="Purchase orders which are in exception state"/>
<separator/>
<filter icon="terp-gtk-go-back-rtl" name="not_invoiced" string="Not Invoiced" domain="[('invoice_ids','=', False)]" help="Purchase orders that include lines not invoiced."/>
<field name="partner_id" operator="child_of"/>
<field name="product_id"/>
<field name="create_uid"/>
<filter name="draft" string="Quotations" domain="[('state','in',('draft','sent'))]"/>
<filter name="approved" string="Purchase Orders" domain="[('state','not in',('draft','cancel'))]"/>
<filter name="exception" string="Exception" domain="[('state','in',('except_invoice','except_picking'))]"/>
<separator/>
<filter name="not_invoiced" string="Not Invoiced" domain="[('invoice_ids','=', False)]"/>
<separator/>
<filter string="New Mail" name="message_unread" domain="[('message_unread','=',True)]"/>
<group expand="0" string="Group By">
<filter string="Supplier" icon="terp-partner" domain="[]" context="{'group_by':'partner_id'}"/>
<filter string="Source" icon="terp-gtk-jump-to-rtl" domain="[]" context="{'group_by':'origin'}"/>
<filter string="Status" icon="terp-stock_effects-object-colorize" domain="[]" context="{'group_by':'state'}"/>
<filter string="Order Month" icon="terp-go-month" domain="[]" context="{'group_by':'date_order'}"/>
<filter string="Expected Month" icon="terp-go-month" domain="[]" context="{'group_by':'minimum_planned_date'}"/>
</group>
@@ -36,22 +36,6 @@
<field name="date_approve"/>
<field name="expected_date"/>
</group>
<newline/>
<group expand="1" string="Group By">
<filter string="Supplier" name="group_partner_id" icon="terp-personal" context="{'group_by':'partner_id'}"/>
<filter string="Responsible" name="Responsible" icon="terp-personal" context="{'group_by':'user_id'}"/>
<filter string="Validated by" icon="terp-personal" context="{'group_by':'validator'}"/>
<filter string="Product" name="group_product_id" icon="terp-accessories-archiver" context="{'group_by':'product_id'}"/>
<filter string="Category" name="group_category_id" icon="terp-stock_symbol-selection" context="{'group_by':'category_id'}"/>
<filter string="Warehouse" icon="terp-go-home" context="{'group_by':'picking_type_id'}"/>
<filter string="Reference UOM" name="group_product_uom" icon="terp-mrp" context="{'group_by':'product_uom'}"/>
<filter string="Destination" icon="terp-gtk-jump-to-ltr" context="{'group_by':'location_id'}"/>
<filter string="Status" icon="terp-stock_effects-object-colorize" context="{'group_by':'state'}"/>
<filter string="Company" icon="terp-go-home" context="{'group_by':'company_id'}" groups="base.group_multi_company"/>
<filter string="Order Date (day)" icon="terp-go-today" context="{'group_by':'date:day'}" help="Order of Day"/>
<filter string="Order Date (month)" icon="terp-go-month" context="{'group_by':'date:month'}" help="Order of Day"/>
<filter string="Order Date (year)" icon="terp-go-year" context="{'group_by':'date:year'}" help="Order of Day"/>
</group>
</search>
</field>
</record>
+29 -18
View File
@@ -14,6 +14,29 @@
</field>
</record>
<!-- Custom reports (aka filters) -->
<record id="filter_sale_report_salespersons" model="ir.filters">
<field name="name">By Salespersons</field>
<field name="model_id">sale.report</field>
<field name="domain">[]</field>
<field name="user_id" eval="False"/>
<field name="context">{'group_by': ['date:month', 'user_id']}</field>
</record>
<record id="filter_sale_report_salesteam" model="ir.filters">
<field name="name">By Salesteam</field>
<field name="model_id">sale.report</field>
<field name="domain">[]</field>
<field name="user_id" eval="False"/>
<field name="context">{'group_by': ['date:month', 'section_id']}</field>
</record>
<record id="filter_isale_report_product" model="ir.filters">
<field name="name">By Product</field>
<field name="model_id">sale.report</field>
<field name="domain">[]</field>
<field name="user_id" eval="False"/>
<field name="context">{'group_by': ['date:month', 'product_id']}</field>
</record>
<record id="view_order_product_search" model="ir.ui.view">
<field name="name">sale.report.search</field>
<field name="model">sale.report</field>
@@ -21,10 +44,12 @@
<search string="Sales Analysis">
<field name="date"/>
<field name="date_confirm"/>
<filter icon="terp-document-new" name="Quotations" domain="[('state','in',('draft','sent'))]"/>
<filter icon="terp-check" name="Sales" string="Sales" domain="[('state','not in',('draft','sent','cancel'))]"/>
<filter string="This Year" name="year" domain="[('date','&lt;=', time.strftime('%%Y-12-31')),('date','&gt;=',time.strftime('%%Y-01-01'))]"/>
<separator/>
<filter icon="terp-personal" string="My Sales" help="My Sales" domain="[('user_id','=',uid)]"/>
<filter name="Quotations" domain="[('state','in',('draft','sent'))]"/>
<filter name="Sales" string="Sales" domain="[('state','not in',('draft','sent','cancel'))]"/>
<separator/>
<filter string="My Sales" help="My Sales" domain="[('user_id','=',uid)]"/>
<field name="partner_id"/>
<field name="product_id"/>
<field name="user_id"/>
@@ -32,20 +57,6 @@
<field name="categ_id"/>
<field name="company_id" groups="base.group_multi_company"/>
</group>
<group expand="1" string="Group By">
<filter string="Salesperson" icon="terp-personal" name="User" context="{'group_by':'user_id'}"/>
<filter string="Sales Team" context="{'group_by':'section_id'}" groups="base.group_multi_salesteams"/>
<filter string="Partner" icon="terp-partner" name="Customer" context="{'group_by':'partner_id'}"/>
<filter string="Product" icon="terp-accessories-archiver" context="{'group_by':'product_id','set_visible':True}"/>
<filter string="Reference Unit of Measure" icon="terp-mrp" context="{'group_by':'product_uom'}"/>
<filter string="Category of Product" icon="terp-stock_symbol-selection" name="Category" context="{'group_by':'categ_id'}"/>
<filter string="Analytic Account" icon="terp-folder-green" context="{'group_by':'analytic_account_id'}" groups="analytic.group_analytic_accounting"/>
<filter string="Status" icon="terp-stock_effects-object-colorize" context="{'group_by':'state'}"/>
<filter string="Company" icon="terp-go-home" groups="base.group_multi_company" context="{'group_by':'company_id'}"/>
<filter string="Order Date (day)" icon="terp-go-today" context="{'group_by':'date:day'}" help="Ordered date of the sales order"/>
<filter string="Order Date (month)" icon="terp-go-today" context="{'group_by':'date:month'}" help="Ordered date of the sales order"/>
<filter string="Order Date (year)" icon="terp-go-today" context="{'group_by':'date:year'}" help="Ordered date of the sales order"/>
</group>
</search>
</field>
</record>
@@ -57,7 +68,7 @@
<field name="view_mode">graph</field>
<field name="search_view_id" ref="view_order_product_search"/>
<field name="view_id" ref="view_order_product_graph"/>
<field name="context">{'search_default_Sales':1, 'group_by_no_leaf':1,'group_by':[]}</field>
<field name="context">{'search_default_Sales':1, 'search_default_year': 1, 'group_by_no_leaf':1,'group_by':[]}</field>
<field name="help">This report performs analysis on your quotations and sales orders. Analysis check your sales revenues and sort it by different group criteria (salesman, partner, product, etc.) Use this report to perform analysis on sales not having invoiced yet. If you want to analyse your turnover, you should use the Invoice Analysis report in the Accounting application.</field>
</record>
+11 -13
View File
@@ -246,24 +246,22 @@
<field name="arch" type="xml">
<search string="Search Sales Order">
<field name="name" string="Sales Order" filter_domain="['|',('name','ilike',self),('client_order_ref','ilike',self)]"/>
<filter icon="terp-mail-message-new" string="Unread Messages" name="message_unread" domain="[('message_unread','=',True)]"/>
<separator/>
<filter icon="terp-document-new" string="Quotations" name="draft" domain="[('state','in',('draft','sent'))]" help="Sales Order that haven't yet been confirmed"/>
<filter icon="terp-check" string="Sales" name="sales" domain="[('state','in',('manual','progress'))]"/>
<filter icon="terp-dolar_ok!" string="To Invoice" domain="[('state','=','manual')]" help="Sales Order ready to be invoiced"/>
<filter icon="terp-dolar_ok!" string="Done" domain="[('state','=','done')]" help="Sales Order done"/>
<separator/>
<filter string="My" domain="[('user_id','=',uid)]" help="My Sales Orders" icon="terp-personal" name="my_sale_orders_filter"/>
<field name="partner_id" operator="child_of"/>
<field name="user_id"/>
<field name="section_id" string="Sales Team" groups="base.group_multi_salesteams"/>
<field name="project_id"/>
<filter string="My" domain="[('user_id','=',uid)]" name="my_sale_orders_filter"/>
<separator/>
<filter string="Quotations" name="draft" domain="[('state','in',('draft','sent'))]" help="Sales Order that haven't yet been confirmed"/>
<filter string="Sales" name="sales" domain="[('state','in',('manual','progress'))]"/>
<filter string="To Invoice" domain="[('state','=','manual')]" help="Sales Order ready to be invoiced"/>
<filter string="Done" domain="[('state','=','done')]" help="Sales Order done"/>
<separator/>
<filter string="New Mail" name="message_unread" domain="[('message_unread','=',True)]"/>
<group expand="0" string="Group By">
<filter string="Customer" icon="terp-personal" domain="[]" context="{'group_by':'partner_id'}"/>
<filter string="Salesperson" icon="terp-personal" domain="[]" context="{'group_by':'user_id'}"/>
<filter string="Status" icon="terp-stock_effects-object-colorize" domain="[]" context="{'group_by':'state'}"/>
<filter string="Order Month" icon="terp-go-month" domain="[]" context="{'group_by':'date_order'}"/>
<filter string="My Sales Team(s)" icon="terp-personal+" domain="[('section_id.user_id','=',uid)]" help="My Sales Team(s)" groups="base.group_multi_salesteams"/>
<filter string="Salesperson" domain="[]" context="{'group_by':'user_id'}"/>
<filter string="Customer" domain="[]" context="{'group_by':'partner_id'}"/>
<filter string="Order Month" domain="[]" context="{'group_by':'date_order'}"/>
</group>
</search>
</field>
@@ -10,9 +10,6 @@
<separator/>
<filter icon="terp-accessories-archiver" string="Picked" domain="[('shipped','=',True)]"/>
</filter>
<xpath expr="//group/filter[@string='Status']" position="after">
<filter string="Warehouse" context="{'group_by':'warehouse_id'}"/>
</xpath>
</field>
</record>
</data>
+19 -22
View File
@@ -49,6 +49,9 @@ else:
env = jinja2.Environment(loader=loader, autoescape=True)
env.filters["json"] = simplejson.dumps
# 1 week cache for asset bundles as advised by Google Page Speed
BUNDLE_MAXAGE = 60 * 60 * 24 * 7
#----------------------------------------------------------
# OpenERP Web helpers
#----------------------------------------------------------
@@ -551,37 +554,31 @@ class Home(http.Controller):
def login(self, db, login, key, redirect="/web", **kw):
return login_and_redirect(db, login, key, redirect_url=redirect)
@http.route('/web/js/<xmlid>', type='http', auth="public")
def js_bundle(self, xmlid, **kw):
# manifest backward compatible mode, to be removed
values = {'manifest_list': manifest_list}
@http.route([
'/web/js/<xmlid>',
'/web/js/<xmlid>/<version>',
], type='http', auth='public')
def js_bundle(self, xmlid, version=None, **kw):
try:
assets_html = request.render(xmlid, lazy=False, qcontext=values)
bundle = AssetsBundle(xmlid)
except QWebTemplateNotFound:
return request.not_found()
bundle = AssetsBundle(xmlid, assets_html, debug=request.debug)
response = request.make_response(
bundle.js(), [('Content-Type', 'application/javascript')])
response = request.make_response(bundle.js(), [('Content-Type', 'application/javascript')])
return make_conditional(response, bundle.last_modified, max_age=BUNDLE_MAXAGE)
# TODO: check that we don't do weird lazy overriding of __call__ which break body-removal
return make_conditional(
response, bundle.last_modified, bundle.checksum, max_age=60*60*24)
@http.route('/web/css/<xmlid>', type='http', auth='public')
def css_bundle(self, xmlid, **kw):
values = {'manifest_list': manifest_list} # manifest backward compatible mode, to be removed
@http.route([
'/web/css/<xmlid>',
'/web/css/<xmlid>/<version>',
], type='http', auth='public')
def css_bundle(self, xmlid, version=None, **kw):
try:
assets_html = request.render(xmlid, lazy=False, qcontext=values)
bundle = AssetsBundle(xmlid)
except QWebTemplateNotFound:
return request.not_found()
bundle = AssetsBundle(xmlid, assets_html, debug=request.debug)
response = request.make_response(
bundle.css(), [('Content-Type', 'text/css')])
return make_conditional(
response, bundle.last_modified, bundle.checksum, max_age=60*60*24)
response = request.make_response(bundle.css(), [('Content-Type', 'text/css')])
return make_conditional(response, bundle.last_modified, max_age=BUNDLE_MAXAGE)
class WebClient(http.Controller):
+16 -2
View File
@@ -75,15 +75,22 @@ var Tour = {
if (!tour) {
Tour.error(null, "Can't run '"+tour_id+"' (tour undefined)");
}
console.log("Tour '"+tour_id+"' Begin from run method");
var state = Tour.getState();
if (state) {
if (state.mode === "test") {
Tour.error(false, "An other running tour has been detected all tours are now killed.");
} else {
Tour.endTour();
}
}
this.time = new Date().getTime();
if (tour.path && !window.location.href.match(new RegExp("("+Tour.getLang()+")?"+tour.path+"#?$", "i"))) {
var href = Tour.getLang()+tour.path;
console.log("Tour '"+tour_id+"' Begin from run method (redirection to "+href+")");
Tour.saveState(tour.id, mode || tour.mode, -1, 0);
$(document).one("ajaxStop", Tour.running);
window.location.href = href;
} else {
console.log("Tour '"+tour_id+"' Begin from run method");
Tour.saveState(tour.id, mode || tour.mode, 0, 0);
Tour.running();
}
@@ -358,6 +365,7 @@ var Tour = {
clearTimeout(Tour.timer);
clearTimeout(Tour.testtimer);
Tour.closePopover();
console.log("Tour reset");
},
running: function () {
var state = Tour.getState();
@@ -398,6 +406,8 @@ var Tour = {
};
function checkNext () {
if (!Tour.getState()) return;
Tour.autoTogglePopover();
clearTimeout(Tour.timer);
@@ -476,6 +486,8 @@ var Tour = {
clearTimeout(Tour.testtimer);
function autoStep () {
if (!Tour.getState()) return;
if (!step) return;
if (step.autoComplete) {
@@ -502,6 +514,7 @@ var Tour = {
// trigger after for step like: mouseenter, next step click on button display with mouseenter
setTimeout(function () {
if (!Tour.getState()) return;
$element.trigger($.Event("mouseup", { srcElement: $element[0] }));
$element.trigger($.Event("mouseleave", { srcElement: $element[0] }));
}, 1000);
@@ -518,6 +531,7 @@ var Tour = {
$element.html(step.sampleText);
}
setTimeout(function () {
if (!Tour.getState()) return;
$element.trigger($.Event("keyup", { srcElement: $element }));
$element.trigger($.Event("change", { srcElement: $element }));
}, self.defaultDelay<<1);
+3 -3
View File
@@ -23,9 +23,6 @@
<script type="text/javascript" src="/web/static/lib/qweb/qweb2.js"></script>
<script type="text/javascript" src="/web/static/src/js/openerpframework.js"></script>
<script type="text/javascript" src="/web/static/src/js/tour.js"></script>
<script type="text/javascript" charset="utf-8">
openerp._modules = <t t-raw="modules"/>;
</script>
<link rel="stylesheet" href="/web/static/lib/fontawesome/css/font-awesome.css"/>
</template>
@@ -85,6 +82,9 @@
<script type="text/javascript" src="/web/static/lib/backbone/backbone.js"></script>
<!-- Internals -->
<script type="text/javascript" charset="utf-8">
openerp._modules = <t t-raw="get_modules_order()"/>;
</script>
<link rel="stylesheet" href="/web/static/src/css/base.css"/>
<link rel="stylesheet" href="/web/static/src/css/data_export.css"/>
<link rel="stylesheet" href="/base/static/src/css/modules.css"/>
+1 -1
View File
@@ -11,6 +11,6 @@ class TestUi(openerp.tests.HttpCase):
def test_03_js_public(self):
self.phantom_js('/',"console.log('ok')","console")
def test_04_js_admin(self):
self.phantom_js('/web',"console.log('ok')","openerp.client.action_manager.inner_widget.views.form", login='admin')
self.phantom_js('/web',"console.log('ok')","openerp.client.action_manager.inner_widget", login='admin')
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
@@ -12,7 +12,6 @@ window.openerp.website.EditorBar.include({
}
var $menuItem = $($.parseHTML('<li><a href="#">'+tour.name+'</a></li>'));
$menuItem.click(function () {
openerp.Tour.reset();
openerp.Tour.run(tour.id);
});
menu.append($menuItem);
+2
View File
@@ -278,6 +278,7 @@ class Post(osv.Model):
'closed_date': fields.datetime('Closed on', readonly=True),
# karma
'karma_ask': fields.function(_get_post_karma_rights, string='Karma to ask', type='integer', multi='_get_post_karma_rights'),
'karma_answer': fields.function(_get_post_karma_rights, string='Karma to answer', type='integer', multi='_get_post_karma_rights'),
'karma_accept': fields.function(_get_post_karma_rights, string='Karma to accept this answer', type='integer', multi='_get_post_karma_rights'),
'karma_edit': fields.function(_get_post_karma_rights, string='Karma to edit', type='integer', multi='_get_post_karma_rights'),
'karma_close': fields.function(_get_post_karma_rights, string='Karma to close', type='integer', multi='_get_post_karma_rights'),
@@ -288,6 +289,7 @@ class Post(osv.Model):
'karma_comment_convert': fields.function(_get_post_karma_rights, string='karma to convert as a comment', type='integer', multi='_get_post_karma_rights'),
# access rights
'can_ask': fields.function(_get_post_karma_rights, string='Can Ask', type='boolean', multi='_get_post_karma_rights'),
'can_answer': fields.function(_get_post_karma_rights, string='Can Answer', type='boolean', multi='_get_post_karma_rights'),
'can_accept': fields.function(_get_post_karma_rights, string='Can Accept', type='boolean', multi='_get_post_karma_rights'),
'can_edit': fields.function(_get_post_karma_rights, string='Can Edit', type='boolean', multi='_get_post_karma_rights'),
'can_close': fields.function(_get_post_karma_rights, string='Can Close', type='boolean', multi='_get_post_karma_rights'),
+6 -3
View File
@@ -592,9 +592,12 @@
</a>
</li>
<li>
<a t-attf-class="fa fa-edit #{not answer.can_edit and 'karma_required text-muted' or ''}"
t-attf-data-karma="#{not answer.can_edit and answer.karma_edit or 0}"
t-attf-href="/forum/#{slug(forum)}/post/#{slug(answer)}/edit"> Edit</a>
<t t-call="website_forum.link_button">
<t t-set="url" t-value="'/forum/' + slug(forum) + '/post/' + slug(answer) + '/edit'"/>
<t t-set="label" t-value="'Edit'"/>
<t t-set="classes" t-value="'fa fa-edit'"/>
<t t-set="karma" t-value="not answer.can_edit and answer.karma_edit or 0"/>
</t>
</li>
<li>
<t t-call="website_forum.link_button">
+314 -98
View File
@@ -9,9 +9,13 @@ import math
import os
import re
import sys
import textwrap
import uuid
import xml # FIXME use lxml and etree
import itertools
import lxml.html
import werkzeug
from subprocess import Popen, PIPE
from urlparse import urlparse
import babel
@@ -20,14 +24,13 @@ from PIL import Image
import openerp.http
import openerp.tools
import openerp.tools.func
from openerp.tools.func import lazy_property
import openerp.tools.lru
from openerp.http import request
from openerp.tools.safe_eval import safe_eval as eval
from openerp.osv import osv, orm, fields
from openerp.tools import html_escape as escape
from openerp.tools.translate import _
from openerp import SUPERUSER_ID
_logger = logging.getLogger(__name__)
@@ -414,16 +417,15 @@ class QWeb(orm.AbstractModel):
def render_tag_call_assets(self, element, template_attributes, generated_attributes, qwebcontext):
""" This special 't-call' tag can be used in order to aggregate/minify javascript and css assets"""
name = template_attributes['call-assets']
# Backward compatibility hack for manifest usage
qwebcontext['manifest_list'] = openerp.addons.web.controllers.main.manifest_list
d = qwebcontext.copy()
d.context['inherit_branding'] = False
content = self.render_tag_call(
element, {'call': name}, generated_attributes, d)
bundle = AssetsBundle(name, html=content)
if element.childNodes:
# An asset bundle is rendered in two differents contexts (when genereting html and
# when generating the bundle itself) so they must be qwebcontext free
# even '0' variable is forbidden
template = qwebcontext.get('__template__')
raise QWebException("t-call-assets cannot contain children nodes", template=template)
xmlid = template_attributes['call-assets']
cr, uid, context = [getattr(qwebcontext, attr) for attr in ('cr', 'uid', 'context')]
bundle = AssetsBundle(xmlid, cr=cr, uid=uid, context=context, registry=self.pool)
css = self.get_attr_bool(template_attributes.get('css'), default=True)
js = self.get_attr_bool(template_attributes.get('js'), default=True)
return bundle.to_html(css=css, js=js, debug=bool(qwebcontext.get('debug')))
@@ -999,36 +1001,67 @@ def get_field_type(column, options):
"""
return options.get('widget', column._type)
class AssetError(Exception):
pass
class AssetNotFound(AssetError):
pass
class AssetsBundle(object):
# Sass installation:
#
# sudo gem install sass compass bootstrap-sass
#
# If the following error is encountered:
# 'ERROR: Cannot load compass.'
# Use this:
# sudo gem install compass --pre
cmd_sass = ['sass', '--stdin', '-t', 'compressed', '--unix-newlines', '--compass', '-r', 'bootstrap-sass']
cache = openerp.tools.lru.LRU(32)
rx_css_import = re.compile("(@import[^;{]+;?)", re.M)
rx_sass_import = re.compile("""(@import\s?['"]([^'"]+)['"])""")
rx_css_split = re.compile("\/\*\! ([a-f0-9-]+) \*\/")
def __init__(self, xmlid, html=None, debug=False):
self.debug = debug
def __init__(self, xmlid, debug=False, cr=None, uid=None, context=None, registry=None):
self.xmlid = xmlid
self.cr = request.cr if cr is None else cr
self.uid = request.uid if uid is None else uid
self.context = request.context if context is None else context
self.registry = request.registry if registry is None else registry
self.javascripts = []
self.stylesheets = []
self.css_errors = []
self.remains = []
self._checksum = None
if html:
self.parse(html)
def parse(self, html):
fragments = lxml.html.fragments_fromstring(html)
context = self.context.copy()
context['inherit_branding'] = False
self.html = self.registry['ir.ui.view'].render(self.cr, self.uid, xmlid, context=context)
self.parse()
def parse(self):
fragments = lxml.html.fragments_fromstring(self.html)
for el in fragments:
if isinstance(el, basestring):
self.remains.append(el)
elif isinstance(el, lxml.html.HtmlElement):
src = el.get('src')
href = el.get('href')
src = el.get('src', '')
href = el.get('href', '')
atype = el.get('type')
media = el.get('media')
if el.tag == 'style':
self.stylesheets.append(StylesheetAsset(source=el.text))
if atype == 'text/sass' or src.endswith('.sass'):
self.stylesheets.append(SassAsset(self, inline=el.text, media=media))
else:
self.stylesheets.append(StylesheetAsset(self, inline=el.text, media=media))
elif el.tag == 'link' and el.get('rel') == 'stylesheet' and self.can_aggregate(href):
self.stylesheets.append(StylesheetAsset(url=href))
if href.endswith('.sass') or atype == 'text/sass':
self.stylesheets.append(SassAsset(self, url=href, media=media))
else:
self.stylesheets.append(StylesheetAsset(self, url=href, media=media))
elif el.tag == 'script' and not src:
self.javascripts.append(JavascriptAsset(source=el.text))
self.javascripts.append(JavascriptAsset(self, inline=el.text))
elif el.tag == 'script' and self.can_aggregate(src):
self.javascripts.append(JavascriptAsset(url=src))
self.javascripts.append(JavascriptAsset(self, url=src))
else:
self.remains.append(lxml.html.tostring(el))
else:
@@ -1041,10 +1074,13 @@ class AssetsBundle(object):
def can_aggregate(self, url):
return not urlparse(url).netloc and not url.startswith(('/web/css', '/web/js'))
def to_html(self, sep='\n ', css=True, js=True, debug=False):
def to_html(self, sep=None, css=True, js=True, debug=False):
if sep is None:
sep = '\n '
response = []
if debug:
if css:
if css and self.stylesheets:
self.compile_sass()
for style in self.stylesheets:
response.append(style.to_html())
if js:
@@ -1052,41 +1088,56 @@ class AssetsBundle(object):
response.append(jscript.to_html())
else:
if css and self.stylesheets:
response.append('<link href="/web/css/%s" rel="stylesheet"/>' % self.xmlid)
if js and self.javascripts:
response.append('<script type="text/javascript" src="/web/js/%s"></script>' % self.xmlid)
response.append('<link href="/web/css/%s/%s" rel="stylesheet"/>' % (self.xmlid, self.version))
if js:
response.append('<script type="text/javascript" src="/web/js/%s/%s"></script>' % (self.xmlid, self.version))
response.extend(self.remains)
return sep + sep.join(response)
@openerp.tools.func.lazy_property
@lazy_property
def last_modified(self):
"""Returns last modified date of linked files"""
return max(itertools.chain(
(asset.last_modified for asset in self.javascripts),
(asset.last_modified for asset in self.stylesheets),
[datetime.datetime(1970, 1, 1)],
))
@openerp.tools.func.lazy_property
@lazy_property
def version(self):
return self.checksum[0:7]
@lazy_property
def checksum(self):
checksum = hashlib.new('sha1')
for asset in itertools.chain(self.javascripts, self.stylesheets):
checksum.update(asset.content.encode("utf-8"))
return checksum.hexdigest()
"""
Not really a full checksum.
We compute a SHA1 on the rendered bundle + max linked files last_modified date
"""
check = self.html + str(self.last_modified)
return hashlib.sha1(check).hexdigest()
def js(self):
key = 'js_' + self.checksum
key = 'js_%s' % self.xmlid
if key in self.cache and self.cache[key][0] != self.version:
# Invalidate cache on version mismach
self.cache.pop(key)
if key not in self.cache:
content =';\n'.join(asset.minify() for asset in self.javascripts)
self.cache[key] = content
if self.debug:
return "/*\n%s\n*/\n" % '\n'.join(
[asset.url for asset in self.javascripts if asset.url]) + self.cache[key]
return self.cache[key]
self.cache[key] = (self.version, content)
return self.cache[key][1]
def css(self):
key = 'css_' + self.checksum
key = 'css_%s' % self.xmlid
if key in self.cache and self.cache[key][0] != self.version:
# Invalidate cache on version mismach
self.cache.pop(key)
if key not in self.cache:
self.compile_sass()
content = '\n'.join(asset.minify() for asset in self.stylesheets)
if self.css_errors:
msg = '\n'.join(self.css_errors)
content += self.css_message(msg.replace('\n', '\\A '))
# move up all @import rules to the top
matches = []
def push(matchobj):
@@ -1097,89 +1148,200 @@ class AssetsBundle(object):
matches.append(content)
content = u'\n'.join(matches)
self.cache[key] = content
if self.debug:
return "/*\n%s\n*/\n" % '\n'.join(
[asset.url for asset in self.javascripts if asset.url]) + self.cache[key]
return self.cache[key]
if self.css_errors:
return content
self.cache[key] = (self.version, content)
return self.cache[key][1]
def css_message(self, message):
return """
body:before {
background: #ffc;
width: 100%%;
font-size: 14px;
font-family: monospace;
white-space: pre;
content: "%s";
}
""" % message.replace('"', '\\"')
def compile_sass(self):
"""
Checks if the bundle contains any sass content, then compiles it to css.
Css compilation is done at the bundle level and not in the assets
because they are potentially interdependant.
"""
sass = [asset for asset in self.stylesheets if isinstance(asset, SassAsset)]
if not sass:
return
source = '\n'.join([asset.get_source() for asset in sass])
# move up all @import rules to the top and exclude file imports
imports = []
def push(matchobj):
ref = matchobj.group(2)
line = '@import "%s"' % ref
if '.' not in ref and line not in imports and not ref.startswith(('.', '/', '~')):
imports.append(line)
return ''
source = re.sub(self.rx_sass_import, push, source)
imports.append(source)
source = u'\n'.join(imports)
try:
compiler = Popen(self.cmd_sass, stdin=PIPE, stdout=PIPE, stderr=PIPE)
except Exception:
msg = "Could not find 'sass' program needed to compile sass/scss files"
_logger.error(msg)
self.css_errors.append(msg)
return
result = compiler.communicate(input=source.encode('utf-8'))
if compiler.returncode:
error = self.get_sass_error(result[1], source=source)
_logger.warning(error)
self.css_errors.append(error)
return
compiled = result[0].strip().decode('utf8')
fragments = self.rx_css_split.split(compiled)[1:]
while fragments:
asset_id = fragments.pop(0)
asset = next(asset for asset in sass if asset.id == asset_id)
asset._content = fragments.pop(0)
def get_sass_error(self, stderr, source=None):
# TODO: try to find out which asset the error belongs to
error = stderr.split('Load paths')[0].replace(' Use --trace for backtrace.', '')
error += "This error occured while compiling the bundle '%s' containing:" % self.xmlid
for asset in self.stylesheets:
if isinstance(asset, SassAsset):
error += '\n - %s' % (asset.url if asset.url else '<inline sass>')
return error
class WebAsset(object):
def __init__(self, source=None, url=None):
self.source = source
html_url = '%s'
def __init__(self, bundle, inline=None, url=None):
self.id = str(uuid.uuid4())
self.bundle = bundle
self.inline = inline
self.url = url
self._irattach = None
self.cr = bundle.cr
self.uid = bundle.uid
self.registry = bundle.registry
self.context = bundle.context
self._content = None
self.filename = None
self.last_modified = None
if source:
self.last_modified = datetime.datetime(1970, 1, 1)
if url:
module = filter(None, self.url.split('/'))[0]
self._filename = None
self._ir_attach = None
name = '<inline asset>' if inline else url
self.name = "%s defined in bundle '%s'" % (name, bundle.xmlid)
if not inline and not url:
raise Exception("An asset should either be inlined or url linked")
def stat(self):
if not (self.inline or self._filename or self._ir_attach):
addon = filter(None, self.url.split('/'))[0]
try:
# Test url against modules static assets
mpath = openerp.http.addons_manifest[module]['addons_path']
self.filename = mpath + self.url.replace('/', os.path.sep)
self.last_modified = datetime.datetime.fromtimestamp(os.path.getmtime(self.filename))
mpath = openerp.http.addons_manifest[addon]['addons_path']
self._filename = mpath + self.url.replace('/', os.path.sep)
except Exception:
try:
# Test url against ir.attachments
fields = ['__last_update', 'datas', 'mimetype']
domain = [('type', '=', 'binary'), ('url', '=', self.url)]
attach = request.registry['ir.attachment'].search_read(request.cr, SUPERUSER_ID, domain, ['__last_update', 'datas', 'mimetype'], context=request.context)
self._irattach = attach[0]
server_format = openerp.tools.misc.DEFAULT_SERVER_DATETIME_FORMAT
try:
self.last_modified = datetime.datetime.strptime(attach[0]['__last_update'], server_format + '.%f')
except ValueError:
self.last_modified = datetime.datetime.strptime(attach[0]['__last_update'], server_format)
ira = self.registry['ir.attachment']
attach = ira.search_read(self.cr, self.uid, domain, fields, context=self.context)
self._ir_attach = attach[0]
except Exception:
raise KeyError("Could not find asset '%s' for '%s' addon" % (self.url, module))
raise AssetNotFound("Could not find %s" % self.name)
@openerp.tools.func.lazy_property
def to_html():
raise NotImplementedError()
@lazy_property
def last_modified(self):
try:
self.stat()
if self._filename:
return datetime.datetime.fromtimestamp(os.path.getmtime(self._filename))
elif self._ir_attach:
server_format = openerp.tools.misc.DEFAULT_SERVER_DATETIME_FORMAT
last_update = self._ir_attach['__last_update']
try:
return datetime.datetime.strptime(last_update, server_format + '.%f')
except ValueError:
return datetime.datetime.strptime(last_update, server_format)
except Exception:
pass
return datetime.datetime(1970, 1, 1)
@property
def content(self):
if self.source:
return self.source
if self._irattach:
return self._irattach['datas'].decode('base64')
return self.get_content()
if not self._content:
self._content = self.inline or self._fetch_content()
return self._content
def get_content(self):
with open(self.filename, 'rb') as fp:
return fp.read().decode('utf-8')
def _fetch_content(self):
""" Fetch content from file or database"""
try:
self.stat()
if self._filename:
with open(self._filename, 'rb') as fp:
return fp.read().decode('utf-8')
else:
return self._ir_attach['datas'].decode('base64')
except UnicodeDecodeError:
raise AssetError('%s is not utf-8 encoded.' % self.name)
except IOError:
raise AssetNotFound('File %s does not exist.' % self.name)
except:
raise AssetError('Could not get content for %s.' % self.name)
def minify(self):
return self.content
def with_header(self, content=None):
if content is None:
content = self.content
return '\n/* %s */\n%s' % (self.name, content)
class JavascriptAsset(WebAsset):
def minify(self):
return rjsmin(self.content)
return self.with_header(rjsmin(self.content))
def _fetch_content(self):
try:
return super(JavascriptAsset, self)._fetch_content()
except AssetError, e:
return "console.error(%s);" % json.dumps(e.message)
def to_html(self):
if self.url:
return '<script type="text/javascript" src="%s"></script>' % self.url
return '<script type="text/javascript" src="%s"></script>' % (self.html_url % self.url)
else:
return '<script type="text/javascript" charset="utf-8">%s</script>' % self.source
return '<script type="text/javascript" charset="utf-8">%s</script>' % self.with_header()
class StylesheetAsset(WebAsset):
rx_import = re.compile(r"""@import\s+('|")(?!'|"|/|https?://)""", re.U)
rx_url = re.compile(r"""url\s*\(\s*('|"|)(?!'|"|/|https?://|data:)""", re.U)
rx_sourceMap = re.compile(r'(/\*# sourceMappingURL=.*)', re.U)
rx_charset = re.compile(r'(@charset "[^"]+";)', re.U)
def _get_content(self):
with open(self.filename, 'rb') as fp:
firstline = fp.readline()
m = re.match(r'@charset "([^"]+)";', firstline)
if m:
encoding = m.group(1)
else:
encoding = "utf-8"
# "reinject" first line as it's not @charset
fp.seek(0)
def __init__(self, *args, **kw):
self.media = kw.pop('media', None)
super(StylesheetAsset, self).__init__(*args, **kw)
return fp.read().decode(encoding)
@property
def content(self):
content = super(StylesheetAsset, self).content
if self.media:
content = '@media %s { %s }' % (self.media, content)
return content
def get_content(self):
content = self._get_content()
if self.url:
def _fetch_content(self):
try:
content = super(StylesheetAsset, self)._fetch_content()
web_dir = os.path.dirname(self.url)
content = self.rx_import.sub(
@@ -1191,6 +1353,12 @@ class StylesheetAsset(WebAsset):
r"url(\1%s/" % (web_dir,),
content,
)
# remove charset declarations, we only support utf-8
content = self.rx_charset.sub('', content)
except AssetError, e:
self.bundle.css_errors.append(e.message)
return ''
return content
def minify(self):
@@ -1201,13 +1369,61 @@ class StylesheetAsset(WebAsset):
# space
content = re.sub(r'\s+', ' ', content)
content = re.sub(r' *([{}]) *', r'\1', content)
return content
return self.with_header(content)
def to_html(self):
media = (' media="%s"' % werkzeug.utils.escape(self.media)) if self.media else ''
if self.url:
href = self.html_url % self.url
return '<link rel="stylesheet" href="%s" type="text/css"%s/>' % (href, media)
else:
return '<style type="text/css"%s>%s</style>' % (media, self.with_header())
class SassAsset(StylesheetAsset):
html_url = '%s.css'
rx_indent = re.compile(r'^( +|\t+)', re.M)
indent = None
reindent = ' '
def minify(self):
return self.with_header()
def to_html(self):
if self.url:
return '<link rel="stylesheet" href="%s" type="text/css"/>' % self.url
else:
return '<style type="text/css">%s</style>' % self.source
ira = self.registry['ir.attachment']
url = self.html_url % self.url
domain = [('type', '=', 'binary'), ('url', '=', self.url)]
ira_id = ira.search(self.cr, self.uid, domain, context=self.context)
if ira_id:
# TODO: update only if needed
ira.write(self.cr, openerp.SUPERUSER_ID, [ira_id], {'datas': self.content}, context=self.context)
else:
ira.create(self.cr, openerp.SUPERUSER_ID, dict(
datas=self.content.encode('utf8').encode('base64'),
mimetype='text/css',
type='binary',
name=url,
url=url,
), context=self.context)
return super(SassAsset, self).to_html()
def get_source(self):
content = textwrap.dedent(self.inline or self._fetch_content())
def fix_indent(m):
ind = m.group()
if self.indent is None:
self.indent = ind
if self.indent == self.reindent:
# Don't reindent the file if identation is the final one (reindent)
raise StopIteration()
return ind.replace(self.indent, self.reindent)
try:
content = self.rx_indent.sub(fix_indent, content)
except StopIteration:
pass
return "/*! %s */\n%s" % (self.id, content)
def rjsmin(script):
""" Minify js with a clever regex.
+10 -5
View File
@@ -519,7 +519,7 @@ class view(osv.osv):
if context is None: context = {}
if root_id is None:
root_id = source_id
sql_inherit = self.pool['ir.ui.view'].get_inheriting_views_arch(cr, uid, source_id, model, context=context)
sql_inherit = self.get_inheriting_views_arch(cr, uid, source_id, model, context=context)
for (specs, view_id) in sql_inherit:
specs_tree = etree.fromstring(specs.encode('utf-8'))
if context.get('inherit_branding'):
@@ -986,10 +986,15 @@ class view(osv.osv):
)
qcontext.update(values)
# TODO: remove this as soon as the following branch is merged
# lp:~openerp-dev/openerp-web/trunk-module-closure-style-msh
from openerp.addons.web.controllers.main import module_boot
qcontext['modules'] = simplejson.dumps(module_boot()) if request else None
# TODO: This helper can be used by any template that wants to embedd the backend.
# It is currently necessary because the ir.ui.view bundle inheritance does not
# match the module dependency graph.
def get_modules_order():
if request:
from openerp.addons.web.controllers.main import module_boot
return simplejson.dumps(module_boot())
return '[]'
qcontext['get_modules_order'] = get_modules_order
def loader(name):
return self.read_template(cr, uid, name, context=context)