Merge remote-tracking branch 'odoo/master' into master-sass-in-bundles-fme

This commit is contained in:
Fabien Meghazi 2014-07-01 14:26:40 +02:00
commit 4042135bc4
71 changed files with 5124 additions and 4089 deletions

View File

@ -15,44 +15,63 @@
</field> </field>
</record> </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"> <record id="view_account_invoice_report_search" model="ir.ui.view">
<field name="name">account.invoice.report.search</field> <field name="name">account.invoice.report.search</field>
<field name="model">account.invoice.report</field> <field name="model">account.invoice.report</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<search string="Invoices Analysis"> <search string="Invoices Analysis">
<field name="date"/> <field name="date"/>
<filter icon="terp-go-year" string="Year" name="year" domain="[('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/> <separator/>
<filter string="Draft" icon="terp-document-new" domain="[('state','=','draft')]" help = "Draft Invoices"/> <filter string="To Invoice" 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="Pro-forma" domain="['|', ('state','=','proforma'),('state','=','proforma2')]"/>
<filter string="Invoiced" name="current" icon="terp-check" domain="[('state','not in', ('draft','cancel','proforma','proforma2'))]" help = "Open and Paid Invoices"/> <filter string="Invoiced" name="current" domain="[('state','not in', ('draft','cancel','proforma','proforma2'))]"/>
<separator/> <separator/>
<filter icon="terp-personal" string="Customer" name="customer" domain="['|', ('type','=','out_invoice'),('type','=','out_refund')]" help="Customer Invoices And Refunds"/> <filter string="Customer" name="customer" domain="['|', ('type','=','out_invoice'),('type','=','out_refund')]"/>
<filter icon="terp-personal" string="Supplier" domain="['|', ('type','=','in_invoice'),('type','=','in_refund')]" help="Supplier Invoices And Refunds"/> <filter string="Supplier" domain="['|', ('type','=','in_invoice'),('type','=','in_refund')]"/>
<separator/> <separator/>
<filter icon="terp-dolar" string="Invoice" domain="['|', ('type','=','out_invoice'),('type','=','in_invoice')]" help="Customer And Supplier Invoices"/> <filter string="Invoice" domain="['|', ('type','=','out_invoice'),('type','=','in_invoice')]"/>
<filter icon="terp-dolar_ok!" string="Refund" domain="['|', ('type','=','out_refund'),('type','=','in_refund')]" help="Customer And Supplier Refunds"/> <filter string="Refund" domain="['|', ('type','=','out_refund'),('type','=','in_refund')]"/>
<field name="partner_id"/> <field name="partner_id"/>
<field name="user_id" /> <field name="user_id" />
<field name="categ_id" filter_domain="[('categ_id', 'child_of', self)]"/> <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> </search>
</field> </field>
</record> </record>

View File

@ -10,22 +10,22 @@
<div class="row mt32 mb32"> <div class="row mt32 mb32">
<div class="col-xs-3"> <div class="col-xs-3">
<strong>Chart of Tax:</strong> <strong>Chart of Tax:</strong>
<p t-esc="account"/> <p t-esc="get_account(data)"/>
</div> </div>
<div class="col-xs-3" t-if="fiscalyear"> <div class="col-xs-3">
<strong>Fiscal Year:</strong> <strong>Fiscal Year:</strong>
<p t-esc="fiscalyear"/> <p t-esc="get_fiscalyear(data)"/>
</div> </div>
<div class="col-xs-3" t-if="period_from and period_to"> <div class="col-xs-3">
<strong>Periods:</strong> <strong>Periods:</strong>
<p> <p>
Start Period: <span t-esc="period_from"/><br/> Start Period: <span t-esc="get_start_period(data)"/><br/>
End Period: <span t-esc="period_to"/> End Period: <span t-esc="get_end_period(data)"/>
</p> </p>
</div> </div>
<div class="col-xs-3"> <div class="col-xs-3">
<strong>Based On:</strong> <strong>Based On:</strong>
<p t-esc="based_on"/> <p t-esc="get_basedon(data)"/>
</div> </div>
</div> </div>

View File

@ -222,13 +222,13 @@
<field name="create_date"/> <field name="create_date"/>
<field name="name"/> <field name="name"/>
<field name="contact_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="email_from"/>
<field name="phone"/> <field name="phone"/>
<field name="stage_id"/> <field name="stage_id"/>
<field name="user_id" invisible="1"/> <field name="user_id" invisible="1"/>
<field name="partner_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="probability" invisible="1"/>
<field name="type_id" invisible="1"/> <field name="type_id" invisible="1"/>
<field name="referred" invisible="1"/> <field name="referred" invisible="1"/>
@ -326,44 +326,31 @@
<search string="Search Leads"> <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="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="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="user_id"/>
<field name="partner_id" operator="child_of"/> <field name="partner_id" operator="child_of"/>
<field name="create_date"/> <field name="create_date"/>
<field name="country_id" context="{'invisible_country': False}"/> <field name="country_id"/>
<separator/> <separator/>
<filter string="My Leads"
domain="[('user_id','=',uid)]"
help="Leads that are assigned to me"/>
<filter string="Unassigned" name="unassigned" <filter string="Unassigned" name="unassigned"
domain="[('user_id','=', False)]" domain="[('user_id','=', False)]"
help="No salesperson"/> help="No salesperson"/>
<filter string="My Leads" <separator />
domain="[('user_id','=',uid)]" context="{'invisible_section': False}" <filter string="New Mail" name="message_unread" domain="[('message_unread','=',True)]"/>
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 /> <separator />
<filter string="Available for mass mailing" <filter string="Available for mass mailing"
name='not_opt_out' domain="[('opt_out', '=', False)]" name='not_opt_out' domain="[('opt_out', '=', False)]"
help="Leads that did not ask not to be included in mass mailing campaigns"/> help="Leads that did not ask not to be included in mass mailing campaigns"/>
<separator />
<group expand="0" string="Group By"> <group expand="0" string="Group By">
<filter string="Salesperson" domain="[]" context="{'group_by':'user_id'}"/> <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="Stage" domain="[]" context="{'group_by':'stage_id'}"/>
<filter string="Customer" help="Partner" domain="[]" context="{'group_by':'partner_id'}"/> <filter string="Customer" help="Partner" domain="[]" context="{'group_by':'partner_id'}"/>
<filter string="Country" domain="[]" context="{'group_by':'country_id'}"/> <filter string="Expected Closing" domain="[]" context="{'group_by':'date_deadline:week'}"/>
<filter string="Referrer" domain="[]" context="{'group_by':'referred'}"/> <filter string="Last Message" name="group_message_last_post" domain="[]" context="{'group_by':'message_last_post:week'}"/>
<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"/>
</group> </group>
</search> </search>
</field> </field>
@ -523,7 +510,7 @@
<field name="create_date"/> <field name="create_date"/>
<field name="name" string="Opportunity"/> <field name="name" string="Opportunity"/>
<field name="partner_id" string="Customer"/> <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="date_action"/>
<field name="title_action"/> <field name="title_action"/>
<field name="channel_id" invisible="1"/> <field name="channel_id" invisible="1"/>
@ -531,7 +518,7 @@
<field name="stage_id"/> <field name="stage_id"/>
<field name="planned_revenue" sum="Expected Revenues"/> <field name="planned_revenue" sum="Expected Revenues"/>
<field name="probability" avg="Avg. of Probability"/> <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="user_id"/>
<field name="referred" invisible="1"/> <field name="referred" invisible="1"/>
<field name="priority" invisible="1"/> <field name="priority" invisible="1"/>
@ -551,7 +538,7 @@
<search string="Search Opportunities"> <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="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="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="user_id"/>
<field name="partner_id" operator="child_of"/> <field name="partner_id" operator="child_of"/>
<field name="stage_id" domain="[]"/> <field name="stage_id" domain="[]"/>
@ -561,34 +548,21 @@
domain="[('probability', '=', 100), ('stage_id.fold', '=', True)]"/> domain="[('probability', '=', 100), ('stage_id.fold', '=', True)]"/>
<filter string="Lost" name="lost" <filter string="Lost" name="lost"
domain="[('probability', '=', 0), ('stage_id.fold', '=', True)]"/> 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" <filter string="Unassigned" name="unassigned"
domain="[('user_id','=', False)]" help="No salesperson"/> 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/> <separator/>
<filter string="New Mail" name="message_unread" domain="[('message_unread','=',True)]"/>
<group expand="0" string="Group By" colspan="16"> <group expand="0" string="Group By" colspan="16">
<filter string="Salesperson" domain="[]" context="{'group_by':'user_id'}"/> <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="Team" domain="[]" context="{'group_by':'section_id'}" groups="base.group_multi_salesteams"/>
<filter string="Stage" domain="[]" context="{'group_by':'stage_id'}"/> <filter string="Stage" domain="[]" context="{'group_by':'stage_id'}"/>
<filter string="Customer" help="Partner" domain="[]" context="{'group_by':'partner_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="Expected Closing" domain="[]" context="{'group_by':'date_deadline'}"/>
<filter string="Referrer" domain="[]" context="{'group_by':'referred'}"/> <filter string="Last Message" name="group_message_last_post" domain="[]" context="{'group_by':'message_last_post:week'}"/>
<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"/>
</group> </group>
</search> </search>
</field> </field>

View File

@ -173,24 +173,24 @@
<field name="name" string="Phonecalls"/> <field name="name" string="Phonecalls"/>
<field name="date"/> <field name="date"/>
<field name="state"/> <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/> <separator/>
<filter icon="terp-gtk-go-back-rtl" string="To Do" name="current" domain="[('state','=','open')]"/> <filter string="To Do" name="current" domain="[('state','=','open')]"/>
<separator/> <separator/>
<filter string="Unassigned Phonecalls" icon="terp-personal-" domain="[('user_id','=',False)]" help="Unassigned Phonecalls"/> <filter string="New Mail" name="message_unread" domain="[('message_unread','=',True)]"/>
<filter string="Unread Messages" name="message_unread" domain="[('message_unread','=',True)]"/>
<separator/> <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="partner_id" operator="child_of"/>
<field name="user_id"/> <field name="user_id"/>
<field name="opportunity_id"/> <field name="opportunity_id"/>
<field name="section_id" string="Sales Team" <field name="section_id" string="Sales Team"
groups="base.group_multi_salesteams"/> groups="base.group_multi_salesteams"/>
<group expand="0" string="Group By"> <group expand="0" string="Group By">
<filter string="Partner" icon="terp-partner" domain="[]" context="{'group_by':'partner_id'}"/> <filter string="Partner" domain="[]" context="{'group_by':'partner_id'}"/>
<filter string="Responsible" icon="terp-personal" domain="[]" context="{'group_by':'user_id'}"/> <filter string="Responsible" domain="[]" context="{'group_by':'user_id'}"/>
<filter string="Creation" icon="terp-go-month" help="Creation Date" domain="[]" context="{'group_by':'create_date'}"/> <filter string="Creation" 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"/> <filter string="Month" domain="[]" context="{'group_by':'date'}" help="Calls Date by Month"/>
</group> </group>
</search> </search>
</field> </field>

View File

@ -2,8 +2,7 @@
<openerp> <openerp>
<data> <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"> <record id="view_report_crm_lead_graph" model="ir.ui.view">
<field name="name">crm.lead.report.graph</field> <field name="name">crm.lead.report.graph</field>
<field name="model">crm.lead.report</field> <field name="model">crm.lead.report</field>
@ -38,8 +37,23 @@
</field> </field>
</record> </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"> <record id="view_report_crm_lead_filter" model="ir.ui.view">
<field name="name">crm.lead.report.select</field> <field name="name">crm.lead.report.select</field>
<field name="model">crm.lead.report</field> <field name="model">crm.lead.report</field>
@ -54,11 +68,6 @@
domain="[('probability', '=', 100), ('stage_id.on_change', '=', 1)]"/> domain="[('probability', '=', 100), ('stage_id.on_change', '=', 1)]"/>
<filter string="Lost" name="lost" <filter string="Lost" name="lost"
domain="[('probability', '=', 0), ('stage_id.sequence', '!=', 1)]"/> 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}" <field name="section_id" context="{'invisible_section': False}"
groups="base.group_multi_salesteams"/> groups="base.group_multi_salesteams"/>
<field name="user_id" string="Salesperson"/> <field name="user_id" string="Salesperson"/>
@ -79,26 +88,6 @@
<field name="opening_date"/> <field name="opening_date"/>
<field name="date_closed"/> <field name="date_closed"/>
</group> </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> </search>
</field> </field>
</record> </record>

View File

@ -59,13 +59,10 @@
<field name="model">crm.lead</field> <field name="model">crm.lead</field>
<field name="inherit_id" ref="crm.view_crm_case_opportunities_filter"/> <field name="inherit_id" ref="crm.view_crm_case_opportunities_filter"/>
<field name="arch" type="xml"> <field name="arch" type="xml">
<filter string="Team" position="after"> <filter string="Customer" position="after">
<filter string="Assigned Partner" icon="terp-personal" domain="[]" context="{'group_by':'partner_assigned_id'}"/> <filter string="Assigned Partner" domain="[]" context="{'group_by':'partner_assigned_id'}"/>
<filter string="Assigned Month" icon="terp-go-month"
domain="[]" context="{'group_by':'date_assign'}"/>
</filter> </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 string="My Assigned Partners" domain="[('partner_assigned_id.user_id', '=', uid)]"/>
</filter> </filter>
<field name="partner_id" position="after"> <field name="partner_id" position="after">
@ -131,10 +128,10 @@
<field name="model">crm.lead</field> <field name="model">crm.lead</field>
<field name="inherit_id" ref="crm.view_crm_case_leads_filter"/> <field name="inherit_id" ref="crm.view_crm_case_leads_filter"/>
<field name="arch" type="xml"> <field name="arch" type="xml">
<filter string="Team" position="after"> <filter string="Customer" position="after">
<filter string="Assigned Partner" icon="terp-personal" domain="[]" context="{'group_by':'partner_assigned_id'}"/> <filter string="Assigned Partner" domain="[]" context="{'group_by':'partner_assigned_id'}"/>
</filter> </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 string="My Assigned Partners" domain="[('partner_assigned_id.user_id', '=', uid)]"/>
</filter> </filter>
<field name="partner_id" position="after"> <field name="partner_id" position="after">

View File

@ -5,7 +5,7 @@
<record id="config_google_drive_client_id" model="ir.config_parameter"> <record id="config_google_drive_client_id" model="ir.config_parameter">
<field name="key">google_drive_client_id</field> <field name="key">google_drive_client_id</field>
<field name="value">598905559630.apps.googleusercontent.com</field> <field name="value">598905559630.apps.googleusercontent.com</field>
<field name="group_ids" eval="[(4, ref('base.group_user'))]" /> <field name="group_ids" eval="[(4, ref('base.group_system'))]" />
</record> </record>
<record id="config_google_drive_client_secret" model="ir.config_parameter"> <record id="config_google_drive_client_secret" model="ir.config_parameter">

View File

@ -193,14 +193,13 @@
<field name="arch" type="xml"> <field name="arch" type="xml">
<search string="Search Applicants"> <search string="Search Applicants">
<field name="partner_name" filter_domain="['|','|',('name','ilike',self),('partner_name','ilike',self),('email_from','ilike',self)]" string="Subject / Applicant"/> <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)]"/> <filter string="My" domain="[('user_id', '=', uid)]"/>
<separator/> <filter string="Unassigned" domain="[('user_id', '=', False)]"/>
<filter string="Unread Messages" name="message_unread" domain="[('message_unread','=',True)]"/>
<separator/> <separator/>
<filter string="Next Actions" context="{'invisible_next_action':False, 'invisible_next_date':False}" <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"/> 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="job_id"/>
<field name="department_id"/> <field name="department_id"/>
<field name="user_id"/> <field name="user_id"/>
@ -210,15 +209,9 @@
<separator/> <separator/>
<group expand="0" string="Group By"> <group expand="0" string="Group By">
<filter string="Responsible" domain="[]" context="{'group_by':'user_id'}"/> <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="Job" domain="[]" context="{'group_by':'job_id'}"/>
<filter string="Degree" domain="[]" context="{'group_by':'type_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="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'}"/> <filter string="Last Stage Update" context="{'group_by':'date_last_stage_update'}"/>
</group> </group>
</search> </search>

View File

@ -12,6 +12,29 @@
</field> </field>
</record> </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"> <record id="view_hr_recruitment_report_search" model="ir.ui.view">
<field name="name">hr.recruitment.report.search</field> <field name="name">hr.recruitment.report.search</field>
<field name="model">hr.recruitment.report</field> <field name="model">hr.recruitment.report</field>
@ -20,11 +43,13 @@
<field name="job_id"/> <field name="job_id"/>
<field name="department_id"/> <field name="department_id"/>
<field name="user_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/> <separator/>
<filter string="Unassigned" domain="[('user_id', '=', False)]"/> <filter string="Unassigned" domain="[('user_id', '=', False)]"/>
<filter string="My" domain="[('user_id', '=', uid)]"/> <filter string="My" domain="[('user_id', '=', uid)]"/>
<separator/> <separator/>
<filter string="New" domain="[('sequence', '=', 1)]"/>
<separator/>
<group expand="0" string="Extended Filters..."> <group expand="0" string="Extended Filters...">
<field name="priority"/> <field name="priority"/>
<field name="stage_id"/> <field name="stage_id"/>
@ -32,21 +57,6 @@
<field name="date_create"/> <field name="date_create"/>
<field name="date_closed"/> <field name="date_closed"/>
</group> </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> </search>
</field> </field>
</record> </record>
@ -56,7 +66,7 @@
<field name="res_model">hr.recruitment.report</field> <field name="res_model">hr.recruitment.report</field>
<field name="view_type">form</field> <field name="view_type">form</field>
<field name="view_mode">graph</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"/> <field name="search_view_id" ref="view_hr_recruitment_report_search"/>
</record> </record>
<menuitem action="action_hr_recruitment_report_all" id="menu_hr_recruitment_report_all" parent="hr.menu_hr_reporting" sequence="0"/> <menuitem action="action_hr_recruitment_report_all" id="menu_hr_recruitment_report_all" parent="hr.menu_hr_reporting" sequence="0"/>

View File

@ -12,8 +12,9 @@
} }
} }
/* button */ /* button */
.oe_systray #oe_topbar_im_icon { .oe_systray #oe_topbar_imbutton_icon {
color: white; color: white;
margin-right: 10px;
} }
.oe_topbar_item.oe_topbar_imbutton .oe_e { .oe_topbar_item.oe_topbar_imbutton .oe_e {
position: relative; position: relative;

View File

@ -104,6 +104,7 @@
this.manager.set("bottom_offset", $('.oe_chat_button').outerHeight()); // TODO correct the value (no hardcode damned !) this.manager.set("bottom_offset", $('.oe_chat_button').outerHeight()); // TODO correct the value (no hardcode damned !)
// override the notification default function // override the notification default function
this.manager.notification = function(notif){ this.manager.notification = function(notif){
alert(notif);
} }
} }
return this.chat(); return this.chat();

View File

@ -116,11 +116,9 @@
<!-- IM module --> <!-- IM module -->
<script type="text/javascript" src="/bus/static/src/js/bus.js"></script> <script type="text/javascript" src="/bus/static/src/js/bus.js"></script>
<script type="text/javascript" src="/im_chat/static/src/js/im_chat.js"></script> <script type="text/javascript" src="/im_chat/static/src/js/im_chat.js"></script>
<script type="text/javascript" src="/im_livechat/static/lib/jquery-achtung/src/ui.achtung.js"></script>
<script type="text/javascript" src="/im_livechat/static/src/js/im_livechat.js"></script> <script type="text/javascript" src="/im_livechat/static/src/js/im_livechat.js"></script>
<!-- CSS --> <!-- CSS -->
<link rel="stylesheet" href="/im_chat/static/src/css/im_common.css"></link> <link rel="stylesheet" href="/im_chat/static/src/css/im_common.css"></link>
<link rel="stylesheet" href="/im_livechat/static/lib/jquery-achtung/src/ui.achtung.css"></link>
</template> </template>
<!-- the js code to initialize the LiveSupport object --> <!-- the js code to initialize the LiveSupport object -->

5
addons/l10n_ro/__init__.py Normal file → Executable file
View File

@ -1,8 +1,9 @@
# -*- encoding: utf-8 -*- # -*- encoding: utf-8 -*-
############################################################################## ##############################################################################
# #
# OpenERP, Open Source Management Solution # @author - Fekete Mihai <feketemihai@gmail.com>
# Copyright (C) 2012 TOTAL PC SYSTEMS (<http://www.erpsystems.ro>). All Rights Reserved # Copyright (C) 2011 TOTAL PC SYSTEMS (http://www.www.erpsystems.ro).
# Copyright (C) 2009 (<http://www.filsystem.ro>)
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by

20
addons/l10n_ro/__openerp__.py Normal file → Executable file
View File

@ -1,8 +1,9 @@
# -*- encoding: utf-8 -*- # -*- encoding: utf-8 -*-
############################################################################## ##############################################################################
# #
# OpenERP, Open Source Management Solution # @author - Fekete Mihai <feketemihai@gmail.com>
# Copyright (C) 2012 (<http://www.erpsystems.ro>). All Rights Reserved # Copyright (C) 2011 TOTAL PC SYSTEMS (http://www.www.erpsystems.ro).
# Copyright (C) 2009 (<http://www.filsystem.ro>)
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by
@ -26,14 +27,21 @@
"category" : "Localization/Account Charts", "category" : "Localization/Account Charts",
"depends" : ['account','account_chart','base_vat'], "depends" : ['account','account_chart','base_vat'],
"description": """ "description": """
This is the module to manage the accounting chart, VAT structure and Registration Number for Romania in OpenERP. This is the module to manage the Accounting Chart, VAT structure, Fiscal Position and Tax Mapping.
It also adds the Registration Number for Romania in OpenERP.
================================================================================================================ ================================================================================================================
Romanian accounting chart and localization. Romanian accounting chart and localization.
""", """,
"demo_xml" : [], "demo" : [],
"data" : ['partner_view.xml','account_tax_code_template.xml','account_chart.xml','account_tax_template.xml','l10n_chart_ro_wizard.xml'], "data" : ['partner_view.xml',
"auto_install": False, 'account_chart.xml',
'account_tax_code_template.xml',
'account_chart_template.xml',
'account_tax_template.xml',
'fiscal_position_template.xml',
'l10n_chart_ro_wizard.xml',
],
"installable": True, "installable": True,
} }
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

687
addons/l10n_ro/account_chart.xml Normal file → Executable file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data noupdate="0">
<!-- Chart template -->
<record id="ro_chart_template" model="account.chart.template">
<field name="name">Romania - Chart of Accounts</field>
<field name="account_root_id" ref="pcg_0"/>
<field name="tax_code_root_id" ref="vat_code_tax"/>
<field name="bank_account_view_id" ref="ro_pcg_cash"/>
<field name="property_account_receivable" ref="ro_pcg_recv"/> <!-- 4111 -->
<field name="property_account_payable" ref="ro_pcg_pay"/> <!-- 4011 -->
<field name="property_account_expense_categ" ref="ro_pcg_expense"/> <!-- 607 -->
<field name="property_account_income_categ" ref="ro_pcg_sale"/> <!-- 707 -->
<field name="currency_id" ref="base.RON"/>
</record>
</data>
</openerp>

173
addons/l10n_ro/account_tax_code_template.xml Normal file → Executable file
View File

@ -20,28 +20,25 @@
<field name="code">TVA colectata 24%</field> <field name="code">TVA colectata 24%</field>
<field name="parent_id" ref="vat_code_tvac_tva"/> <field name="parent_id" ref="vat_code_tvac_tva"/>
</record> </record>
<record model="account.tax.code.template" id="vat_code_tvac_tva19"> <record model="account.tax.code.template" id="vat_code_tvac_tva19">
<field name="name">TVA 19%</field> <field name="name">TVA 19%</field>
<field name="code">TVA colectata 19%</field> <field name="code">TVA colectata 19%</field>
<field name="parent_id" ref="vat_code_tvac_tva"/> <field name="parent_id" ref="vat_code_tvac_tva"/>
</record> </record>
<record model="account.tax.code.template" id="vat_code_tvac_tva09"> <record model="account.tax.code.template" id="vat_code_tvac_tva09">
<field name="name">TVA 9%</field> <field name="name">TVA 9%</field>
<field name="code">TVA colectata 9%</field> <field name="code">TVA colectata 9%</field>
<field name="parent_id" ref="vat_code_tvac_tva"/> <field name="parent_id" ref="vat_code_tvac_tva"/>
</record> </record>
<record model="account.tax.code.template" id="vat_code_tvac_tva05"> <record model="account.tax.code.template" id="vat_code_tvac_tva05">
<field name="name">TVA 5%</field> <field name="name">TVA 5%</field>
<field name="code">TVA colectata 5%</field> <field name="code">TVA colectata 5%</field>
<field name="parent_id" ref="vat_code_tvac_tva"/> <field name="parent_id" ref="vat_code_tvac_tva"/>
</record> </record>
<record model="account.tax.code.template" id="vat_code_tvac_tvaTI">
<field name="name">TVA Taxare inversa</field>
<field name="code">TVA</field>
<field name="parent_id" ref="vat_code_tvac_tva"/>
</record>
<record model="account.tax.code.template" id="vat_code_tvac_tva00"> <record model="account.tax.code.template" id="vat_code_tvac_tva00">
<field name="name">TVA 0%</field> <field name="name">TVA 0%</field>
<field name="code">TVA colectata 0%</field> <field name="code">TVA colectata 0%</field>
@ -53,60 +50,61 @@
<field name="code">a)</field> <field name="code">a)</field>
<field name="parent_id" ref="vat_code_tax"/> <field name="parent_id" ref="vat_code_tax"/>
</record> </record>
<record model="account.tax.code.template" id="vat_code_tvac_baza24"> <record model="account.tax.code.template" id="vat_code_tvac_baza24">
<field name="name">Baza TVA 24%</field> <field name="name">Baza TVA 24%</field>
<field name="code">TVA colectata 24%(Baza)</field> <field name="code">TVA colectata 24%(Baza)</field>
<field name="parent_id" ref="vat_code_tvac_baza"/> <field name="parent_id" ref="vat_code_tvac_baza"/>
</record> </record>
<record model="account.tax.code.template" id="vat_code_tvac_baza19"> <record model="account.tax.code.template" id="vat_code_tvac_baza19">
<field name="name">Baza TVA 19%</field> <field name="name">Baza TVA 19%</field>
<field name="code">TVA colectata 19%(Baza)</field> <field name="code">TVA colectata 19%(Baza)</field>
<field name="parent_id" ref="vat_code_tvac_baza"/> <field name="parent_id" ref="vat_code_tvac_baza"/>
</record> </record>
<record model="account.tax.code.template" id="vat_code_tvac_baza09"> <record model="account.tax.code.template" id="vat_code_tvac_baza09">
<field name="name">Baza TVA 9%</field> <field name="name">Baza TVA 9%</field>
<field name="code">TVA colectata 9%(Baza)</field> <field name="code">TVA colectata 9%(Baza)</field>
<field name="parent_id" ref="vat_code_tvac_baza"/> <field name="parent_id" ref="vat_code_tvac_baza"/>
</record> </record>
<record model="account.tax.code.template" id="vat_code_tvac_baza05"> <record model="account.tax.code.template" id="vat_code_tvac_baza05">
<field name="name">Baza TVA 5%</field> <field name="name">Baza TVA 5%</field>
<field name="code">TVA colectata 5%(Baza)</field> <field name="code">TVA colectata 5%(Baza)</field>
<field name="parent_id" ref="vat_code_tvac_baza"/> <field name="parent_id" ref="vat_code_tvac_baza"/>
</record> </record>
<record model="account.tax.code.template" id="vat_code_tvac_bazaTI">
<field name="name">Baza TVA Taxare inversa</field>
<field name="code">Baza</field>
<field name="parent_id" ref="vat_code_tvac_baza"/>
</record>
<record model="account.tax.code.template" id="vat_code_tvac_baza00"> <record model="account.tax.code.template" id="vat_code_tvac_baza00">
<field name="name">Baza TVA 0%</field> <field name="name">Baza TVA 0%</field>
<field name="code">TVA colectata 0%(Baza)</field> <field name="code">TVA colectata 0%(Baza)</field>
<field name="parent_id" ref="vat_code_tvac_baza"/> <field name="parent_id" ref="vat_code_tvac_baza"/>
</record> </record>
<record model="account.tax.code.template" id="vat_code_tvad_tva"> <record model="account.tax.code.template" id="vat_code_tvad_tva">
<field name="name">TVA DEDUCTIBILA</field> <field name="name">TVA DEDUCTIBILA</field>
<field name="code">d)</field> <field name="code">d)</field>
<field name="parent_id" ref="vat_code_tax"/> <field name="parent_id" ref="vat_code_tax"/>
</record> </record>
<record model="account.tax.code.template" id="vat_code_tvad_tva24"> <record model="account.tax.code.template" id="vat_code_tvad_tva24">
<field name="name">TVA 24%</field> <field name="name">TVA 24%</field>
<field name="code">TVA deductibila 24%</field> <field name="code">TVA deductibila 24%</field>
<field name="parent_id" ref="vat_code_tvad_tva"/> <field name="parent_id" ref="vat_code_tvad_tva"/>
</record> </record>
<record model="account.tax.code.template" id="vat_code_tvad_tva19"> <record model="account.tax.code.template" id="vat_code_tvad_tva19">
<field name="name">TVA 19%</field> <field name="name">TVA 19%</field>
<field name="code">TVA deductibila 19%</field> <field name="code">TVA deductibila 19%</field>
<field name="parent_id" ref="vat_code_tvad_tva"/> <field name="parent_id" ref="vat_code_tvad_tva"/>
</record> </record>
<record model="account.tax.code.template" id="vat_code_tvad_tva09"> <record model="account.tax.code.template" id="vat_code_tvad_tva09">
<field name="name">TVA 9%</field> <field name="name">TVA 9%</field>
<field name="code">TVA deductibila 9%</field> <field name="code">TVA deductibila 9%</field>
<field name="parent_id" ref="vat_code_tvad_tva"/> <field name="parent_id" ref="vat_code_tvad_tva"/>
</record> </record>
<record model="account.tax.code.template" id="vat_code_tvad_tva05"> <record model="account.tax.code.template" id="vat_code_tvad_tva05">
<field name="name">TVA 5%</field> <field name="name">TVA 5%</field>
<field name="code">TVA deductibila 5%</field> <field name="code">TVA deductibila 5%</field>
@ -118,26 +116,31 @@
<field name="code">TVA deductibila 0%</field> <field name="code">TVA deductibila 0%</field>
<field name="parent_id" ref="vat_code_tvad_tva"/> <field name="parent_id" ref="vat_code_tvad_tva"/>
</record> </record>
<record model="account.tax.code.template" id="vat_code_tvad_baza"> <record model="account.tax.code.template" id="vat_code_tvad_baza">
<field name="name">BAZA TVA DEDUCTIBIL</field> <field name="name">BAZA TVA DEDUCTIBIL</field>
<field name="code">c)</field> <field name="code">c)</field>
<field name="parent_id" ref="vat_code_tax"/> <field name="parent_id" ref="vat_code_tax"/>
</record> </record>
<record model="account.tax.code.template" id="vat_code_tvad_baza24"> <record model="account.tax.code.template" id="vat_code_tvad_baza24">
<field name="name">Baza TVA 24%</field> <field name="name">Baza TVA 24%</field>
<field name="code">TVA deductibila 24%(Baza)</field> <field name="code">TVA deductibila 24%(Baza)</field>
<field name="parent_id" ref="vat_code_tvad_baza"/> <field name="parent_id" ref="vat_code_tvad_baza"/>
</record> </record>
<record model="account.tax.code.template" id="vat_code_tvad_baza19"> <record model="account.tax.code.template" id="vat_code_tvad_baza19">
<field name="name">Baza TVA 19%</field> <field name="name">Baza TVA 19%</field>
<field name="code">TVA deductibila 19%(Baza)</field> <field name="code">TVA deductibila 19%(Baza)</field>
<field name="parent_id" ref="vat_code_tvad_baza"/> <field name="parent_id" ref="vat_code_tvad_baza"/>
</record> </record>
<record model="account.tax.code.template" id="vat_code_tvad_baza09"> <record model="account.tax.code.template" id="vat_code_tvad_baza09">
<field name="name">Baza TVA 9%</field> <field name="name">Baza TVA 9%</field>
<field name="code">TVA deductibila 9%(Baza)</field> <field name="code">TVA deductibila 9%(Baza)</field>
<field name="parent_id" ref="vat_code_tvad_baza"/> <field name="parent_id" ref="vat_code_tvad_baza"/>
</record> </record>
<record model="account.tax.code.template" id="vat_code_tvad_baza05"> <record model="account.tax.code.template" id="vat_code_tvad_baza05">
<field name="name">Baza TVA 5%</field> <field name="name">Baza TVA 5%</field>
<field name="code">TVA deductibila 5%(Baza)</field> <field name="code">TVA deductibila 5%(Baza)</field>
@ -150,37 +153,137 @@
<field name="parent_id" ref="vat_code_tvad_baza"/> <field name="parent_id" ref="vat_code_tvad_baza"/>
</record> </record>
<record model="account.tax.code.template" id="vat_code_tvaneex_baza"> <record model="account.tax.code.template" id="vat_code_tvati_baza">
<field name="name">BAZA TVA NEEXIGIBIL</field> <field name="name">BAZA TVA TAXARE INVERSA</field>
<field name="code">e)</field> <field name="code">e)</field>
<field name="parent_id" ref="vat_code_tax"/> <field name="parent_id" ref="vat_code_tax"/>
</record> </record>
<record model="account.tax.code.template" id="vat_code_tvaneexc_baza">
<field name="name">Baza TVA neexigibil - colectat</field> <record model="account.tax.code.template" id="vat_code_tvac_bazaTI">
<field name="code">TVA neexigibil - colectat (Baza)</field> <field name="name">Baza TVA Taxare inversa</field>
<field name="parent_id" ref="vat_code_tvaneex_baza"/> <field name="code">Baza TVA Taxare inversa</field>
<field name="parent_id" ref="vat_code_tvati_baza"/>
</record> </record>
<record model="account.tax.code.template" id="vat_code_tvaneexd_baza">
<field name="name">Baza TVA neexigibil - deductibil</field> <record model="account.tax.code.template" id="vat_code_tvati">
<field name="code">TVA neexigibil - deductibil (Baza)</field> <field name="name">TVA TAXARE INVERSA</field>
<field name="parent_id" ref="vat_code_tvaneex_baza"/>
</record>
<record model="account.tax.code.template" id="vat_code_tvaneex">
<field name="name">TVA NEEXIGIBIL</field>
<field name="code">f)</field> <field name="code">f)</field>
<field name="parent_id" ref="vat_code_tax"/> <field name="parent_id" ref="vat_code_tax"/>
</record> </record>
<record model="account.tax.code.template" id="vat_code_tvaneexc">
<field name="name">TVA neexigibil - colectat</field> <record model="account.tax.code.template" id="vat_code_tvac_tvaTI">
<field name="code">TVA neexigibil - colectat</field> <field name="name">TVA Taxare inversa</field>
<field name="parent_id" ref="vat_code_tvaneex"/> <field name="code">TVA Taxare inversa</field>
</record> <field name="parent_id" ref="vat_code_tvati"/>
<record model="account.tax.code.template" id="vat_code_tvaneexd">
<field name="name">TVA neexigibil - deductibil</field>
<field name="code">TVA neexigibil - deductibil</field>
<field name="parent_id" ref="vat_code_tvaneex"/>
</record> </record>
<record model="account.tax.code.template" id="vat_code_tvati_baza">
<field name="name">BAZA TVA TAXARE INVERSA</field>
<field name="code">e)</field>
<field name="parent_id" ref="vat_code_tax"/>
</record>
<record model="account.tax.code.template" id="vat_code_tvac_bazaTI">
<field name="name">Baza TVA Taxare inversa</field>
<field name="code">Baza TVA Taxare inversa</field>
<field name="parent_id" ref="vat_code_tvati_baza"/>
</record>
<record model="account.tax.code.template" id="vat_code_tvati">
<field name="name">TVA TAXARE INVERSA</field>
<field name="code">f)</field>
<field name="parent_id" ref="vat_code_tax"/>
</record>
<record model="account.tax.code.template" id="vat_code_tvac_tvaTI">
<field name="name">TVA Taxare inversa</field>
<field name="code">TVA Taxare inversa</field>
<field name="parent_id" ref="vat_code_tvati"/>
</record>
<record model="account.tax.code.template" id="vat_code_tvanex_baza">
<field name="name">BAZA TVA NEEXIGIBILA</field>
<field name="code">g)</field>
<field name="parent_id" ref="vat_code_tax"/>
</record>
<record model="account.tax.code.template" id="vat_code_tvac_nex_baza">
<field name="name">Baza TVA Neexigibil Colectat</field>
<field name="code">Baza TVA Neexigibil Colectat</field>
<field name="parent_id" ref="vat_code_tvanex_baza"/>
</record>
<record model="account.tax.code.template" id="vat_code_tvad_nex_baza">
<field name="name">Baza TVA Neexigibil Deductibil</field>
<field name="code">Baza TVA Neexigibil Deductibil</field>
<field name="parent_id" ref="vat_code_tvanex_baza"/>
</record>
<record model="account.tax.code.template" id="vat_code_tvanex">
<field name="name">TVA NEEXIGIBILA</field>
<field name="code">h)</field>
<field name="parent_id" ref="vat_code_tax"/>
</record>
<record model="account.tax.code.template" id="vat_code_tvac_nex">
<field name="name">TVA Neexigibil Colectat</field>
<field name="code">TVA Neexigibil Colectat</field>
<field name="parent_id" ref="vat_code_tvanex"/>
</record>
<record model="account.tax.code.template" id="vat_code_tvad_nex">
<field name="name">TVA Neexigibil Deductibil</field>
<field name="code">TVA Neexigibil Deductibil</field>
<field name="parent_id" ref="vat_code_tvanex"/>
</record>
<record model="account.tax.code.template" id="vat_code_tvatisc_baza">
<field name="name">BAZA TVA TAXARE INTRACOMUNITARA SCUTITA</field>
<field name="code">i)</field>
<field name="parent_id" ref="vat_code_tax"/>
</record>
<record model="account.tax.code.template" id="vat_code_tvac_bazaTISC">
<field name="name">Baza TVA Taxare intracomunitara scutita</field>
<field name="code">Baza TVA Taxare intracomunitara scutita</field>
<field name="parent_id" ref="vat_code_tvatisc_baza"/>
</record>
<record model="account.tax.code.template" id="vat_code_tvatisc">
<field name="name">TVA TAXARE INTRACOMUNITARA SCUTITA</field>
<field name="code">j)</field>
<field name="parent_id" ref="vat_code_tax"/>
</record>
<record model="account.tax.code.template" id="vat_code_tvac_tvaTISC">
<field name="name">TVA Taxare intracomunitara scutita</field>
<field name="code">TVA Taxare intracomunitara scutita</field>
<field name="parent_id" ref="vat_code_tvatisc"/>
</record>
<record model="account.tax.code.template" id="vat_code_tvatine_baza">
<field name="name">BAZA TVA TAXARE INTRACOMUNITARA NEIMPOZABILA</field>
<field name="code">k)</field>
<field name="parent_id" ref="vat_code_tax"/>
</record>
<record model="account.tax.code.template" id="vat_code_tvac_bazaTINE">
<field name="name">Baza TVA Taxare intracomunitara neimpozabila</field>
<field name="code">Baza TVA Taxare intracomunitara neimpozabila</field>
<field name="parent_id" ref="vat_code_tvatine_baza"/>
</record>
<record model="account.tax.code.template" id="vat_code_tvatine">
<field name="name">TVA TAXARE INTRACOMUNITARA NEIMPOZABILA</field>
<field name="code">l)</field>
<field name="parent_id" ref="vat_code_tax"/>
</record>
<record model="account.tax.code.template" id="vat_code_tvac_tvaTINE">
<field name="name">TVA Taxare intracomunitara neimpozabila</field>
<field name="code">TVA Taxare intracomunitara neimpozabila</field>
<field name="parent_id" ref="vat_code_tvatine"/>
</record>
</data> </data>
</openerp> </openerp>

129
addons/l10n_ro/account_tax_template.xml Normal file → Executable file
View File

@ -1,11 +1,12 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<openerp> <openerp>
<data noupdate="0"> <data noupdate="0">
<record id="tvac_00" model="account.tax.template"> <record id="tvac_00" model="account.tax.template">
<field name="chart_template_id" ref="romania_chart_template"/> <field name="chart_template_id" ref="ro_chart_template"/>
<field name="name">TVA colectat 0%</field> <field name="name">TVA colectat 0%</field>
<field name="amount">0.000000</field> <field name="amount">0.000000</field>
<field name="type">percent</field> <field name="type">percent</field>
<field name="type_tax_use">sale</field>
<field name="account_collected_id" ref="pcg_4427"/> <field name="account_collected_id" ref="pcg_4427"/>
<field name="account_paid_id" ref="pcg_4427"/> <field name="account_paid_id" ref="pcg_4427"/>
<field name="base_code_id" ref="vat_code_tvac_baza00"/> <field name="base_code_id" ref="vat_code_tvac_baza00"/>
@ -16,12 +17,14 @@
<field name="ref_base_sign">-1.00</field> <field name="ref_base_sign">-1.00</field>
<field name="ref_tax_code_id" ref="vat_code_tvac_tva00"/> <field name="ref_tax_code_id" ref="vat_code_tvac_tva00"/>
<field name="ref_tax_sign">-1.00</field> <field name="ref_tax_sign">-1.00</field>
</record> </record>
<record id="tvac_05" model="account.tax.template">
<field name="chart_template_id" ref="romania_chart_template"/> <record id="tvac_05" model="account.tax.template">
<field name="chart_template_id" ref="ro_chart_template"/>
<field name="name">TVA colectat 5%</field> <field name="name">TVA colectat 5%</field>
<field name="amount">0.050000</field> <field name="amount">0.050000</field>
<field name="type">percent</field> <field name="type">percent</field>
<field name="type_tax_use">sale</field>
<field name="account_collected_id" ref="pcg_4427"/> <field name="account_collected_id" ref="pcg_4427"/>
<field name="account_paid_id" ref="pcg_4427"/> <field name="account_paid_id" ref="pcg_4427"/>
<field name="base_code_id" ref="vat_code_tvac_baza05"/> <field name="base_code_id" ref="vat_code_tvac_baza05"/>
@ -32,12 +35,14 @@
<field name="ref_tax_sign">-1.00</field> <field name="ref_tax_sign">-1.00</field>
<field name="base_sign">1.00</field> <field name="base_sign">1.00</field>
<field name="tax_sign">1.00</field> <field name="tax_sign">1.00</field>
</record> </record>
<record id="tvac_09" model="account.tax.template">
<field name="chart_template_id" ref="romania_chart_template"/> <record id="tvac_09" model="account.tax.template">
<field name="chart_template_id" ref="ro_chart_template"/>
<field name="name">TVA colectat 9%</field> <field name="name">TVA colectat 9%</field>
<field name="amount">0.090000</field> <field name="amount">0.090000</field>
<field name="type">percent</field> <field name="type">percent</field>
<field name="type_tax_use">sale</field>
<field name="account_collected_id" ref="pcg_4427"/> <field name="account_collected_id" ref="pcg_4427"/>
<field name="account_paid_id" ref="pcg_4427"/> <field name="account_paid_id" ref="pcg_4427"/>
<field name="base_code_id" ref="vat_code_tvac_baza09"/> <field name="base_code_id" ref="vat_code_tvac_baza09"/>
@ -48,12 +53,14 @@
<field name="ref_tax_sign">-1.00</field> <field name="ref_tax_sign">-1.00</field>
<field name="base_sign">1.00</field> <field name="base_sign">1.00</field>
<field name="tax_sign">1.00</field> <field name="tax_sign">1.00</field>
</record> </record>
<record id="tvac_19" model="account.tax.template">
<field name="chart_template_id" ref="romania_chart_template"/> <record id="tvac_19" model="account.tax.template">
<field name="chart_template_id" ref="ro_chart_template"/>
<field name="name">TVA colectat 19%</field> <field name="name">TVA colectat 19%</field>
<field name="amount">0.190000</field> <field name="amount">0.190000</field>
<field name="type">percent</field> <field name="type">percent</field>
<field name="type_tax_use">sale</field>
<field name="account_collected_id" ref="pcg_4427"/> <field name="account_collected_id" ref="pcg_4427"/>
<field name="account_paid_id" ref="pcg_4427"/> <field name="account_paid_id" ref="pcg_4427"/>
<field name="base_code_id" ref="vat_code_tvac_baza19"/> <field name="base_code_id" ref="vat_code_tvac_baza19"/>
@ -64,13 +71,14 @@
<field name="ref_tax_sign">-1.00</field> <field name="ref_tax_sign">-1.00</field>
<field name="base_sign">1.00</field> <field name="base_sign">1.00</field>
<field name="tax_sign">1.00</field> <field name="tax_sign">1.00</field>
</record> </record>
<record id="tvac_24" model="account.tax.template"> <record id="tvac_24" model="account.tax.template">
<field name="chart_template_id" ref="romania_chart_template"/> <field name="chart_template_id" ref="ro_chart_template"/>
<field name="name">TVA colectat 24%</field> <field name="name">TVA colectat 24%</field>
<field name="amount">0.240000</field> <field name="amount">0.240000</field>
<field name="type">percent</field> <field name="type">percent</field>
<field name="type_tax_use">sale</field>
<field name="account_collected_id" ref="pcg_4427"/> <field name="account_collected_id" ref="pcg_4427"/>
<field name="account_paid_id" ref="pcg_4427"/> <field name="account_paid_id" ref="pcg_4427"/>
<field name="base_code_id" ref="vat_code_tvac_baza24"/> <field name="base_code_id" ref="vat_code_tvac_baza24"/>
@ -81,13 +89,14 @@
<field name="ref_tax_sign">-1.00</field> <field name="ref_tax_sign">-1.00</field>
<field name="base_sign">1.00</field> <field name="base_sign">1.00</field>
<field name="tax_sign">1.00</field> <field name="tax_sign">1.00</field>
</record> </record>
<record id="tvad_00" model="account.tax.template"> <record id="tvad_00" model="account.tax.template">
<field name="chart_template_id" ref="romania_chart_template"/> <field name="chart_template_id" ref="ro_chart_template"/>
<field name="name">TVA deductibil 0%</field> <field name="name">TVA deductibil 0%</field>
<field name="amount">0.000000</field> <field name="amount">0.000000</field>
<field name="type">percent</field> <field name="type">percent</field>
<field name="type_tax_use">purchase</field>
<field name="account_collected_id" ref="pcg_4426"/> <field name="account_collected_id" ref="pcg_4426"/>
<field name="account_paid_id" ref="pcg_4426"/> <field name="account_paid_id" ref="pcg_4426"/>
<field name="base_code_id" ref="vat_code_tvad_baza00"/> <field name="base_code_id" ref="vat_code_tvad_baza00"/>
@ -98,12 +107,14 @@
<field name="ref_base_sign">-1.00</field> <field name="ref_base_sign">-1.00</field>
<field name="ref_tax_code_id" ref="vat_code_tvad_tva00"/> <field name="ref_tax_code_id" ref="vat_code_tvad_tva00"/>
<field name="ref_tax_sign">-1.00</field> <field name="ref_tax_sign">-1.00</field>
</record> </record>
<record id="tvad_05" model="account.tax.template">
<field name="chart_template_id" ref="romania_chart_template"/> <record id="tvad_05" model="account.tax.template">
<field name="chart_template_id" ref="ro_chart_template"/>
<field name="name">TVA deductibil 5%</field> <field name="name">TVA deductibil 5%</field>
<field name="amount">0.050000</field> <field name="amount">0.050000</field>
<field name="type">percent</field> <field name="type">percent</field>
<field name="type_tax_use">purchase</field>
<field name="account_collected_id" ref="pcg_4426"/> <field name="account_collected_id" ref="pcg_4426"/>
<field name="account_paid_id" ref="pcg_4426"/> <field name="account_paid_id" ref="pcg_4426"/>
<field name="base_code_id" ref="vat_code_tvad_baza05"/> <field name="base_code_id" ref="vat_code_tvad_baza05"/>
@ -114,12 +125,14 @@
<field name="ref_tax_sign">-1.00</field> <field name="ref_tax_sign">-1.00</field>
<field name="base_sign">1.00</field> <field name="base_sign">1.00</field>
<field name="tax_sign">1.00</field> <field name="tax_sign">1.00</field>
</record> </record>
<record id="tvad_09" model="account.tax.template">
<field name="chart_template_id" ref="romania_chart_template"/> <record id="tvad_09" model="account.tax.template">
<field name="chart_template_id" ref="ro_chart_template"/>
<field name="name">TVA deductibil 9%</field> <field name="name">TVA deductibil 9%</field>
<field name="amount">0.090000</field> <field name="amount">0.090000</field>
<field name="type">percent</field> <field name="type">percent</field>
<field name="type_tax_use">purchase</field>
<field name="account_collected_id" ref="pcg_4426"/> <field name="account_collected_id" ref="pcg_4426"/>
<field name="account_paid_id" ref="pcg_4426"/> <field name="account_paid_id" ref="pcg_4426"/>
<field name="base_code_id" ref="vat_code_tvad_baza09"/> <field name="base_code_id" ref="vat_code_tvad_baza09"/>
@ -130,12 +143,14 @@
<field name="ref_tax_sign">-1.00</field> <field name="ref_tax_sign">-1.00</field>
<field name="base_sign">1.00</field> <field name="base_sign">1.00</field>
<field name="tax_sign">1.00</field> <field name="tax_sign">1.00</field>
</record> </record>
<record id="tvad_19" model="account.tax.template">
<field name="chart_template_id" ref="romania_chart_template"/> <record id="tvad_19" model="account.tax.template">
<field name="chart_template_id" ref="ro_chart_template"/>
<field name="name">TVA deductibil 19%</field> <field name="name">TVA deductibil 19%</field>
<field name="amount">0.190000</field> <field name="amount">0.190000</field>
<field name="type">percent</field> <field name="type">percent</field>
<field name="type_tax_use">purchase</field>
<field name="account_collected_id" ref="pcg_4426"/> <field name="account_collected_id" ref="pcg_4426"/>
<field name="account_paid_id" ref="pcg_4426"/> <field name="account_paid_id" ref="pcg_4426"/>
<field name="base_code_id" ref="vat_code_tvad_baza19"/> <field name="base_code_id" ref="vat_code_tvad_baza19"/>
@ -146,13 +161,14 @@
<field name="ref_tax_sign">-1.00</field> <field name="ref_tax_sign">-1.00</field>
<field name="base_sign">1.00</field> <field name="base_sign">1.00</field>
<field name="tax_sign">1.00</field> <field name="tax_sign">1.00</field>
</record>
</record> <record id="tvad_24" model="account.tax.template">
<record id="tvad_24" model="account.tax.template"> <field name="chart_template_id" ref="ro_chart_template"/>
<field name="chart_template_id" ref="romania_chart_template"/>
<field name="name">TVA deductibil 24%</field> <field name="name">TVA deductibil 24%</field>
<field name="amount">0.240000</field> <field name="amount">0.240000</field>
<field name="type">percent</field> <field name="type">percent</field>
<field name="type_tax_use">purchase</field>
<field name="account_collected_id" ref="pcg_4426"/> <field name="account_collected_id" ref="pcg_4426"/>
<field name="account_paid_id" ref="pcg_4426"/> <field name="account_paid_id" ref="pcg_4426"/>
<field name="base_code_id" ref="vat_code_tvad_baza24"/> <field name="base_code_id" ref="vat_code_tvad_baza24"/>
@ -163,8 +179,61 @@
<field name="ref_tax_sign">-1.00</field> <field name="ref_tax_sign">-1.00</field>
<field name="base_sign">1.00</field> <field name="base_sign">1.00</field>
<field name="tax_sign">1.00</field> <field name="tax_sign">1.00</field>
</record>
</record> <record id="tvati" model="account.tax.template">
<field name="chart_template_id" ref="ro_chart_template"/>
<field name="name">TVA Taxare Inversa</field>
<field name="amount">0.000000</field>
<field name="type">percent</field>
<field name="type_tax_use">sale</field>
<field name="account_collected_id" ref="pcg_4426"/>
<field name="account_paid_id" ref="pcg_4426"/>
<field name="base_code_id" ref="vat_code_tvac_bazaTI"/>
<field name="base_sign">1.00</field>
<field name="tax_code_id" ref="vat_code_tvac_tvaTI"/>
<field name="tax_sign">1.00</field>
<field name="ref_base_code_id" ref="vat_code_tvac_bazaTI"/>
<field name="ref_base_sign">-1.00</field>
<field name="ref_tax_code_id" ref="vat_code_tvac_tvaTI"/>
<field name="ref_tax_sign">-1.00</field>
</record>
<record id="tvatisc" model="account.tax.template">
<field name="chart_template_id" ref="ro_chart_template"/>
<field name="name">TVA Taxare Intracomunitara Scutita</field>
<field name="amount">0.000000</field>
<field name="type">percent</field>
<field name="type_tax_use">all</field>
<field name="account_collected_id" ref="pcg_4426"/>
<field name="account_paid_id" ref="pcg_4426"/>
<field name="base_code_id" ref="vat_code_tvac_bazaTISC"/>
<field name="base_sign">1.00</field>
<field name="tax_code_id" ref="vat_code_tvac_tvaTISC"/>
<field name="tax_sign">1.00</field>
<field name="ref_base_code_id" ref="vat_code_tvac_bazaTISC"/>
<field name="ref_base_sign">-1.00</field>
<field name="ref_tax_code_id" ref="vat_code_tvac_tvaTISC"/>
<field name="ref_tax_sign">-1.00</field>
</record>
<record id="tvatine" model="account.tax.template">
<field name="chart_template_id" ref="ro_chart_template"/>
<field name="name">TVA Taxare Intracomunitara Neimpozabila</field>
<field name="amount">0.000000</field>
<field name="type">percent</field>
<field name="type_tax_use">all</field>
<field name="account_collected_id" ref="pcg_4426"/>
<field name="account_paid_id" ref="pcg_4426"/>
<field name="base_code_id" ref="vat_code_tvac_bazaTINE"/>
<field name="base_sign">1.00</field>
<field name="tax_code_id" ref="vat_code_tvac_tvaTINE"/>
<field name="tax_sign">1.00</field>
<field name="ref_base_code_id" ref="vat_code_tvac_bazaTINE"/>
<field name="ref_base_sign">-1.00</field>
<field name="ref_tax_code_id" ref="vat_code_tvac_tvaTINE"/>
<field name="ref_tax_sign">-1.00</field>
</record>
</data> </data>
</openerp> </openerp>

View File

@ -0,0 +1,389 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data noupdate="0">
<!-- Fiscal Position Templates -->
<record id="fiscal_position_template_1" model="account.fiscal.position.template">
<field name="name">Regim National</field>
<field name="chart_template_id" ref="ro_chart_template"/>
</record>
<record id="fiscal_position_template_2" model="account.fiscal.position.template">
<field name="name">Regim Taxare Inversa</field>
<field name="chart_template_id" ref="ro_chart_template"/>
</record>
<record id="fiscal_position_template_3" model="account.fiscal.position.template">
<field name="name">Regim Intra-Comunitar Bunuri</field>
<field name="chart_template_id" ref="ro_chart_template"/>
</record>
<record id="fiscal_position_template_4" model="account.fiscal.position.template">
<field name="name">Regim Intra-Comunitar Servicii</field>
<field name="chart_template_id" ref="ro_chart_template"/>
</record>
<record id="fiscal_position_template_5" model="account.fiscal.position.template">
<field name="name">Regim Intra-Comunitar Scutite</field>
<field name="chart_template_id" ref="ro_chart_template"/>
</record>
<record id="fiscal_position_template_6" model="account.fiscal.position.template">
<field name="name">Regim Intra-Comunitar Neimpozabile</field>
<field name="chart_template_id" ref="ro_chart_template"/>
</record>
<record id="fiscal_position_template_7" model="account.fiscal.position.template">
<field name="name">Regim Extra-Comunitar</field>
<field name="chart_template_id" ref="ro_chart_template"/>
</record>
<!-- account.fiscal.position.tax.template -->
<!-- Inverse taxation -->
<!-- Sales -->
<record id="afptt_inverse_1" model="account.fiscal.position.tax.template">
<field name="position_id" ref="fiscal_position_template_2"/>
<field name="tax_src_id" ref="tvac_00"/>
<field name="tax_dest_id" ref="tvati"/>
</record>
<record id="afptt_inverse_2" model="account.fiscal.position.tax.template">
<field name="position_id" ref="fiscal_position_template_2"/>
<field name="tax_src_id" ref="tvac_05"/>
<field name="tax_dest_id" ref="tvati"/>
</record>
<record id="afptt_inverse_3" model="account.fiscal.position.tax.template">
<field name="position_id" ref="fiscal_position_template_2"/>
<field name="tax_src_id" ref="tvac_09"/>
<field name="tax_dest_id" ref="tvati"/>
</record>
<record id="afptt_inverse_4" model="account.fiscal.position.tax.template">
<field name="position_id" ref="fiscal_position_template_2"/>
<field name="tax_src_id" ref="tvac_19"/>
<field name="tax_dest_id" ref="tvati"/>
</record>
<record id="afptt_inverse_5" model="account.fiscal.position.tax.template">
<field name="position_id" ref="fiscal_position_template_2"/>
<field name="tax_src_id" ref="tvac_24"/>
<field name="tax_dest_id" ref="tvati"/>
</record>
<!-- Purchases -->
<record id="afptt_inverse_6" model="account.fiscal.position.tax.template">
<field name="position_id" ref="fiscal_position_template_2"/>
<field name="tax_src_id" ref="tvad_00"/>
<field name="tax_dest_id" ref="tvad_00"/>
</record>
<record id="afptt_inverse_7" model="account.fiscal.position.tax.template">
<field name="position_id" ref="fiscal_position_template_2"/>
<field name="tax_src_id" ref="tvad_00"/>
<field name="tax_dest_id" ref="tvac_00"/>
</record>
<record id="afptt_inverse_8" model="account.fiscal.position.tax.template">
<field name="position_id" ref="fiscal_position_template_2"/>
<field name="tax_src_id" ref="tvad_05"/>
<field name="tax_dest_id" ref="tvad_05"/>
</record>
<record id="afptt_inverse_9" model="account.fiscal.position.tax.template">
<field name="position_id" ref="fiscal_position_template_2"/>
<field name="tax_src_id" ref="tvad_05"/>
<field name="tax_dest_id" ref="tvac_05"/>
</record>
<record id="afptt_inverse_10" model="account.fiscal.position.tax.template">
<field name="position_id" ref="fiscal_position_template_2"/>
<field name="tax_src_id" ref="tvad_09"/>
<field name="tax_dest_id" ref="tvad_09"/>
</record>
<record id="afptt_inverse_11" model="account.fiscal.position.tax.template">
<field name="position_id" ref="fiscal_position_template_2"/>
<field name="tax_src_id" ref="tvad_09"/>
<field name="tax_dest_id" ref="tvac_09"/>
</record>
<record id="afptt_inverse_12" model="account.fiscal.position.tax.template">
<field name="position_id" ref="fiscal_position_template_2"/>
<field name="tax_src_id" ref="tvad_19"/>
<field name="tax_dest_id" ref="tvad_19"/>
</record>
<record id="afptt_inverse_13" model="account.fiscal.position.tax.template">
<field name="position_id" ref="fiscal_position_template_2"/>
<field name="tax_src_id" ref="tvad_19"/>
<field name="tax_dest_id" ref="tvac_19"/>
</record>
<record id="afptt_inverse_14" model="account.fiscal.position.tax.template">
<field name="position_id" ref="fiscal_position_template_2"/>
<field name="tax_src_id" ref="tvad_24"/>
<field name="tax_dest_id" ref="tvad_24"/>
</record>
<record id="afptt_inverse_15" model="account.fiscal.position.tax.template">
<field name="position_id" ref="fiscal_position_template_2"/>
<field name="tax_src_id" ref="tvad_24"/>
<field name="tax_dest_id" ref="tvac_24"/>
</record>
<!-- Intracomunitar Bunuri -->
<!-- Sales -->
<record id="afptt_intracom_1" model="account.fiscal.position.tax.template">
<field name="position_id" ref="fiscal_position_template_3"/>
<field name="tax_src_id" ref="tvac_00"/>
<field name="tax_dest_id" ref="tvati"/>
</record>
<record id="afptt_intracom_2" model="account.fiscal.position.tax.template">
<field name="position_id" ref="fiscal_position_template_3"/>
<field name="tax_src_id" ref="tvac_05"/>
<field name="tax_dest_id" ref="tvati"/>
</record>
<record id="afptt_intracom_3" model="account.fiscal.position.tax.template">
<field name="position_id" ref="fiscal_position_template_3"/>
<field name="tax_src_id" ref="tvac_09"/>
<field name="tax_dest_id" ref="tvati"/>
</record>
<record id="afptt_intracom_4" model="account.fiscal.position.tax.template">
<field name="position_id" ref="fiscal_position_template_3"/>
<field name="tax_src_id" ref="tvac_19"/>
<field name="tax_dest_id" ref="tvati"/>
</record>
<record id="afptt_intracom_5" model="account.fiscal.position.tax.template">
<field name="position_id" ref="fiscal_position_template_3"/>
<field name="tax_src_id" ref="tvac_24"/>
<field name="tax_dest_id" ref="tvati"/>
</record>
<!-- Purchases -->
<record id="afptt_intracom_6" model="account.fiscal.position.tax.template">
<field name="position_id" ref="fiscal_position_template_3"/>
<field name="tax_src_id" ref="tvad_00"/>
<field name="tax_dest_id" ref="tvad_00"/>
</record>
<record id="afptt_intracom_7" model="account.fiscal.position.tax.template">
<field name="position_id" ref="fiscal_position_template_3"/>
<field name="tax_src_id" ref="tvad_00"/>
<field name="tax_dest_id" ref="tvac_00"/>
</record>
<record id="afptt_intracom_8" model="account.fiscal.position.tax.template">
<field name="position_id" ref="fiscal_position_template_3"/>
<field name="tax_src_id" ref="tvad_05"/>
<field name="tax_dest_id" ref="tvad_05"/>
</record>
<record id="afptt_intracom_9" model="account.fiscal.position.tax.template">
<field name="position_id" ref="fiscal_position_template_3"/>
<field name="tax_src_id" ref="tvad_05"/>
<field name="tax_dest_id" ref="tvac_05"/>
</record>
<record id="afptt_intracom_10" model="account.fiscal.position.tax.template">
<field name="position_id" ref="fiscal_position_template_3"/>
<field name="tax_src_id" ref="tvad_09"/>
<field name="tax_dest_id" ref="tvad_09"/>
</record>
<record id="afptt_intracom_11" model="account.fiscal.position.tax.template">
<field name="position_id" ref="fiscal_position_template_3"/>
<field name="tax_src_id" ref="tvad_09"/>
<field name="tax_dest_id" ref="tvac_09"/>
</record>
<record id="afptt_intracom_12" model="account.fiscal.position.tax.template">
<field name="position_id" ref="fiscal_position_template_3"/>
<field name="tax_src_id" ref="tvad_19"/>
<field name="tax_dest_id" ref="tvad_19"/>
</record>
<record id="afptt_intracom_13" model="account.fiscal.position.tax.template">
<field name="position_id" ref="fiscal_position_template_3"/>
<field name="tax_src_id" ref="tvad_19"/>
<field name="tax_dest_id" ref="tvac_19"/>
</record>
<record id="afptt_intracom_14" model="account.fiscal.position.tax.template">
<field name="position_id" ref="fiscal_position_template_3"/>
<field name="tax_src_id" ref="tvad_24"/>
<field name="tax_dest_id" ref="tvad_24"/>
</record>
<record id="afptt_intracom_15" model="account.fiscal.position.tax.template">
<field name="position_id" ref="fiscal_position_template_3"/>
<field name="tax_src_id" ref="tvad_24"/>
<field name="tax_dest_id" ref="tvac_24"/>
</record>
<!-- Intracomunitar Servicii -->
<!-- Sales -->
<record id="afptt_intracoms_1" model="account.fiscal.position.tax.template">
<field name="position_id" ref="fiscal_position_template_4"/>
<field name="tax_src_id" ref="tvac_00"/>
<field name="tax_dest_id" ref="tvati"/>
</record>
<record id="afptt_intracoms_2" model="account.fiscal.position.tax.template">
<field name="position_id" ref="fiscal_position_template_4"/>
<field name="tax_src_id" ref="tvac_05"/>
<field name="tax_dest_id" ref="tvati"/>
</record>
<record id="afptt_intracoms_3" model="account.fiscal.position.tax.template">
<field name="position_id" ref="fiscal_position_template_4"/>
<field name="tax_src_id" ref="tvac_09"/>
<field name="tax_dest_id" ref="tvati"/>
</record>
<record id="afptt_intracoms_4" model="account.fiscal.position.tax.template">
<field name="position_id" ref="fiscal_position_template_4"/>
<field name="tax_src_id" ref="tvac_19"/>
<field name="tax_dest_id" ref="tvati"/>
</record>
<record id="afptt_intracoms_5" model="account.fiscal.position.tax.template">
<field name="position_id" ref="fiscal_position_template_4"/>
<field name="tax_src_id" ref="tvac_24"/>
<field name="tax_dest_id" ref="tvati"/>
</record>
<!-- Purchases -->
<record id="afptt_intracoms_6" model="account.fiscal.position.tax.template">
<field name="position_id" ref="fiscal_position_template_4"/>
<field name="tax_src_id" ref="tvad_00"/>
<field name="tax_dest_id" ref="tvad_00"/>
</record>
<record id="afptt_intracoms_7" model="account.fiscal.position.tax.template">
<field name="position_id" ref="fiscal_position_template_4"/>
<field name="tax_src_id" ref="tvad_00"/>
<field name="tax_dest_id" ref="tvac_00"/>
</record>
<record id="afptt_intracoms_8" model="account.fiscal.position.tax.template">
<field name="position_id" ref="fiscal_position_template_4"/>
<field name="tax_src_id" ref="tvad_05"/>
<field name="tax_dest_id" ref="tvad_05"/>
</record>
<record id="afptt_intracoms_9" model="account.fiscal.position.tax.template">
<field name="position_id" ref="fiscal_position_template_4"/>
<field name="tax_src_id" ref="tvad_05"/>
<field name="tax_dest_id" ref="tvac_05"/>
</record>
<record id="afptt_intracoms_10" model="account.fiscal.position.tax.template">
<field name="position_id" ref="fiscal_position_template_4"/>
<field name="tax_src_id" ref="tvad_09"/>
<field name="tax_dest_id" ref="tvad_09"/>
</record>
<record id="afptt_intracoms_11" model="account.fiscal.position.tax.template">
<field name="position_id" ref="fiscal_position_template_4"/>
<field name="tax_src_id" ref="tvad_09"/>
<field name="tax_dest_id" ref="tvac_09"/>
</record>
<record id="afptt_intracoms_12" model="account.fiscal.position.tax.template">
<field name="position_id" ref="fiscal_position_template_4"/>
<field name="tax_src_id" ref="tvad_19"/>
<field name="tax_dest_id" ref="tvad_19"/>
</record>
<record id="afptt_intracoms_13" model="account.fiscal.position.tax.template">
<field name="position_id" ref="fiscal_position_template_4"/>
<field name="tax_src_id" ref="tvad_19"/>
<field name="tax_dest_id" ref="tvac_19"/>
</record>
<record id="afptt_intracoms_14" model="account.fiscal.position.tax.template">
<field name="position_id" ref="fiscal_position_template_4"/>
<field name="tax_src_id" ref="tvad_24"/>
<field name="tax_dest_id" ref="tvad_24"/>
</record>
<record id="afptt_intracoms_15" model="account.fiscal.position.tax.template">
<field name="position_id" ref="fiscal_position_template_4"/>
<field name="tax_src_id" ref="tvad_24"/>
<field name="tax_dest_id" ref="tvac_24"/>
</record>
<!-- Intracomunitar Scutite -->
<!-- Sales -->
<record id="afptt_intracomsc_1" model="account.fiscal.position.tax.template">
<field name="position_id" ref="fiscal_position_template_5"/>
<field name="tax_src_id" ref="tvac_00"/>
<field name="tax_dest_id" ref="tvatisc"/>
</record>
<record id="afptt_intracomsc_2" model="account.fiscal.position.tax.template">
<field name="position_id" ref="fiscal_position_template_5"/>
<field name="tax_src_id" ref="tvac_05"/>
<field name="tax_dest_id" ref="tvatisc"/>
</record>
<record id="afptt_intracomsc_3" model="account.fiscal.position.tax.template">
<field name="position_id" ref="fiscal_position_template_5"/>
<field name="tax_src_id" ref="tvac_09"/>
<field name="tax_dest_id" ref="tvatisc"/>
</record>
<record id="afptt_intracomsc_4" model="account.fiscal.position.tax.template">
<field name="position_id" ref="fiscal_position_template_5"/>
<field name="tax_src_id" ref="tvac_19"/>
<field name="tax_dest_id" ref="tvatisc"/>
</record>
<record id="afptt_intracomsc_5" model="account.fiscal.position.tax.template">
<field name="position_id" ref="fiscal_position_template_5"/>
<field name="tax_src_id" ref="tvac_24"/>
<field name="tax_dest_id" ref="tvatisc"/>
</record>
<!-- Purchases -->
<record id="afptt_intracomsc_6" model="account.fiscal.position.tax.template">
<field name="position_id" ref="fiscal_position_template_5"/>
<field name="tax_src_id" ref="tvad_00"/>
<field name="tax_dest_id" ref="tvatisc"/>
</record>
<record id="afptt_intracomsc_7" model="account.fiscal.position.tax.template">
<field name="position_id" ref="fiscal_position_template_5"/>
<field name="tax_src_id" ref="tvad_05"/>
<field name="tax_dest_id" ref="tvatisc"/>
</record>
<record id="afptt_intracomsc_8" model="account.fiscal.position.tax.template">
<field name="position_id" ref="fiscal_position_template_5"/>
<field name="tax_src_id" ref="tvad_09"/>
<field name="tax_dest_id" ref="tvatisc"/>
</record>
<record id="afptt_intracomsc_9" model="account.fiscal.position.tax.template">
<field name="position_id" ref="fiscal_position_template_5"/>
<field name="tax_src_id" ref="tvad_19"/>
<field name="tax_dest_id" ref="tvatisc"/>
</record>
<record id="afptt_intracomsc_10" model="account.fiscal.position.tax.template">
<field name="position_id" ref="fiscal_position_template_5"/>
<field name="tax_src_id" ref="tvad_24"/>
<field name="tax_dest_id" ref="tvatisc"/>
</record>
<!-- Taxare Inversa - Neimpozabile -->
<!-- Sales -->
<record id="afptt_intracomne_1" model="account.fiscal.position.tax.template">
<field name="position_id" ref="fiscal_position_template_6"/>
<field name="tax_src_id" ref="tvac_00"/>
<field name="tax_dest_id" ref="tvatine"/>
</record>
<record id="afptt_intracomne_2" model="account.fiscal.position.tax.template">
<field name="position_id" ref="fiscal_position_template_6"/>
<field name="tax_src_id" ref="tvac_05"/>
<field name="tax_dest_id" ref="tvatine"/>
</record>
<record id="afptt_intracomne_3" model="account.fiscal.position.tax.template">
<field name="position_id" ref="fiscal_position_template_6"/>
<field name="tax_src_id" ref="tvac_09"/>
<field name="tax_dest_id" ref="tvatine"/>
</record>
<record id="afptt_intracomne_4" model="account.fiscal.position.tax.template">
<field name="position_id" ref="fiscal_position_template_6"/>
<field name="tax_src_id" ref="tvac_19"/>
<field name="tax_dest_id" ref="tvatine"/>
</record>
<record id="afptt_intracomne_5" model="account.fiscal.position.tax.template">
<field name="position_id" ref="fiscal_position_template_6"/>
<field name="tax_src_id" ref="tvac_24"/>
<field name="tax_dest_id" ref="tvatine"/>
</record>
<!-- Purchases -->
<record id="afptt_intracomne_6" model="account.fiscal.position.tax.template">
<field name="position_id" ref="fiscal_position_template_6"/>
<field name="tax_src_id" ref="tvad_00"/>
<field name="tax_dest_id" ref="tvatine"/>
</record>
<record id="afptt_intracomne_7" model="account.fiscal.position.tax.template">
<field name="position_id" ref="fiscal_position_template_6"/>
<field name="tax_src_id" ref="tvad_05"/>
<field name="tax_dest_id" ref="tvatine"/>
</record>
<record id="afptt_intracomne_8" model="account.fiscal.position.tax.template">
<field name="position_id" ref="fiscal_position_template_6"/>
<field name="tax_src_id" ref="tvad_09"/>
<field name="tax_dest_id" ref="tvatine"/>
</record>
<record id="afptt_intracomne_9" model="account.fiscal.position.tax.template">
<field name="position_id" ref="fiscal_position_template_6"/>
<field name="tax_src_id" ref="tvad_19"/>
<field name="tax_dest_id" ref="tvatine"/>
</record>
<record id="afptt_intracomne_10" model="account.fiscal.position.tax.template">
<field name="position_id" ref="fiscal_position_template_6"/>
<field name="tax_src_id" ref="tvad_24"/>
<field name="tax_dest_id" ref="tvatine"/>
</record>
</data>
</openerp>

View File

@ -1,5 +1,6 @@
<openerp> <openerp>
<data noupdate="1"> <data noupdate="1">
<record id="account.action_wizard_multi_chart_todo" model="ir.actions.todo"> <record id="account.action_wizard_multi_chart_todo" model="ir.actions.todo">
<field name="state">open</field> <field name="state">open</field>
</record> </record>

6
addons/l10n_ro/partner_view.xml Normal file → Executable file
View File

@ -3,12 +3,12 @@
<data> <data>
<record model="ir.ui.view" id="res_partner_form_ro"> <record model="ir.ui.view" id="res_partner_form_ro">
<field name="name">res.partner.form.ro</field> <field name="name">res.partner.form.ro</field>
<field name="inherit_id" ref="base_vat.view_partner_form"/> <field name="inherit_id" ref="base.view_partner_form"/>
<field name="model">res.partner</field> <field name="model">res.partner</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<xpath expr="//div[@name='vat_info']" position="after"> <field name="property_account_position" position="after">
<field name="nrc" placeholder="e.g. J12/1234/2012" class="oe_inline"/> <field name="nrc" placeholder="e.g. J12/1234/2012" class="oe_inline"/>
</xpath> </field>
</field> </field>
</record> </record>
</data> </data>

28
addons/l10n_ro/res_partner.py Normal file → Executable file
View File

@ -1,8 +1,9 @@
# -*- encoding: utf-8 -*- # -*- encoding: utf-8 -*-
############################################################################## ##############################################################################
# #
# OpenERP, Open Source Management Solution # @author - Fekete Mihai <feketemihai@gmail.com>
# Copyright (C) 2012 (<http://www.erpsystems.ro>). All Rights Reserved # Copyright (C) 2011 TOTAL PC SYSTEMS (http://www.www.erpsystems.ro).
# Copyright (C) 2009 (<http://www.filsystem.ro>)
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by
@ -25,34 +26,23 @@ class res_partner(osv.osv):
_name = "res.partner" _name = "res.partner"
_inherit = "res.partner" _inherit = "res.partner"
_columns = { _columns = {
'nrc' : fields.char('NRC', size=16, help='Registration number at the Registry of Commerce'), 'nrc' : fields.char('NRC', help='Registration number at the Registry of Commerce'),
} }
# The SQL constraints are no-ops but present only to display the right error message to the
# user when the partial unique indexes defined below raise errors/
# The real constraints need to be implemented with PARTIAL UNIQUE INDEXES (see auto_init),
# due to the way accounting data is delegated by contacts to their companies in OpenERP 7.0.
_sql_constraints = [
('vat_uniq', 'unique (id)', 'The vat of the partner must be unique !'),
('nrc_uniq', 'unique (id)', 'The code of the partner must be unique !')
]
def _auto_init(self, cr, context=None): def _auto_init(self, cr, context=None):
result = super(res_partner, self)._auto_init(cr, context=context) result = super(res_partner, self)._auto_init(cr, context=context)
# Real implementation of the vat/nrc constraints: only "commercial entities" need to have # Remove constrains for vat, nrc on "commercial entities" because is not mandatory by legislation
# unique numbers, and the condition for being a commercial entity is "is_company or parent_id IS NULL". # Even that VAT numbers are unique, the NRC field is not unique, and there are certain entities that
# Contacts inside a company automatically have a copy of the company's commercial fields # doesn't have a NRC number plus the formatting was changed few times, so we cannot have a base rule for
# (see _commercial_fields()), so they are automatically consistent. # checking if available and emmited by the Ministry of Finance, only online on their website.
cr.execute(""" cr.execute("""
DROP INDEX IF EXISTS res_partner_vat_uniq_for_companies; DROP INDEX IF EXISTS res_partner_vat_uniq_for_companies;
DROP INDEX IF EXISTS res_partner_nrc_uniq_for_companies; DROP INDEX IF EXISTS res_partner_nrc_uniq_for_companies;
CREATE UNIQUE INDEX res_partner_vat_uniq_for_companies ON res_partner (vat) WHERE is_company OR parent_id IS NULL;
CREATE UNIQUE INDEX res_partner_nrc_uniq_for_companies ON res_partner (nrc) WHERE is_company OR parent_id IS NULL;
""") """)
return result return result
def _commercial_fields(self, cr, uid, context=None): def _commercial_fields(self, cr, uid, context=None):
return super(res_partner, self)._commercial_fields(cr, uid, context=context) + ['nrc'] return super(res_partner, self)._commercial_fields(cr, uid, context=context) + ['nrc']
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -18,29 +18,24 @@
<search string="Tasks"> <search string="Tasks">
<field name="name" string="Tasks"/> <field name="name" string="Tasks"/>
<field name="categ_ids"/> <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="partner_id"/>
<field name="project_id"/> <field name="project_id"/>
<field name="reviewer_id"/> <field name="reviewer_id"/>
<field name="user_id"/> <field name="user_id"/>
<field name="stage_id" domain="[]"/> <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"> <group expand="0" string="Group By">
<filter string="Reviewers" name="group_reviewer_id" domain="[]" context="{'group_by':'reviewer_id'}"/> <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="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="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="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="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="Last Message" name="group_message_last_post" domain="[]" context="{'group_by':'message_last_post:week'}"/>
<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"/>
</group> </group>
</search> </search>
</field> </field>
@ -190,15 +185,14 @@
<field name="arch" type="xml"> <field name="arch" type="xml">
<search string="Search Project"> <search string="Search Project">
<field name="name" string="Project Name"/> <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/> <separator/>
<filter icon="terp-check" string="Open" name="Current" domain="[('state', '=','open')]" help="Open Projects"/> <filter string="Member" domain="['|',('user_id', '=', uid),('members', '=', uid)]"/>
<filter icon="gtk-media-pause" string="Pending" name="Pending" domain="[('state', '=','pending')]" help="Pending Projects"/> <filter string="Manager" domain="[('user_id','=',uid)]"/>
<filter icon="gtk-media-pause" string="Template" name="Template" domain="[('state', '=','template')]" help="Templates of Projects"/>
<separator/> <separator/>
<filter icon="terp-personal+" string="Member" domain="['|',('user_id', '=', uid),('members', '=', uid)]" help="Projects in which I am a member."/> <filter string="New Mail" name="message_unread" domain="[('message_unread','=',True)]"/>
<separator/>
<filter string="Project(s) Manager" domain="[('user_id','=',uid)]" help="Projects in which I am a manager" icon="terp-personal"/>
<field name="user_id" string="Project Manager"/> <field name="user_id" string="Project Manager"/>
<field name="partner_id" string="Contact" filter_domain="[('partner_id', 'child_of', self)]"/> <field name="partner_id" string="Contact" filter_domain="[('partner_id', 'child_of', self)]"/>
<group expand="0" string="Group By"> <group expand="0" string="Group By">

View File

@ -30,6 +30,7 @@ class report_project_task_user(osv.osv):
_columns = { _columns = {
'name': fields.char('Task Summary', readonly=True), 'name': fields.char('Task Summary', readonly=True),
'user_id': fields.many2one('res.users', 'Assigned To', 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), 'date_start': fields.date('Assignation Date', readonly=True),
'no_of_days': fields.integer('# of Days', size=128, readonly=True), 'no_of_days': fields.integer('# of Days', size=128, readonly=True),
'date_end': fields.date('Ending Date', 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, -- 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, abs((extract('epoch' from (t.write_date-t.date_start)))/(3600*24)) as no_of_days,
t.user_id, t.user_id,
t.reviewer_id,
progress as progress, progress as progress,
t.project_id, t.project_id,
t.effective_hours as hours_effective, t.effective_hours as hours_effective,
@ -103,6 +105,7 @@ class report_project_task_user(osv.osv):
date_deadline, date_deadline,
date_last_stage_update, date_last_stage_update,
t.user_id, t.user_id,
t.reviewer_id,
t.project_id, t.project_id,
t.priority, t.priority,
name, name,

View File

@ -21,6 +21,22 @@
</field> </field>
</record> </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"> <record id="view_task_project_user_search" model="ir.ui.view">
<field name="name">report.project.task.user.search</field> <field name="name">report.project.task.user.search</field>
<field name="model">report.project.task.user</field> <field name="model">report.project.task.user</field>
@ -34,25 +50,14 @@
<field name="user_id"/> <field name="user_id"/>
<field name="partner_id" filter_domain="[('partner_id', 'child_of', self)]"/> <field name="partner_id" filter_domain="[('partner_id', 'child_of', self)]"/>
<field name="stage_id"/> <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="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..."> <group expand="0" string="Extended Filters...">
<field name="priority"/> <field name="priority"/>
<field name="company_id" groups="base.group_multi_company"/> <field name="company_id" groups="base.group_multi_company"/>
</group> </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> </search>
</field> </field>
</record> </record>

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="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="id"/>
<field name="partner_id" operator="child_of"/> <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="user_id"/>
<field name="project_id"/> <field name="project_id"/>
<field name="categ_ids"/> <field name="categ_ids"/>
<field name="stage_id" domain="[]"/> <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" > <group expand="0" string="Group By" >
<filter string="Responsible" name="group_user_id" icon="terp-personal" domain="[]" context="{'group_by':'user_id'}"/> <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="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="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="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="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> </group>
</search> </search>
</field> </field>

View File

@ -13,6 +13,22 @@
</field> </field>
</record> </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"> <record id="view_project_issue_report_filter" model="ir.ui.view">
<field name="name">project.issue.report.select</field> <field name="name">project.issue.report.select</field>
<field name="model">project.issue.report</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="partner_id" filter_domain="[('partner_id', 'child_of', self)]"/>
<field name="version_id"/> <field name="version_id"/>
<field name="stage_id"/> <field name="stage_id"/>
<filter string="My Task" domain="[('user_id','=',uid)]" />
<filter string="Unassigned" name="unassigned" domain="[('user_id','=',False)]"/> <filter string="Unassigned" name="unassigned" domain="[('user_id','=',False)]"/>
<filter string="New" name="new" domain="[('stage_id.sequence', '=', 1)]"/> <separator/>
<filter string="Done" name="done" domain="[('stage_id.fold', '=', True)]" <filter string="New" name="new" domain="[('stage_id.sequence', '&lt;=', 1)]"/>
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>
</search> </search>
</field> </field>
</record> </record>

View File

@ -41,8 +41,8 @@
<field name="model">project.project</field> <field name="model">project.project</field>
<field name="inherit_id" ref="project.view_project_project_filter"/> <field name="inherit_id" ref="project.view_project_project_filter"/>
<field name="arch" type="xml"> <field name="arch" type="xml">
<xpath expr='//filter[@string="Project(s) Manager"]' position='after'> <xpath expr='//filter[@string="Template"]' position='after'>
<filter icon="terp-camera_test" string="Billable" domain="[('to_invoice','!=', False)]" help="Billable Project"/> <filter string="Billable" domain="[('to_invoice','!=', False)]"/>
</xpath> </xpath>
</field> </field>
</record> </record>

View File

@ -305,18 +305,16 @@
<field name="arch" type="xml"> <field name="arch" type="xml">
<search string="Search Purchase Order"> <search string="Search Purchase Order">
<field name="name" string="Reference"/> <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="partner_id" operator="child_of"/>
<field name="product_id"/> <field name="product_id"/>
<field name="create_uid"/> <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"> <group expand="0" string="Group By">
<filter string="Supplier" icon="terp-partner" domain="[]" context="{'group_by':'partner_id'}"/> <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="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'}"/> <filter string="Expected Date" icon="terp-go-month" domain="[]" context="{'group_by':'minimum_planned_date'}"/>
</group> </group>
@ -330,20 +328,18 @@
<field name="arch" type="xml"> <field name="arch" type="xml">
<search string="Search Purchase Order"> <search string="Search Purchase Order">
<field name="name" string="Reference"/> <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="partner_id" operator="child_of"/>
<field name="product_id"/> <field name="product_id"/>
<field name="create_uid"/> <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"> <group expand="0" string="Group By">
<filter string="Supplier" icon="terp-partner" domain="[]" context="{'group_by':'partner_id'}"/> <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="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'}"/> <filter string="Expected Month" icon="terp-go-month" domain="[]" context="{'group_by':'minimum_planned_date'}"/>
</group> </group>

View File

@ -36,22 +36,6 @@
<field name="date_approve"/> <field name="date_approve"/>
<field name="expected_date"/> <field name="expected_date"/>
</group> </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> </search>
</field> </field>
</record> </record>

View File

@ -50,7 +50,12 @@ class ReportController(Controller):
if data.get('options'): if data.get('options'):
options_data = simplejson.loads(data['options']) options_data = simplejson.loads(data['options'])
if data.get('context'): if data.get('context'):
context.update(simplejson.loads(data['context'])) # Ignore 'lang' here, because the context in data is the one from the webclient *but* if
# the user explicitely wants to change the lang, this mechanism overwrites it.
data_context = simplejson.loads(data['context'])
if data_context.get('lang'):
del data_context['lang']
context.update(data_context)
if converter == 'html': if converter == 'html':
html = report_obj.get_html(cr, uid, docids, reportname, data=options_data, context=context) html = report_obj.get_html(cr, uid, docids, reportname, data=options_data, context=context)

View File

@ -34,10 +34,8 @@ import lxml.html
import cStringIO import cStringIO
import subprocess import subprocess
from distutils.version import LooseVersion from distutils.version import LooseVersion
try: from functools import partial
from pyPdf import PdfFileWriter, PdfFileReader from pyPdf import PdfFileWriter, PdfFileReader
except ImportError:
PdfFileWriter = PdfFileReader = None
_logger = logging.getLogger(__name__) _logger = logging.getLogger(__name__)
@ -71,23 +69,6 @@ class Report(osv.Model):
public_user = None public_user = None
MINIMAL_HTML_PAGE = """
<base href="{base_url}">
<!DOCTYPE html>
<html style="height: 0;">
<head>
<link href="/report/static/src/css/reset.min.css" rel="stylesheet"/>
<link href="/web/static/lib/bootstrap/css/bootstrap.css" rel="stylesheet"/>
<link href="/website/static/src/css/website.css" rel="stylesheet"/>
<link href="/web/static/lib/fontawesome/css/font-awesome.css" rel="stylesheet"/>
<style type='text/css'>{css}</style>
{subst}
</head>
<body class="container" onload="subst()">
{body}
</body>
</html>"""
#-------------------------------------------------------------------------- #--------------------------------------------------------------------------
# Extension of ir_ui_view.render with arguments frequently used in reports # Extension of ir_ui_view.render with arguments frequently used in reports
#-------------------------------------------------------------------------- #--------------------------------------------------------------------------
@ -133,7 +114,9 @@ class Report(osv.Model):
user = self.pool['res.users'].browse(cr, uid, uid) user = self.pool['res.users'].browse(cr, uid, uid)
website = None website = None
if request and hasattr(request, 'website'): if request and hasattr(request, 'website'):
if request.website is not None:
website = request.website website = request.website
context.update(translatable=context.get('lang') != request.website.default_lang_code)
values.update( values.update(
time=time, time=time,
translate_doc=translate_doc, translate_doc=translate_doc,
@ -141,7 +124,7 @@ class Report(osv.Model):
user=user, user=user,
res_company=user.company_id, res_company=user.company_id,
website=website, website=website,
editable_no_editor=True, editable_no_editor=_("The preferred way to edit a report is to use the HTML Editor"),
) )
return view_obj.render(cr, uid, template, values, context=context) return view_obj.render(cr, uid, template, values, context=context)
@ -191,13 +174,16 @@ class Report(osv.Model):
paperformat = report.paperformat_id paperformat = report.paperformat_id
# Preparing the minimal html pages # Preparing the minimal html pages
subst = "<script src='/report/static/src/js/subst.js'></script> "
css = '' # Will contain local css css = '' # Will contain local css
headerhtml = [] headerhtml = []
contenthtml = [] contenthtml = []
footerhtml = [] footerhtml = []
base_url = self.pool['ir.config_parameter'].get_param(cr, uid, 'web.base.url') base_url = self.pool['ir.config_parameter'].get_param(cr, uid, 'web.base.url')
# Minimal page renderer
view_obj = self.pool['ir.ui.view']
render_minimal = partial(view_obj.render, cr, uid, 'report.minimal_layout', context=context)
# The received html report must be simplified. We convert it in a xml tree # The received html report must be simplified. We convert it in a xml tree
# in order to extract headers, bodies and footers. # in order to extract headers, bodies and footers.
try: try:
@ -208,12 +194,12 @@ class Report(osv.Model):
for node in root.xpath("//div[@class='header']"): for node in root.xpath("//div[@class='header']"):
body = lxml.html.tostring(node) body = lxml.html.tostring(node)
header = self.MINIMAL_HTML_PAGE.format(css=css, subst=subst, body=body, base_url=base_url) header = render_minimal(dict(css=css, subst=True, body=body, base_url=base_url))
headerhtml.append(header) headerhtml.append(header)
for node in root.xpath("//div[@class='footer']"): for node in root.xpath("//div[@class='footer']"):
body = lxml.html.tostring(node) body = lxml.html.tostring(node)
footer = self.MINIMAL_HTML_PAGE.format(css=css, subst=subst, body=body, base_url=base_url) footer = render_minimal(dict(css=css, subst=True, body=body, base_url=base_url))
footerhtml.append(footer) footerhtml.append(footer)
for node in root.xpath("//div[@class='page']"): for node in root.xpath("//div[@class='page']"):
@ -230,7 +216,7 @@ class Report(osv.Model):
reportid = False reportid = False
body = lxml.html.tostring(node) body = lxml.html.tostring(node)
reportcontent = self.MINIMAL_HTML_PAGE.format(css=css, subst='', body=body, base_url=base_url) reportcontent = render_minimal(dict(css=css, subst=False, body=body, base_url=base_url))
# FIXME: imo the best way to extract record id from html reports is by using the # FIXME: imo the best way to extract record id from html reports is by using the
# qweb branding. As website editor is not yet splitted in a module independant from # qweb branding. As website editor is not yet splitted in a module independant from
@ -254,11 +240,10 @@ class Report(osv.Model):
specific_paperformat_args[attribute[0]] = attribute[1] specific_paperformat_args[attribute[0]] = attribute[1]
# Run wkhtmltopdf process # Run wkhtmltopdf process
pdf = self._generate_wkhtml_pdf( return self._run_wkhtmltopdf(
cr, uid, headerhtml, footerhtml, contenthtml, context.get('landscape'), cr, uid, headerhtml, footerhtml, contenthtml, context.get('landscape'),
paperformat, specific_paperformat_args, save_in_attachment paperformat, specific_paperformat_args, save_in_attachment
) )
return pdf
def get_action(self, cr, uid, ids, report_name, data=None, context=None): def get_action(self, cr, uid, ids, report_name, data=None, context=None):
"""Return an action of type ir.actions.report.xml. """Return an action of type ir.actions.report.xml.
@ -329,7 +314,7 @@ class Report(osv.Model):
def _check_wkhtmltopdf(self): def _check_wkhtmltopdf(self):
return wkhtmltopdf_state return wkhtmltopdf_state
def _generate_wkhtml_pdf(self, cr, uid, headers, footers, bodies, landscape, paperformat, spec_paperformat_args=None, save_in_attachment=None): def _run_wkhtmltopdf(self, cr, uid, headers, footers, bodies, landscape, paperformat, spec_paperformat_args=None, save_in_attachment=None):
"""Execute wkhtmltopdf as a subprocess in order to convert html given in input into a pdf """Execute wkhtmltopdf as a subprocess in order to convert html given in input into a pdf
document. document.

View File

@ -1,55 +1,31 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<openerp> <openerp>
<data> <data>
<template id="html_container"> <template id="layout" inherit_id="web.layout" primary="True">
&lt;!DOCTYPE html&gt; <!-- Add report attributes -->
<html t-att-lang="lang and lang.replace('_', '-')" <xpath expr="//html" position="attributes">
t-att-data-editable="'1' if editable else None" <attribute name="t-att-data-report-margin-top">data_report_margin_top if data_report_margin_top else None</attribute>
t-att-data-view-xmlid="xmlid if editable else None" <attribute name="t-att-data-report-header-spacing">data_report_header_spacing if data_report_header_spacing else None</attribute>
t-att-data-main-object="repr(main_object) if editable else None" <attribute name="t-att-data-report-dpi">data_report_dpi if data_report_dpi else None</attribute>
t-att-data-report-margin-top="data_report_margin_top if data_report_margin_top else None" </xpath>
t-att-data-report-header-spacing="data_report_header_spacing if data_report_header_spacing else None" <!-- Add report style -->
t-att-data-report-dpi="data_report_dpi if data_report_dpi else None" <xpath expr="//head" position="inside">
t-att-data-oe-company-name="res_company.name"> <link href="/web/static/lib/bootstrap/css/bootstrap.css" rel="stylesheet"/>
<head> <link href="/website/static/src/css/website.css" rel="stylesheet"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/> <link href="/web/static/lib/fontawesome/css/font-awesome.css" rel="stylesheet"/>
<t t-if="main_object and 'website_meta_title' in main_object">
<t t-set="title" t-value="main_object.website_meta_title"/>
</t>
<t t-if="not title and main_object and 'name' in main_object">
<t t-set="additional_title" t-value="main_object.name"/>
</t>
<meta name="description" t-att-value="main_object and 'website_meta_description' in main_object
and main_object.website_meta_description or website_meta_description"/>
<meta name="keywords" t-att-value="main_object and 'website_meta_keywords' in main_object
and main_object.website_meta_keywords or website_meta_keywords"/>
<link id="bootstrap_css" rel='stylesheet' href='/web/static/lib/bootstrap/css/bootstrap.css' t-ignore="true"/>
<link rel="stylesheet" type="text/css" href='/website/static/src/css/website.css'/>
<style type="text/css"> <style type="text/css">
<t t-call="report.style"/> <t t-call="report.style"/>
</style> </style>
</xpath>
<!-- Remove conflicting style -->
<xpath expr="//head/link[@href='/web/static/src/css/full.css']" position="replace"></xpath>
</template>
<link rel='stylesheet' href='/web/static/lib/fontawesome/css/font-awesome.css'/> <template id="html_container">
<t t-set="body_classname" t-value="'container'"/>
<script type="text/javascript" src="/web/static/lib/es5-shim/es5-shim.min.js"></script> <t t-call="report.layout">
<script type="text/javascript" src="/web/static/lib/underscore/underscore.js"></script>
<script type="text/javascript" src="/web/static/lib/underscore.string/lib/underscore.string.js"></script>
<script type="text/javascript" src="/web/static/lib/jquery/jquery.js"></script>
<script type="text/javascript" src="/web/static/lib/bootstrap/js/bootstrap.js"></script>
<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="/website/static/src/js/website.js"></script>
<script t-if="not translatable" type="text/javascript" src="/website/static/src/js/website.snippets.animation.js"></script>
<t t-raw="head or ''" name='layout_head'/>
</head>
<body class="container">
<div id="wrapwrap">
<t t-raw="0"/> <t t-raw="0"/>
</div> </t>
</body>
</html>
</template> </template>
<template id="style"> <template id="style">
@ -164,5 +140,25 @@
<t t-raw="0" /> <t t-raw="0" />
</template> </template>
<template id="minimal_layout">
<t t-raw="'&lt;base href=%s&gt;' % base_url"/>
&lt;!DOCTYPE html&gt;
<html style="height: 0;">
<head>
<link href="/report/static/src/css/reset.min.css" rel="stylesheet"/>
<link href="/web/static/lib/bootstrap/css/bootstrap.css" rel="stylesheet"/>
<link href="/website/static/src/css/website.css" rel="stylesheet"/>
<link href="/web/static/lib/fontawesome/css/font-awesome.css" rel="stylesheet"/>
<style type='text/css'><t t-raw="css"/></style>
<t t-if="subst is True">
<script src='/report/static/src/js/subst.js'></script>
</t>
</head>
<body class="container" onload="subst()">
<t t-raw="body"/>
</body>
</html>
</template>
</data> </data>
</openerp> </openerp>

View File

@ -14,6 +14,29 @@
</field> </field>
</record> </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"> <record id="view_order_product_search" model="ir.ui.view">
<field name="name">sale.report.search</field> <field name="name">sale.report.search</field>
<field name="model">sale.report</field> <field name="model">sale.report</field>
@ -21,10 +44,12 @@
<search string="Sales Analysis"> <search string="Sales Analysis">
<field name="date"/> <field name="date"/>
<field name="date_confirm"/> <field name="date_confirm"/>
<filter icon="terp-document-new" name="Quotations" domain="[('state','in',('draft','sent'))]"/> <filter string="This Year" name="year" domain="[('date','&lt;=', time.strftime('%%Y-12-31')),('date','&gt;=',time.strftime('%%Y-01-01'))]"/>
<filter icon="terp-check" name="Sales" string="Sales" domain="[('state','not in',('draft','sent','cancel'))]"/>
<separator/> <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="partner_id"/>
<field name="product_id"/> <field name="product_id"/>
<field name="user_id"/> <field name="user_id"/>
@ -32,20 +57,6 @@
<field name="categ_id"/> <field name="categ_id"/>
<field name="company_id" groups="base.group_multi_company"/> <field name="company_id" groups="base.group_multi_company"/>
</group> </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> </search>
</field> </field>
</record> </record>
@ -57,7 +68,7 @@
<field name="view_mode">graph</field> <field name="view_mode">graph</field>
<field name="search_view_id" ref="view_order_product_search"/> <field name="search_view_id" ref="view_order_product_search"/>
<field name="view_id" ref="view_order_product_graph"/> <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> <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> </record>

View File

@ -246,24 +246,22 @@
<field name="arch" type="xml"> <field name="arch" type="xml">
<search string="Search Sales Order"> <search string="Search Sales Order">
<field name="name" string="Sales Order" filter_domain="['|',('name','ilike',self),('client_order_ref','ilike',self)]"/> <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="partner_id" operator="child_of"/>
<field name="user_id"/> <field name="user_id"/>
<field name="section_id" string="Sales Team" groups="base.group_multi_salesteams"/> <field name="section_id" string="Sales Team" groups="base.group_multi_salesteams"/>
<field name="project_id"/> <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"> <group expand="0" string="Group By">
<filter string="Customer" icon="terp-personal" domain="[]" context="{'group_by':'partner_id'}"/> <filter string="Salesperson" domain="[]" context="{'group_by':'user_id'}"/>
<filter string="Salesperson" icon="terp-personal" domain="[]" context="{'group_by':'user_id'}"/> <filter string="Customer" domain="[]" context="{'group_by':'partner_id'}"/>
<filter string="Status" icon="terp-stock_effects-object-colorize" domain="[]" context="{'group_by':'state'}"/> <filter string="Order Month" domain="[]" context="{'group_by':'date_order'}"/>
<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"/>
</group> </group>
</search> </search>
</field> </field>

View File

@ -10,9 +10,6 @@
<separator/> <separator/>
<filter icon="terp-accessories-archiver" string="Picked" domain="[('shipped','=',True)]"/> <filter icon="terp-accessories-archiver" string="Picked" domain="[('shipped','=',True)]"/>
</filter> </filter>
<xpath expr="//group/filter[@string='Status']" position="after">
<filter string="Warehouse" context="{'group_by':'warehouse_id'}"/>
</xpath>
</field> </field>
</record> </record>
</data> </data>

View File

@ -1564,6 +1564,52 @@
-webkit-border-radius: 2px; -webkit-border-radius: 2px;
border-radius: 2px; border-radius: 2px;
} }
.openerp .oe_searchview .oe-autocomplete {
display: none;
position: absolute;
width: 300px;
background-color: white;
border: 1px solid #afafb6;
z-index: 666;
margin-top: 2px;
cursor: default;
-moz-border-radius: 3px;
-webkit-border-radius: 3px;
border-radius: 3px;
-moz-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);
-webkit-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);
}
.openerp .oe_searchview .oe-autocomplete ul {
list-style-type: none;
padding-left: 0;
margin: 5px 0px;
}
.openerp .oe_searchview .oe-autocomplete ul li {
padding-left: 20px;
text-shadow: 0 0 0 white;
}
.openerp .oe_searchview .oe-autocomplete ul li span:first-child {
margin-right: 5px;
}
.openerp .oe_searchview .oe-autocomplete ul li span.oe-expand {
cursor: pointer;
}
.openerp .oe_searchview .oe-autocomplete ul li.oe-indent {
margin-left: 20px;
}
.openerp .oe_searchview .oe-autocomplete ul li.oe-selection-focus {
background-color: #7c7bad;
color: white;
}
.openerp .oe_searchview .oe-autocomplete ul li.oe-separator {
margin-top: 2px;
margin-bottom: 2px;
border-top: 1px solid #afafb6;
}
.openerp .oe_searchview .oe-autocomplete ul li.oe-separator:last-child {
display: none;
}
.openerp .oe_searchview_drawer_container { .openerp .oe_searchview_drawer_container {
overflow: auto; overflow: auto;
} }

View File

@ -1289,6 +1289,41 @@ $sheet-padding: 16px
text-shadow: 0 1px 1px rgba(0, 0, 0, 0.4) text-shadow: 0 1px 1px rgba(0, 0, 0, 0.4)
@include radius(2px) @include radius(2px)
.oe-autocomplete
display: none
position: absolute
width: 300px
background-color: white
border: 1px solid #afafb6
z-index: 666
margin-top: 2px
cursor: default
@include radius(3px)
@include box-shadow(0 1px 4px rgba(0, 0, 0, 0.3))
ul
list-style-type: none
padding-left: 0
margin: 5px 0px
li
padding-left: 20px
text-shadow: 0 0 0 white
span:first-child
margin-right: 5px
span.oe-expand
cursor: pointer
li.oe-indent
margin-left: 20px
li.oe-selection-focus
background-color: #7c7bad
color: white
li.oe-separator
margin-top: 2px
margin-bottom: 2px
border-top: 1px solid #afafb6
li.oe-separator:last-child
display: none
.oe_searchview_drawer_container .oe_searchview_drawer_container
overflow: auto overflow: auto
.oe_searchview_drawer .oe_searchview_drawer

View File

@ -1377,7 +1377,6 @@ instance.web.WebClient = instance.web.Client.extend({
var state = event.getState(true); var state = event.getState(true);
if(!state.action && state.menu_id) { if(!state.action && state.menu_id) {
self.menu.is_bound.done(function() { self.menu.is_bound.done(function() {
self.menu.do_reload();
self.menu.menu_click(state.menu_id); self.menu.menu_click(state.menu_id);
}); });
} else { } else {

View File

@ -350,7 +350,9 @@ instance.web.SearchView = instance.web.Widget.extend(/** @lends instance.web.Sea
e.preventDefault(); e.preventDefault();
break; break;
case $.ui.keyCode.RIGHT: case $.ui.keyCode.RIGHT:
if (!this.autocomplete.is_expandable()) {
this.focusFollowing(e.target); this.focusFollowing(e.target);
}
e.preventDefault(); e.preventDefault();
break; break;
} }
@ -484,53 +486,18 @@ instance.web.SearchView = instance.web.Widget.extend(/** @lends instance.web.Sea
* Sets up search view's view-wide auto-completion widget * Sets up search view's view-wide auto-completion widget
*/ */
setup_global_completion: function () { setup_global_completion: function () {
var autocomplete = this.$el.autocomplete({ var self = this;
this.autocomplete = new instance.web.search.AutoComplete(this, {
source: this.proxy('complete_global_search'), source: this.proxy('complete_global_search'),
select: this.proxy('select_completion'), select: this.proxy('select_completion'),
focus: function (e) { e.preventDefault(); }, delay: 0,
html: true, get_search_string: function () {
autoFocus: true,
minLength: 1,
delay: 250,
}).data('autocomplete');
this.$el.on('input', function () {
this.$el.autocomplete('close');
}.bind(this));
// MonkeyPatch autocomplete instance
_.extend(autocomplete, {
_renderItem: function (ul, item) {
// item of completion list
var $item = $( "<li></li>" )
.data( "item.autocomplete", item )
.appendTo( ul );
if (item.facet !== undefined) {
// regular completion item
if (item.first) {
$item.css('borderTop', '1px solid #cccccc');
}
return $item.append(
(item.label)
? $('<a>').html(item.label)
: $('<a>').text(item.value));
}
return $item.text(item.label)
.css({
borderTop: '1px solid #cccccc',
margin: 0,
padding: 0,
zoom: 1,
'float': 'left',
clear: 'left',
width: '100%'
});
},
_value: function() {
return self.$('div.oe_searchview_input').text(); return self.$('div.oe_searchview_input').text();
}, },
width: this.$el.width(),
}); });
this.autocomplete.appendTo(this.$el);
}, },
/** /**
* Provide auto-completion result for req.term (an array to `resp`) * Provide auto-completion result for req.term (an array to `resp`)
@ -546,12 +513,6 @@ instance.web.SearchView = instance.web.Widget.extend(/** @lends instance.web.Sea
.value()).then(function () { .value()).then(function () {
resp(_(arguments).chain() resp(_(arguments).chain()
.compact() .compact()
.map(function (completion) {
if (completion.length && completion[0].facet !== undefined) {
completion[0].first = true;
}
return completion;
})
.flatten(true) .flatten(true)
.value()); .value());
}); });
@ -579,14 +540,9 @@ instance.web.SearchView = instance.web.Widget.extend(/** @lends instance.web.Sea
childBlurred: function () { childBlurred: function () {
var val = this.$el.val(); var val = this.$el.val();
this.$el.val(''); this.$el.val('');
var complete = this.$el.data('autocomplete');
if ((val && complete.term === undefined) || complete.previous) {
throw new Error("new jquery.ui version altering implementation" +
" details relied on");
}
delete complete.term;
this.$el.removeClass('oe_focused') this.$el.removeClass('oe_focused')
.trigger('blur'); .trigger('blur');
this.autocomplete.close();
}, },
/** /**
* Call the renderFacets method with the correct arguments. * Call the renderFacets method with the correct arguments.
@ -846,13 +802,13 @@ instance.web.SearchViewDrawer = instance.web.Widget.extend({
var $first_col = this.$(".col-md-7"), var $first_col = this.$(".col-md-7"),
$snd_col = this.$(".col-md-5"); $snd_col = this.$(".col-md-5");
var add_custom_reports = in_drawer[0].appendTo($first_col), var add_custom_filters = in_drawer[0].appendTo($first_col),
add_filters = in_drawer[1].appendTo($first_col), add_filters = in_drawer[1].appendTo($first_col),
add_rest = $.when.apply(null, _(in_drawer.slice(2)).invoke('appendTo', $snd_col)), add_rest = $.when.apply(null, _(in_drawer.slice(2)).invoke('appendTo', $snd_col)),
defaults_fetched = $.when.apply(null, _(this.inputs).invoke( defaults_fetched = $.when.apply(null, _(this.inputs).invoke(
'facet_for_defaults', this.searchview.defaults)); 'facet_for_defaults', this.searchview.defaults));
return $.when(defaults_fetched, add_custom_reports, add_filters, add_rest); return $.when(defaults_fetched, add_custom_filters, add_filters, add_rest);
}, },
/** /**
* Sets up thingie where all the mess is put? * Sets up thingie where all the mess is put?
@ -890,8 +846,10 @@ instance.web.SearchViewDrawer = instance.web.Widget.extend({
filters.push(new instance.web.search.Filter(item, group)); filters.push(new instance.web.search.Filter(item, group));
break; break;
case 'group': case 'group':
self.add_separator();
self.make_widgets(item.children, fields, self.make_widgets(item.children, fields,
new instance.web.search.Group(group, 'w', item)); new instance.web.search.Group(group, 'w', item));
self.add_separator();
break; break;
case 'field': case 'field':
var field = this.make_field( var field = this.make_field(
@ -907,6 +865,11 @@ instance.web.SearchViewDrawer = instance.web.Widget.extend({
group.push(new instance.web.search.FilterGroup(filters, this)); group.push(new instance.web.search.FilterGroup(filters, this));
} }
}, },
add_separator: function () {
if (!(_.last(this.inputs) instanceof instance.web.search.Separator))
new instance.web.search.Separator(this);
},
/** /**
* Creates a field for the provided field descriptor item (which comes * Creates a field for the provided field descriptor item (which comes
* from fields_view_get) * from fields_view_get)
@ -937,7 +900,7 @@ instance.web.SearchViewDrawer = instance.web.Widget.extend({
add_common_inputs: function() { add_common_inputs: function() {
// add custom filters to this.inputs // add custom filters to this.inputs
this.custom_filters = new instance.web.search.CustomReports(this); this.custom_filters = new instance.web.search.CustomFilters(this);
// add Filters to this.inputs, need view.controls filled // add Filters to this.inputs, need view.controls filled
(new instance.web.search.Filters(this)); (new instance.web.search.Filters(this));
(new instance.web.search.SaveFilter(this, this.custom_filters)); (new instance.web.search.SaveFilter(this, this.custom_filters));
@ -1349,6 +1312,15 @@ instance.web.search.Filter = instance.web.search.Input.extend(/** @lends instanc
get_context: function () { }, get_context: function () { },
get_domain: function () { }, get_domain: function () { },
}); });
instance.web.search.Separator = instance.web.search.Input.extend({
_in_drawer: false,
complete: function () {
return {is_separator: true};
}
});
instance.web.search.Field = instance.web.search.Input.extend( /** @lends instance.web.search.Field# */ { instance.web.search.Field = instance.web.search.Input.extend( /** @lends instance.web.search.Field# */ {
template: 'SearchView.field', template: 'SearchView.field',
default_operator: '=', default_operator: '=',
@ -1639,7 +1611,25 @@ instance.web.search.ManyToOneField = instance.web.search.CharField.extend({
this._super(view_section, field, parent); this._super(view_section, field, parent);
this.model = new instance.web.Model(this.attrs.relation); this.model = new instance.web.Model(this.attrs.relation);
}, },
complete: function (needle) {
complete: function (value) {
if (_.isEmpty(value)) { return $.when(null); }
var label = _.str.sprintf(_.str.escapeHTML(
_t("Search %(field)s for: %(value)s")), {
field: '<em>' + _.escape(this.attrs.string) + '</em>',
value: '<strong>' + _.escape(value) + '</strong>'});
return $.when([{
label: label,
facet: {
category: this.attrs.string,
field: this,
values: [{label: value, value: value}]
},
expand: this.expand.bind(this),
}]);
},
expand: function (needle) {
var self = this; var self = this;
// FIXME: "concurrent" searches (multiple requests, mis-ordered responses) // FIXME: "concurrent" searches (multiple requests, mis-ordered responses)
var context = instance.web.pyeval.eval( var context = instance.web.pyeval.eval(
@ -1652,13 +1642,12 @@ instance.web.search.ManyToOneField = instance.web.search.CharField.extend({
context: context context: context
}).then(function (results) { }).then(function (results) {
if (_.isEmpty(results)) { return null; } if (_.isEmpty(results)) { return null; }
return [{label: self.attrs.string}].concat( return _(results).map(function (result) {
_(results).map(function (result) {
return { return {
label: _.escape(result[1]), label: _.escape(result[1]),
facet: facet_from(self, result) facet: facet_from(self, result)
}; };
})); });
}); });
}, },
facet_for: function (value) { facet_for: function (value) {
@ -1701,8 +1690,8 @@ instance.web.search.ManyToOneField = instance.web.search.CharField.extend({
} }
}); });
instance.web.search.CustomReports = instance.web.search.Input.extend({ instance.web.search.CustomFilters = instance.web.search.Input.extend({
template: 'SearchView.CustomReports', template: 'SearchView.Custom',
_in_drawer: true, _in_drawer: true,
init: function () { init: function () {
this.is_ready = $.Deferred(); this.is_ready = $.Deferred();
@ -1721,11 +1710,15 @@ instance.web.search.CustomReports = instance.web.search.Input.extend({
self.clear_selection(); self.clear_selection();
}) })
.on('reset', this.proxy('clear_selection')); .on('reset', this.proxy('clear_selection'));
return this.model.call('get_filters', [this.view.model]) return this.model.call('get_filters', [this.view.model, this.get_action_id()])
.then(this.proxy('set_filters')) .then(this.proxy('set_filters'))
.done(function () { self.is_ready.resolve(); }) .done(function () { self.is_ready.resolve(); })
.fail(function () { self.is_ready.reject.apply(self.is_ready, arguments); }); .fail(function () { self.is_ready.reject.apply(self.is_ready, arguments); });
}, },
get_action_id: function(){
var action = instance.client.action_manager.inner_action;
if (action) return action.id;
},
/** /**
* Special implementation delaying defaults until CustomFilters is loaded * Special implementation delaying defaults until CustomFilters is loaded
*/ */
@ -1745,9 +1738,11 @@ instance.web.search.CustomReports = instance.web.search.Input.extend({
* @return {String} mapping key corresponding to the filter * @return {String} mapping key corresponding to the filter
*/ */
key_for: function (filter) { key_for: function (filter) {
var user_id = filter.user_id; var user_id = filter.user_id,
action_id = filter.action_id;
var uid = (user_id instanceof Array) ? user_id[0] : user_id; var uid = (user_id instanceof Array) ? user_id[0] : user_id;
return _.str.sprintf('(%s)%s', uid, filter.name); var act_id = (action_id instanceof Array) ? action_id[0] : action_id;
return _.str.sprintf('(%s)(%s)%s', uid, act_id, filter.name);
}, },
/** /**
* Generates a :js:class:`~instance.web.search.Facet` descriptor from a * Generates a :js:class:`~instance.web.search.Facet` descriptor from a
@ -1848,9 +1843,9 @@ instance.web.search.CustomReports = instance.web.search.Input.extend({
instance.web.search.SaveFilter = instance.web.search.Input.extend({ instance.web.search.SaveFilter = instance.web.search.Input.extend({
template: 'SearchView.SaveFilter', template: 'SearchView.SaveFilter',
_in_drawer: true, _in_drawer: true,
init: function (parent, custom_reports) { init: function (parent, custom_filters) {
this._super(parent); this._super(parent);
this.custom_reports = custom_reports; this.custom_filters = custom_filters;
}, },
start: function () { start: function () {
var self = this; var self = this;
@ -1894,13 +1889,14 @@ instance.web.search.SaveFilter = instance.web.search.Input.extend({
model_id: self.view.model, model_id: self.view.model,
context: results.context, context: results.context,
domain: results.domain, domain: results.domain,
is_default: set_as_default is_default: set_as_default,
action_id: self.custom_filters.get_action_id()
}; };
// FIXME: current context? // FIXME: current context?
return self.model.call('create_or_replace', [filter]).done(function (id) { return self.model.call('create_or_replace', [filter]).done(function (id) {
filter.id = id; filter.id = id;
if (self.custom_reports) { if (self.custom_filters) {
self.custom_reports.append_filter(filter); self.custom_filters.append_filter(filter);
} }
self.$el self.$el
.removeClass('oe_opened') .removeClass('oe_opened')
@ -2311,6 +2307,213 @@ instance.web.search.custom_filters = new instance.web.Registry({
'id': 'instance.web.search.ExtendedSearchProposition.Id' 'id': 'instance.web.search.ExtendedSearchProposition.Id'
}); });
instance.web.search.AutoComplete = instance.web.Widget.extend({
template: "SearchView.autocomplete",
// Parameters for autocomplete constructor:
//
// parent: this is used to detect keyboard events
//
// options.source: function ({term:query}, callback). This function will be called to
// obtain the search results corresponding to the query string. It is assumed that
// options.source will call callback with the results.
// options.delay: delay in millisecond before calling source. Useful if you don't want
// to make too many rpc calls
// options.select: function (ev, {item: {facet:facet}}). Autocomplete widget will call
// that function when a selection is made by the user
// options.get_search_string: function (). This function will be called by autocomplete
// to obtain the current search string.
init: function (parent, options) {
this._super(parent);
this.$input = parent.$el;
this.source = options.source;
this.delay = options.delay;
this.select = options.select,
this.get_search_string = options.get_search_string;
this.width = options.width || 400;
this.current_result = null;
this.searching = true;
this.search_string = null;
this.current_search = null;
},
start: function () {
var self = this;
this.$el.width(this.width);
this.$input.on('keyup', function (ev) {
if (ev.which === $.ui.keyCode.RIGHT) {
self.searching = true;
ev.preventDefault();
return;
}
if (!self.searching) {
self.searching = true;
return;
}
self.search_string = self.get_search_string();
if (self.search_string.length) {
var search_string = self.search_string;
setTimeout(function () { self.initiate_search(search_string);}, self.delay);
} else {
self.close();
}
});
this.$input.on('keydown', function (ev) {
switch (ev.which) {
case $.ui.keyCode.TAB:
case $.ui.keyCode.ENTER:
if (self.get_search_string().length) {
self.select_item(ev);
}
break;
case $.ui.keyCode.DOWN:
self.move('down');
self.searching = false;
ev.preventDefault();
break;
case $.ui.keyCode.UP:
self.move('up');
self.searching = false;
ev.preventDefault();
break;
case $.ui.keyCode.RIGHT:
self.searching = false;
var current = self.current_result
if (current && current.expand && !current.expanded) {
self.expand();
self.searching = true;
}
ev.preventDefault();
break;
case $.ui.keyCode.ESCAPE:
self.close();
self.searching = false;
break;
}
});
},
initiate_search: function (query) {
if (query === this.search_string && query !== this.current_search) {
this.search(query);
}
},
search: function (query) {
var self = this;
this.current_search = query;
this.source({term:query}, function (results) {
if (results.length) {
self.render_search_results(results);
self.focus_element(self.$('li:first-child'));
} else {
self.close();
}
});
},
render_search_results: function (results) {
var self = this;
var $list = this.$('ul');
$list.empty();
var render_separator = false;
results.forEach(function (result) {
if (result.is_separator) {
if (render_separator)
$list.append($('<li>').addClass('oe-separator'));
render_separator = false;
} else {
var $item = self.make_list_item(result).appendTo($list);
result.$el = $item;
render_separator = true;
}
});
this.show();
},
make_list_item: function (result) {
var self = this;
var $li = $('<li>')
.hover(function (ev) {self.focus_element($li);})
.mousedown(function (ev) {
if (ev.button === 0) { // left button
self.select(ev, {item: {facet: result.facet}});
self.close();
} else {
ev.preventDefault();
}
})
.data('result', result);
if (result.expand) {
var $expand = $('<span class="oe-expand">').text('▶').appendTo($li);
$expand.mousedown(function (ev) {
ev.preventDefault();
ev.stopPropagation();
if (result.expanded)
self.fold();
else
self.expand();
});
result.expanded = false;
}
if (result.indent) $li.addClass('oe-indent');
$li.append($('<span>').html(result.label));
return $li;
},
expand: function () {
var self = this;
this.current_result.expand(this.get_search_string()).then(function (results) {
(results || [{label: '(no result)'}]).reverse().forEach(function (result) {
result.indent = true;
var $li = self.make_list_item(result);
self.current_result.$el.after($li);
});
self.current_result.expanded = true;
self.current_result.$el.find('span.oe-expand').html('▼');
});
},
fold: function () {
var $next = this.current_result.$el.next();
while ($next.hasClass('oe-indent')) {
$next.remove();
$next = this.current_result.$el.next();
}
this.current_result.expanded = false;
this.current_result.$el.find('span.oe-expand').html('▶');
},
focus_element: function ($li) {
this.$('li').removeClass('oe-selection-focus');
$li.addClass('oe-selection-focus');
this.current_result = $li.data('result');
},
select_item: function (ev) {
if (this.current_result.facet) {
this.select(ev, {item: {facet: this.current_result.facet}});
this.close();
}
},
show: function () {
this.$el.show();
},
close: function () {
this.current_search = null;
this.search_string = null;
this.searching = true;
this.$el.hide();
},
move: function (direction) {
var $next;
if (direction === 'down') {
$next = this.$('li.oe-selection-focus').nextAll(':not(.oe-separator)').first();
if (!$next.length) $next = this.$('li:first-child');
} else {
$next = this.$('li.oe-selection-focus').prevAll(':not(.oe-separator)').first();
if (!$next.length) $next = this.$('li:not(.oe-separator)').last();
}
this.focus_element($next);
},
is_expandable: function () {
return !!this.$('.oe-selection-focus .oe-expand').length;
},
});
})(); })();
// vim:et fdc=0 fdl=0 foldnestmax=3 fdm=syntax: // vim:et fdc=0 fdl=0 foldnestmax=3 fdm=syntax:

View File

@ -75,15 +75,22 @@ var Tour = {
if (!tour) { if (!tour) {
Tour.error(null, "Can't run '"+tour_id+"' (tour undefined)"); 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(); this.time = new Date().getTime();
if (tour.path && !window.location.href.match(new RegExp("("+Tour.getLang()+")?"+tour.path+"#?$", "i"))) { if (tour.path && !window.location.href.match(new RegExp("("+Tour.getLang()+")?"+tour.path+"#?$", "i"))) {
var href = Tour.getLang()+tour.path; 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); Tour.saveState(tour.id, mode || tour.mode, -1, 0);
$(document).one("ajaxStop", Tour.running); $(document).one("ajaxStop", Tour.running);
window.location.href = href; window.location.href = href;
} else { } else {
console.log("Tour '"+tour_id+"' Begin from run method");
Tour.saveState(tour.id, mode || tour.mode, 0, 0); Tour.saveState(tour.id, mode || tour.mode, 0, 0);
Tour.running(); Tour.running();
} }
@ -358,6 +365,7 @@ var Tour = {
clearTimeout(Tour.timer); clearTimeout(Tour.timer);
clearTimeout(Tour.testtimer); clearTimeout(Tour.testtimer);
Tour.closePopover(); Tour.closePopover();
console.log("Tour reset");
}, },
running: function () { running: function () {
var state = Tour.getState(); var state = Tour.getState();
@ -398,6 +406,8 @@ var Tour = {
}; };
function checkNext () { function checkNext () {
if (!Tour.getState()) return;
Tour.autoTogglePopover(); Tour.autoTogglePopover();
clearTimeout(Tour.timer); clearTimeout(Tour.timer);
@ -476,6 +486,8 @@ var Tour = {
clearTimeout(Tour.testtimer); clearTimeout(Tour.testtimer);
function autoStep () { function autoStep () {
if (!Tour.getState()) return;
if (!step) return; if (!step) return;
if (step.autoComplete) { if (step.autoComplete) {
@ -502,6 +514,7 @@ var Tour = {
// trigger after for step like: mouseenter, next step click on button display with mouseenter // trigger after for step like: mouseenter, next step click on button display with mouseenter
setTimeout(function () { setTimeout(function () {
if (!Tour.getState()) return;
$element.trigger($.Event("mouseup", { srcElement: $element[0] })); $element.trigger($.Event("mouseup", { srcElement: $element[0] }));
$element.trigger($.Event("mouseleave", { srcElement: $element[0] })); $element.trigger($.Event("mouseleave", { srcElement: $element[0] }));
}, 1000); }, 1000);
@ -518,6 +531,7 @@ var Tour = {
$element.html(step.sampleText); $element.html(step.sampleText);
} }
setTimeout(function () { setTimeout(function () {
if (!Tour.getState()) return;
$element.trigger($.Event("keyup", { srcElement: $element })); $element.trigger($.Event("keyup", { srcElement: $element }));
$element.trigger($.Event("change", { srcElement: $element })); $element.trigger($.Event("change", { srcElement: $element }));
}, self.defaultDelay<<1); }, self.defaultDelay<<1);

View File

@ -1653,9 +1653,9 @@
</div> </div>
<div t-name="SearchView.CustomReports" class="oe_searchview_custom oe_searchview_section"> <div t-name="SearchView.Custom" class="oe_searchview_custom oe_searchview_section">
<dl class="dl-horizontal"> <dl class="dl-horizontal">
<dt><span class="oe_i">M</span> Custom Reports</dt> <dt><span class="oe_i">M</span> Favorites</dt>
<dd><ul class="oe_searchview_custom_list"/></dd> <dd><ul class="oe_searchview_custom_list"/></dd>
</dl> </dl>
</div> </div>
@ -1720,6 +1720,10 @@
</select> </select>
</t> </t>
<div t-name="SearchView.autocomplete" class="oe-autocomplete">
<ul>
</ul>
</div>
<t t-name="ExportView"> <t t-name="ExportView">
<a id="exportview" href="javascript: void(0)" style="text-decoration: none;color: #3D3D3D;">Export</a> <a id="exportview" href="javascript: void(0)" style="text-decoration: none;color: #3D3D3D;">Export</a>
</t> </t>

View File

@ -171,6 +171,7 @@ var makeSearchView = function (instance, dummy_widget_attributes, defaults) {
dummy: {type: 'char', string: 'Dummy'} dummy: {type: 'char', string: 'Dummy'}
}; };
}; };
instance.client = { action_manager: { inner_action: undefined } };
var dataset = new instance.web.DataSet(null, 'dummy.model'); var dataset = new instance.web.DataSet(null, 'dummy.model');
var mock_parent = {getParent: function () {return null;}}; var mock_parent = {getParent: function () {return null;}};
@ -558,7 +559,20 @@ openerp.testing.section('search.completions', {
new Date(2012, 4, 21, 21, 21, 21).getTime()); new Date(2012, 4, 21, 21, 21, 21).getTime());
}); });
}); });
test("M2O", {asserts: 13}, function (instance, $s, mock) { test("M2O complete", {asserts: 4}, function (instance, $s, mock) {
var view = {inputs: [], dataset: {get_context: function () {}}};
var f = new instance.web.search.ManyToOneField(
{attrs: {string: 'Dummy'}}, {relation: 'dummy.model'}, view);
return f.complete("bob")
.done(function (c) {
equal(c.length, 1, "should return one line");
var bob = c[0];
ok(bob.expand, "should return an expand callback");
ok(bob.facet, "should have a facet");
ok(bob.label, "should have a label");
});
});
test("M2O expand", {asserts: 11}, function (instance, $s, mock) {
mock('dummy.model:name_search', function (args, kwargs) { mock('dummy.model:name_search', function (args, kwargs) {
deepEqual(args, []); deepEqual(args, []);
strictEqual(kwargs.name, 'bob'); strictEqual(kwargs.name, 'bob');
@ -568,21 +582,18 @@ openerp.testing.section('search.completions', {
var view = {inputs: [], dataset: {get_context: function () {}}}; var view = {inputs: [], dataset: {get_context: function () {}}};
var f = new instance.web.search.ManyToOneField( var f = new instance.web.search.ManyToOneField(
{attrs: {string: 'Dummy'}}, {relation: 'dummy.model'}, view); {attrs: {string: 'Dummy'}}, {relation: 'dummy.model'}, view);
return f.complete("bob") return f.expand("bob")
.done(function (c) { .done(function (c) {
equal(c.length, 3, "should return results + title"); equal(c.length, 2, "should return results");
var title = c[0];
equal(title.label, f.attrs.string, "title should match field name");
ok(!title.facet, "title should not have a facet");
var f1 = new instance.web.search.Facet(c[1].facet); var f1 = new instance.web.search.Facet(c[0].facet);
equal(c[1].label, "choice 1"); equal(c[0].label, "choice 1");
equal(f1.get('category'), f.attrs.string); equal(f1.get('category'), f.attrs.string);
equal(f1.get('field'), f); equal(f1.get('field'), f);
deepEqual(f1.values.toJSON(), [{label: 'choice 1', value: 42}]); deepEqual(f1.values.toJSON(), [{label: 'choice 1', value: 42}]);
var f2 = new instance.web.search.Facet(c[2].facet); var f2 = new instance.web.search.Facet(c[1].facet);
equal(c[2].label, "choice @"); equal(c[1].label, "choice @");
equal(f2.get('category'), f.attrs.string); equal(f2.get('category'), f.attrs.string);
equal(f2.get('field'), f); equal(f2.get('field'), f);
deepEqual(f2.values.toJSON(), [{label: 'choice @', value: 43}]); deepEqual(f2.values.toJSON(), [{label: 'choice @', value: 43}]);
@ -597,7 +608,7 @@ openerp.testing.section('search.completions', {
var view = {inputs: [], dataset: {get_context: function () {}}}; var view = {inputs: [], dataset: {get_context: function () {}}};
var f = new instance.web.search.ManyToOneField( var f = new instance.web.search.ManyToOneField(
{attrs: {string: 'Dummy'}}, {relation: 'dummy.model'}, view); {attrs: {string: 'Dummy'}}, {relation: 'dummy.model'}, view);
return f.complete("bob") return f.expand("bob")
.done(function (c) { .done(function (c) {
ok(!c, "no match should yield no completion"); ok(!c, "no match should yield no completion");
}); });
@ -620,9 +631,9 @@ openerp.testing.section('search.completions', {
var f = new instance.web.search.ManyToOneField( var f = new instance.web.search.ManyToOneField(
{attrs: {string: 'Dummy', domain: '[["foo", "=", "bar"]]'}}, {attrs: {string: 'Dummy', domain: '[["foo", "=", "bar"]]'}},
{relation: 'dummy.model'}, view); {relation: 'dummy.model'}, view);
return f.complete("bob"); return f.expand("bob");
}); });
test("M2O custom operator", {asserts: 10}, function (instance, $s, mock) { test("M2O custom operator", {asserts: 8}, function (instance, $s, mock) {
mock('dummy.model:name_search', function (args, kwargs) { mock('dummy.model:name_search', function (args, kwargs) {
deepEqual(args, [], "should have no positional arguments"); deepEqual(args, [], "should have no positional arguments");
// the operator is meant for the final search term generation, not the autocompletion // the operator is meant for the final search term generation, not the autocompletion
@ -635,15 +646,12 @@ openerp.testing.section('search.completions', {
{attrs: {string: 'Dummy', operator: 'ilike'}}, {attrs: {string: 'Dummy', operator: 'ilike'}},
{relation: 'dummy.model'}, view); {relation: 'dummy.model'}, view);
return f.complete('bob') return f.expand('bob')
.done(function (c) { .done(function (c) {
equal(c.length, 2, "should return result + title"); equal(c.length, 1, "should return result");
var title = c[0];
equal(title.label, f.attrs.string, "title should match field name");
ok(!title.facet, "title should not have a facet");
var f1 = new instance.web.search.Facet(c[1].facet); var f1 = new instance.web.search.Facet(c[0].facet);
equal(c[1].label, "Match"); equal(c[0].label, "Match");
equal(f1.get('category'), f.attrs.string); equal(f1.get('category'), f.attrs.string);
equal(f1.get('field'), f); equal(f1.get('field'), f);
deepEqual(f1.values.toJSON(), [{label: 'Match', value: 42}]); deepEqual(f1.values.toJSON(), [{label: 'Match', value: 42}]);
@ -1210,13 +1218,14 @@ openerp.testing.section('search.groupby', {
var view = makeSearchView(instance); var view = makeSearchView(instance);
return view.appendTo($fix) return view.appendTo($fix)
.done(function () { .done(function () {
// 3 filters, 3 filtergroups, 1 custom filter, 1 advanced, 1 Filters // 3 filters, 3 filtergroups, 1 custom filter, 1 advanced, 1 Filters,
// and 1 SaveFilter widget // and 1 SaveFilter widget
equal(view.drawer.inputs.length, 10, "should have 10 inputs total"); equal(view.drawer.inputs.length, 10, "should have 10 inputs total");
var groups = _.filter(view.drawer.inputs, function (f) { var groups = _.filter(view.drawer.inputs, function (f) {
return f instanceof instance.web.search.GroupbyGroup; return f instanceof instance.web.search.GroupbyGroup;
}); });
equal(groups.length, 3, "should have 3 GroupbyGroups"); equal(groups.length, 3, "should have 3 GroupbyGroups");
groups[0].toggle(groups[0].filters[0]); groups[0].toggle(groups[0].filters[0]);
@ -1549,10 +1558,11 @@ openerp.testing.section('search.invisible', {
var done = $.Deferred(); var done = $.Deferred();
view.complete_global_search({term: 'filter'}, function (compls) { view.complete_global_search({term: 'filter'}, function (compls) {
done.resolve(); done.resolve();
strictEqual(compls.length, 2, console.log("completions", compls);
"should have 2 completions"); strictEqual(compls.length, 5,
"should have 5 completions"); // 2 filters and 3 separators
deepEqual(_.pluck(compls, 'label'), deepEqual(_.pluck(compls, 'label'),
['Field 0', 'Filter on: Filter 0'], [undefined, 'Field 0', 'Filter on: Filter 0', undefined, undefined],
"should complete on field 0 and filter 0"); "should complete on field 0 and filter 0");
}); });
return done; return done;

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/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/openerpframework.js"></script>
<script type="text/javascript" src="/web/static/src/js/tour.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"/> <link rel="stylesheet" href="/web/static/lib/fontawesome/css/font-awesome.css"/>
</template> </template>
@ -85,6 +82,9 @@
<script type="text/javascript" src="/web/static/lib/backbone/backbone.js"></script> <script type="text/javascript" src="/web/static/lib/backbone/backbone.js"></script>
<!-- Internals --> <!-- 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/base.css"/>
<link rel="stylesheet" href="/web/static/src/css/data_export.css"/> <link rel="stylesheet" href="/web/static/src/css/data_export.css"/>
<link rel="stylesheet" href="/base/static/src/css/modules.css"/> <link rel="stylesheet" href="/base/static/src/css/modules.css"/>
@ -184,7 +184,7 @@
<template id="web.menu_link"> <template id="web.menu_link">
<t t-set="debug_param" t-value="'?&amp;debug=' if debug else ''"/> <t t-set="debug_param" t-value="'?&amp;debug=' if debug else ''"/>
<a t-att-href="'/web%s#menu_id=%s&amp;action=%s' % (debug_param, menu['id'], menu['action'] and menu['action'].split(',')[1] or '')" <a t-att-href="'/web%s#menu_id=%s&amp;action=%s' % (debug_param, menu['id'], menu['action'] and menu['action'].split(',')[1] or '')"
t-att-class="'oe_menu_toggler' if menu['children'] else 'oe_menu_leaf'" t-att-class="'oe_menu_toggler' if menu.get('children') else 'oe_menu_leaf'"
t-att-data-menu="menu['id']" t-att-data-menu="menu['id']"
t-att-data-action-model="menu['action'] and menu['action'].split(',')[0] or ''" t-att-data-action-model="menu['action'] and menu['action'].split(',')[0] or ''"
t-att-data-action-id="menu['action'] and menu['action'].split(',')[1] or ''"> t-att-data-action-id="menu['action'] and menu['action'].split(',')[1] or ''">

View File

@ -151,7 +151,7 @@ class view(osv.osv):
user_id=self.pool.get("res.users").browse(cr, uid, uid), user_id=self.pool.get("res.users").browse(cr, uid, uid),
translatable=context.get('lang') != request.website.default_lang_code, translatable=context.get('lang') != request.website.default_lang_code,
editable=request.website.is_publisher(), editable=request.website.is_publisher(),
menu_data=self.pool['ir.ui.menu'].load_menus(cr, uid, context=context) if request.website.is_user() else None, menu_data=self.pool['ir.ui.menu'].load_menus_root(cr, uid, context=context) if request.website.is_user() else None,
) )
# add some values # add some values

View File

@ -244,7 +244,7 @@ class website(osv.osv):
return is_website_publisher return is_website_publisher
def is_user(self, cr, uid, ids, context=None): def is_user(self, cr, uid, ids, context=None):
return self.pool['res.users'].has_group(cr, request.uid, 'base.group_user') return self.pool['res.users'].has_group(cr, uid, 'base.group_user')
def get_template(self, cr, uid, ids, template, context=None): def get_template(self, cr, uid, ids, template, context=None):
if isinstance(template, (int, long)): if isinstance(template, (int, long)):

View File

@ -12,7 +12,6 @@ window.openerp.website.EditorBar.include({
} }
var $menuItem = $($.parseHTML('<li><a href="#">'+tour.name+'</a></li>')); var $menuItem = $($.parseHTML('<li><a href="#">'+tour.name+'</a></li>'));
$menuItem.click(function () { $menuItem.click(function () {
openerp.Tour.reset();
openerp.Tour.run(tour.id); openerp.Tour.run(tour.id);
}); });
menu.append($menuItem); menu.append($menuItem);

View File

@ -4,6 +4,7 @@
<!-- Front-end/Back-end integration --> <!-- Front-end/Back-end integration -->
<template id="user_navbar" inherit_id="website.layout" groups="base.group_user"> <template id="user_navbar" inherit_id="website.layout" groups="base.group_user">
<xpath expr="//body/div['id=wrawrap']" position="before"> <xpath expr="//body/div['id=wrawrap']" position="before">
<t t-if="website and menu_data">
<nav id="oe_main_menu_navbar" class="navbar navbar-inverse" role="navigation"> <nav id="oe_main_menu_navbar" class="navbar navbar-inverse" role="navigation">
<div class="navbar-header"> <div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#oe_applications"> <button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#oe_applications">
@ -79,6 +80,7 @@
</li> </li>
</ul> </ul>
</nav> </nav>
</t>
</xpath> </xpath>
</template> </template>
</data> </data>

View File

@ -57,7 +57,7 @@
<template id="layout" name="Main layout">&lt;!DOCTYPE html&gt; <template id="layout" name="Main layout">&lt;!DOCTYPE html&gt;
<html t-att-lang="lang and lang.replace('_', '-')" <html t-att-lang="lang and lang.replace('_', '-')"
t-att-data-website-id="website.id if editable else None" t-att-data-website-id="website.id if editable and website else None"
t-att-data-editable="'1' if editable else None" t-att-data-editable="'1' if editable else None"
t-att-data-editable-no-editor="editable_no_editor or None" t-att-data-editable-no-editor="editable_no_editor or None"
t-att-data-translatable="'1' if translatable else None" t-att-data-translatable="'1' if translatable else None"
@ -82,10 +82,10 @@
and main_object.website_meta_keywords or website_meta_keywords"/> and main_object.website_meta_keywords or website_meta_keywords"/>
<title><t t-esc="title"/></title> <title><t t-esc="title"/></title>
<t t-set="languages" t-value="website.get_languages()"/> <t t-set="languages" t-value="website.get_languages() if website else None"/>
<t t-if="request.website_multilang"> <t t-if="request and request.website_multilang">
<t t-foreach="languages" t-as="lg"> <t t-foreach="languages" t-as="lg">
<t t-set="force_lang" t-value="lg[0] if lg[0] != website.default_lang_code else None"/> <t t-set="force_lang" t-value="lg[0] if website and lg[0] != website.default_lang_code else None"/>
<link rel="alternate" t-att-href="url_for(request.httprequest.path + '?' + keep_query(), lang=force_lang)" t-att-hreflang="lg[0].replace('_', '-').lower()" /> <link rel="alternate" t-att-href="url_for(request.httprequest.path + '?' + keep_query(), lang=force_lang)" t-att-hreflang="lg[0].replace('_', '-').lower()" />
</t> </t>
</t> </t>
@ -150,7 +150,7 @@
</div> </div>
</footer> </footer>
</div> </div>
<t t-if="website.google_analytics_key"> <t t-if="website and website.google_analytics_key">
<script> <script>
(function(b,o,i,l,e,r){b.GoogleAnalyticsObject=l;b[l]||(b[l]= (function(b,o,i,l,e,r){b.GoogleAnalyticsObject=l;b[l]||(b[l]=
function(){(b[l].q=b[l].q||[]).push(arguments)});b[l].l=+new Date; function(){(b[l].q=b[l].q||[]).push(arguments)});b[l].l=+new Date;

View File

@ -54,9 +54,11 @@
<field name="arch" type="xml"> <field name="arch" type="xml">
<form string="Blog Post"> <form string="Blog Post">
<sheet> <sheet>
<h1><field name="name" placeholder="Name"/></h1> <group>
<field name="name" placeholder="Name"/>
<field name="tag_ids" widget="many2many_tags"/> <field name="tag_ids" widget="many2many_tags"/>
<field name="subtitle" placeholder="Blog Subtitle"/> <field name="subtitle" placeholder="Blog Subtitle"/>
</group>
<group> <group>
<field name="background_image"/> <field name="background_image"/>
<field name="blog_id"/> <field name="blog_id"/>

View File

@ -12,7 +12,6 @@ from openerp.addons.web.controllers.main import login_redirect
from openerp.addons.web.http import request from openerp.addons.web.http import request
from openerp.addons.website.controllers.main import Website as controllers from openerp.addons.website.controllers.main import Website as controllers
from openerp.addons.website.models.website import slug from openerp.addons.website.models.website import slug
from openerp.tools import html2plaintext
controllers = controllers() controllers = controllers()
@ -33,20 +32,12 @@ class WebsiteForum(http.Controller):
return msg return msg
def _prepare_forum_values(self, forum=None, **kwargs): def _prepare_forum_values(self, forum=None, **kwargs):
Forum = request.registry['forum.forum']
user = request.registry['res.users'].browse(request.cr, request.uid, request.uid, context=request.context) user = request.registry['res.users'].browse(request.cr, request.uid, request.uid, context=request.context)
values = {'user': user, values = {'user': user,
'is_public_user': user.id == request.website.user_id.id, 'is_public_user': user.id == request.website.user_id.id,
'notifications': self._get_notifications(), 'notifications': self._get_notifications(),
'header': kwargs.get('header', dict()), 'header': kwargs.get('header', dict()),
'searches': kwargs.get('searches', dict()), 'searches': kwargs.get('searches', dict()),
'can_edit_own': True,
'can_edit_all': user.karma > Forum._karma_modo_edit_all,
'can_close_own': user.karma > Forum._karma_modo_close_own,
'can_close_all': user.karma > Forum._karma_modo_close_all,
'can_unlink_own': user.karma > Forum._karma_modo_unlink_own,
'can_unlink_all': user.karma > Forum._karma_modo_unlink_all,
'can_unlink_comment': user.karma > Forum._karma_modo_unlink_comment,
} }
if forum: if forum:
values['forum'] = forum values['forum'] = forum
@ -55,14 +46,6 @@ class WebsiteForum(http.Controller):
values.update(kwargs) values.update(kwargs)
return values return values
def _has_enough_karma(self, karma_name, uid=None):
Forum = request.registry['forum.forum']
karma = hasattr(Forum, karma_name) and getattr(Forum, karma_name) or 0
user = request.registry['res.users'].browse(request.cr, SUPERUSER_ID, uid or request.uid, context=request.context)
if user.karma < karma:
return False, {'error': 'not_enough_karma', 'karma': karma}
return True, {}
# Forum # Forum
# -------------------------------------------------- # --------------------------------------------------
@ -244,10 +227,6 @@ class WebsiteForum(http.Controller):
@http.route('/forum/<model("forum.forum"):forum>/question/<model("forum.post"):question>/ask_for_close', type='http', auth="user", methods=['POST'], website=True) @http.route('/forum/<model("forum.forum"):forum>/question/<model("forum.post"):question>/ask_for_close', type='http', auth="user", methods=['POST'], website=True)
def question_ask_for_close(self, forum, question, **post): def question_ask_for_close(self, forum, question, **post):
check_res = self._has_enough_karma(question.create_uid.id == request.uid and '_karma_modo_close_own' or '_karma_modo_close_all')
if not check_res[0]:
return werkzeug.utils.redirect("/forum/%s" % slug(forum))
cr, uid, context = request.cr, request.uid, request.context cr, uid, context = request.cr, request.uid, request.context
Reason = request.registry['forum.post.reason'] Reason = request.registry['forum.post.reason']
reason_ids = Reason.search(cr, uid, [], context=context) reason_ids = Reason.search(cr, uid, [], context=context)
@ -272,42 +251,21 @@ class WebsiteForum(http.Controller):
@http.route('/forum/<model("forum.forum"):forum>/question/<model("forum.post"):question>/close', type='http', auth="user", methods=['POST'], website=True) @http.route('/forum/<model("forum.forum"):forum>/question/<model("forum.post"):question>/close', type='http', auth="user", methods=['POST'], website=True)
def question_close(self, forum, question, **post): def question_close(self, forum, question, **post):
check_res = self._has_enough_karma(question.create_uid.id == request.uid and '_karma_modo_close_own' or '_karma_modo_close_all') request.registry['forum.post'].close(request.cr, request.uid, [question.id], reason_id=int(post.get('reason_id', False)), context=request.context)
if not check_res[0]:
return werkzeug.utils.redirect("/forum/%s" % slug(forum))
request.registry['forum.post'].write(request.cr, request.uid, [question.id], {
'state': 'close',
'closed_uid': request.uid,
'closed_date': datetime.today().strftime(tools.DEFAULT_SERVER_DATETIME_FORMAT),
'closed_reason_id': int(post.get('reason_id', False)),
}, context=request.context)
return werkzeug.utils.redirect("/forum/%s/question/%s" % (slug(forum), slug(question))) return werkzeug.utils.redirect("/forum/%s/question/%s" % (slug(forum), slug(question)))
@http.route('/forum/<model("forum.forum"):forum>/question/<model("forum.post"):question>/reopen', type='http', auth="user", methods=['POST'], website=True) @http.route('/forum/<model("forum.forum"):forum>/question/<model("forum.post"):question>/reopen', type='http', auth="user", methods=['POST'], website=True)
def question_reopen(self, forum, question, **kwarg): def question_reopen(self, forum, question, **kwarg):
check_res = self._has_enough_karma(question.create_uid.id == request.uid and '_karma_modo_close_own' or '_karma_modo_close_all')
if not check_res[0]:
return werkzeug.utils.redirect("/forum/%s" % slug(forum))
request.registry['forum.post'].write(request.cr, request.uid, [question.id], {'state': 'active'}, context=request.context) request.registry['forum.post'].write(request.cr, request.uid, [question.id], {'state': 'active'}, context=request.context)
return werkzeug.utils.redirect("/forum/%s/question/%s" % (slug(forum), slug(question))) return werkzeug.utils.redirect("/forum/%s/question/%s" % (slug(forum), slug(question)))
@http.route('/forum/<model("forum.forum"):forum>/question/<model("forum.post"):question>/delete', type='http', auth="user", methods=['POST'], website=True) @http.route('/forum/<model("forum.forum"):forum>/question/<model("forum.post"):question>/delete', type='http', auth="user", methods=['POST'], website=True)
def question_delete(self, forum, question, **kwarg): def question_delete(self, forum, question, **kwarg):
check_res = self._has_enough_karma(question.create_uid.id == request.uid and '_karma_modo_unlink_own' or '_karma_modo_unlink_all')
if not check_res[0]:
return werkzeug.utils.redirect("/forum/%s" % slug(forum))
request.registry['forum.post'].write(request.cr, request.uid, [question.id], {'active': False}, context=request.context) request.registry['forum.post'].write(request.cr, request.uid, [question.id], {'active': False}, context=request.context)
return werkzeug.utils.redirect("/forum/%s/question/%s" % (slug(forum), slug(question))) return werkzeug.utils.redirect("/forum/%s/question/%s" % (slug(forum), slug(question)))
@http.route('/forum/<model("forum.forum"):forum>/question/<model("forum.post"):question>/undelete', type='http', auth="user", methods=['POST'], website=True) @http.route('/forum/<model("forum.forum"):forum>/question/<model("forum.post"):question>/undelete', type='http', auth="user", methods=['POST'], website=True)
def question_undelete(self, forum, question, **kwarg): def question_undelete(self, forum, question, **kwarg):
check_res = self._has_enough_karma(question.create_uid.id == request.uid and '_karma_modo_unlink_own' or '_karma_modo_unlink_all')
if not check_res[0]:
return werkzeug.utils.redirect("/forum/%s" % slug(forum))
request.registry['forum.post'].write(request.cr, request.uid, [question.id], {'active': True}, context=request.context) request.registry['forum.post'].write(request.cr, request.uid, [question.id], {'active': True}, context=request.context)
return werkzeug.utils.redirect("/forum/%s/question/%s" % (slug(forum), slug(question))) return werkzeug.utils.redirect("/forum/%s/question/%s" % (slug(forum), slug(question)))
@ -349,11 +307,6 @@ class WebsiteForum(http.Controller):
return request.redirect('/') return request.redirect('/')
if not request.session.uid: if not request.session.uid:
return {'error': 'anonymous_user'} return {'error': 'anonymous_user'}
user = request.registry['res.users'].browse(request.cr, SUPERUSER_ID, request.uid, context=request.context)
if post.parent_id.create_uid.id != uid and user.karma < request.registry['forum.forum']._karma_answer_accept_all:
return {'error': 'not_enough_karma', 'karma': request.registry['forum.forum']._karma_answer_accept_all}
if post.create_uid.id == user.id and user.karma < request.registry['forum.forum']._karma_answer_accept_own:
return {'error': 'not_enough_karma', 'karma': request.registry['forum.forum']._karma_answer_accept_own}
# set all answers to False, only one can be accepted # set all answers to False, only one can be accepted
request.registry['forum.post'].write(cr, uid, [c.id for c in post.parent_id.child_ids], {'is_correct': False}, context=context) request.registry['forum.post'].write(cr, uid, [c.id for c in post.parent_id.child_ids], {'is_correct': False}, context=context)
@ -362,10 +315,6 @@ class WebsiteForum(http.Controller):
@http.route('/forum/<model("forum.forum"):forum>/post/<model("forum.post"):post>/delete', type='http', auth="user", methods=['POST'], website=True) @http.route('/forum/<model("forum.forum"):forum>/post/<model("forum.post"):post>/delete', type='http', auth="user", methods=['POST'], website=True)
def post_delete(self, forum, post, **kwargs): def post_delete(self, forum, post, **kwargs):
check_res = self._has_enough_karma(post.create_uid.id == request.uid and '_karma_modo_unlink_own' or '_karma_modo_unlink_all')
if not check_res[0]:
return werkzeug.utils.redirect("/forum/%s" % slug(forum))
question = post.parent_id question = post.parent_id
request.registry['forum.post'].unlink(request.cr, request.uid, [post.id], context=request.context) request.registry['forum.post'].unlink(request.cr, request.uid, [post.id], context=request.context)
if question: if question:
@ -374,10 +323,6 @@ class WebsiteForum(http.Controller):
@http.route('/forum/<model("forum.forum"):forum>/post/<model("forum.post"):post>/edit', type='http', auth="user", website=True) @http.route('/forum/<model("forum.forum"):forum>/post/<model("forum.post"):post>/edit', type='http', auth="user", website=True)
def post_edit(self, forum, post, **kwargs): def post_edit(self, forum, post, **kwargs):
check_res = self._has_enough_karma(post.create_uid.id == request.uid and '_karma_modo_edit_own' or '_karma_modo_edit_all')
if not check_res[0]:
return werkzeug.utils.redirect("/forum/%s" % slug(forum))
tags = "" tags = ""
for tag_name in post.tag_ids: for tag_name in post.tag_ids:
tags += tag_name.name + "," tags += tag_name.name + ","
@ -419,9 +364,6 @@ class WebsiteForum(http.Controller):
return {'error': 'anonymous_user'} return {'error': 'anonymous_user'}
if request.uid == post.create_uid.id: if request.uid == post.create_uid.id:
return {'error': 'own_post'} return {'error': 'own_post'}
check_res = self._has_enough_karma('_karma_upvote')
if not check_res[0]:
return check_res[1]
upvote = True if not post.user_vote > 0 else False upvote = True if not post.user_vote > 0 else False
return request.registry['forum.post'].vote(request.cr, request.uid, [post.id], upvote=upvote, context=request.context) return request.registry['forum.post'].vote(request.cr, request.uid, [post.id], upvote=upvote, context=request.context)
@ -431,9 +373,6 @@ class WebsiteForum(http.Controller):
return {'error': 'anonymous_user'} return {'error': 'anonymous_user'}
if request.uid == post.create_uid.id: if request.uid == post.create_uid.id:
return {'error': 'own_post'} return {'error': 'own_post'}
check_res = self._has_enough_karma('_karma_downvote')
if not check_res[0]:
return check_res[1]
upvote = True if post.user_vote < 0 else False upvote = True if post.user_vote < 0 else False
return request.registry['forum.post'].vote(request.cr, request.uid, [post.id], upvote=upvote, context=request.context) return request.registry['forum.post'].vote(request.cr, request.uid, [post.id], upvote=upvote, context=request.context)
@ -625,26 +564,25 @@ class WebsiteForum(http.Controller):
# Messaging # Messaging
# -------------------------------------------------- # --------------------------------------------------
@http.route('/forum/<model("forum.forum"):forum>/post/<model("forum.post"):post>/comment/<model("mail.message"):comment>/convert_to_answer', type='http', auth="public", methods=['POST'], website=True) @http.route('/forum/<model("forum.forum"):forum>/post/<model("forum.post"):post>/comment/<model("mail.message"):comment>/convert_to_answer', type='http', auth="user", methods=['POST'], website=True)
def convert_comment_to_answer(self, forum, post, comment, **kwarg): def convert_comment_to_answer(self, forum, post, comment, **kwarg):
body = comment.body new_post_id = request.registry['forum.post'].convert_comment_to_answer(request.cr, request.uid, comment.id, context=request.context)
request.registry['mail.message'].unlink(request.cr, request.uid, [comment.id], context=request.context) if not new_post_id:
return werkzeug.utils.redirect("/forum/%s" % slug(forum))
post = request.registry['forum.post'].browse(request.cr, request.uid, new_post_id, context=request.context)
question = post.parent_id if post.parent_id else post question = post.parent_id if post.parent_id else post
for answer in question.child_ids: return werkzeug.utils.redirect("/forum/%s/question/%s" % (slug(forum), slug(question)))
if answer.create_uid.id == request.uid:
return self.post_comment(forum, answer, comment=html2plaintext(body))
return self.post_new(forum, question, content=body)
@http.route('/forum/<model("forum.forum"):forum>/post/<model("forum.post"):post>/convert_to_comment', type='http', auth="user", methods=['POST'], website=True) @http.route('/forum/<model("forum.forum"):forum>/post/<model("forum.post"):post>/convert_to_comment', type='http', auth="user", methods=['POST'], website=True)
def convert_answer_to_comment(self, forum, post, **kwarg): def convert_answer_to_comment(self, forum, post, **kwarg):
values = {
'comment': html2plaintext(post.content),
}
question = post.parent_id question = post.parent_id
request.registry['forum.post'].unlink(request.cr, SUPERUSER_ID, [post.id], context=request.context) new_msg_id = request.registry['forum.post'].convert_answer_to_comment(request.cr, request.uid, post.id, context=request.context)
return self.post_comment(forum, question, **values) if not new_msg_id:
return werkzeug.utils.redirect("/forum/%s" % slug(forum))
return werkzeug.utils.redirect("/forum/%s/question/%s" % (slug(forum), slug(question)))
@http.route('/forum/<model("forum.forum"):forum>/post/<model("forum.post"):post>/comment/<model("mail.message"):comment>/delete', type='json', auth="user", website=True) @http.route('/forum/<model("forum.forum"):forum>/post/<model("forum.post"):post>/comment/<model("mail.message"):comment>/delete', type='json', auth="user", website=True)
def delete_comment(self, forum, post, comment, **kwarg): def delete_comment(self, forum, post, comment, **kwarg):
request.registry['mail.message'].unlink(request.cr, SUPERUSER_ID, [comment.id], context=request.context) if not request.session.uid:
return True return {'error': 'anonymous_user'}
return request.registry['forum.post'].unlink(request.cr, request.uid, post.id, comment.id, context=request.context)

View File

@ -44,7 +44,7 @@
<record id="mt_question_new" model="mail.message.subtype"> <record id="mt_question_new" model="mail.message.subtype">
<field name="name">New Question</field> <field name="name">New Question</field>
<field name="res_model">forum.post</field> <field name="res_model">forum.post</field>
<field name="default" eval="False"/> <field name="default" eval="True"/>
<field name="description">New Question</field> <field name="description">New Question</field>
</record> </record>
<record id="mt_question_edit" model="mail.message.subtype"> <record id="mt_question_edit" model="mail.message.subtype">
@ -57,7 +57,7 @@
<record id="mt_forum_answer_new" model="mail.message.subtype"> <record id="mt_forum_answer_new" model="mail.message.subtype">
<field name="name">New Answer</field> <field name="name">New Answer</field>
<field name="res_model">forum.forum</field> <field name="res_model">forum.forum</field>
<field name="default" eval="False"/> <field name="default" eval="True"/>
<field name="hidden" eval="False"/> <field name="hidden" eval="False"/>
<field name="parent_id" eval="ref('mt_answer_new')"/> <field name="parent_id" eval="ref('mt_answer_new')"/>
<field name="relation_field">forum_id</field> <field name="relation_field">forum_id</field>

View File

@ -1,50 +1,63 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from datetime import datetime
import openerp import openerp
from openerp import tools
from openerp import SUPERUSER_ID from openerp import SUPERUSER_ID
from openerp.addons.website.models.website import slug from openerp.addons.website.models.website import slug
from openerp.osv import osv, fields from openerp.osv import osv, fields
from openerp.tools import html2plaintext
from openerp.tools.translate import _ from openerp.tools.translate import _
class KarmaError(ValueError):
""" Karma-related error, used for forum and posts. """
pass
class Forum(osv.Model): class Forum(osv.Model):
"""TDE TODO: set karma values for actions dynamic for a given forum""" """TDE TODO: set karma values for actions dynamic for a given forum"""
_name = 'forum.forum' _name = 'forum.forum'
_description = 'Forums' _description = 'Forums'
_inherit = ['mail.thread', 'website.seo.metadata'] _inherit = ['mail.thread', 'website.seo.metadata']
# karma values
_karma_upvote = 5 # done
_karma_downvote = 50 # done
_karma_answer_accept_own = 20 # done
_karma_answer_accept_own_now = 50
_karma_answer_accept_all = 500
_karma_editor_link_files = 30 # done
_karma_editor_clickable_link = 50
_karma_comment = 1
_karma_modo_retag = 75
_karma_modo_flag = 100
_karma_modo_flag_see_all = 300
_karma_modo_unlink_comment = 750
_karma_modo_edit_own = 1 # done
_karma_modo_edit_all = 300 # done
_karma_modo_close_own = 100 # done
_karma_modo_close_all = 900 # done
_karma_modo_unlink_own = 500 # done
_karma_modo_unlink_all = 1000 # done
# karma generation
_karma_gen_quest_new = 2 # done
_karma_gen_upvote_quest = 5 # done
_karma_gen_downvote_quest = -2 # done
_karma_gen_upvote_ans = 10 # done
_karma_gen_downvote_ans = -2 # done
_karma_gen_ans_accept = 2 # done
_karma_gen_ans_accepted = 15 # done
_karma_gen_ans_flagged = -100
_columns = { _columns = {
'name': fields.char('Name', required=True, translate=True), 'name': fields.char('Name', required=True, translate=True),
'faq': fields.html('Guidelines'), 'faq': fields.html('Guidelines'),
'description': fields.html('Description'), 'description': fields.html('Description'),
# karma generation
'karma_gen_question_new': fields.integer('Karma earned for new questions'),
'karma_gen_question_upvote': fields.integer('Karma earned for upvoting a question'),
'karma_gen_question_downvote': fields.integer('Karma earned for downvoting a question'),
'karma_gen_answer_upvote': fields.integer('Karma earned for upvoting an answer'),
'karma_gen_answer_downvote': fields.integer('Karma earned for downvoting an answer'),
'karma_gen_answer_accept': fields.integer('Karma earned for accepting an anwer'),
'karma_gen_answer_accepted': fields.integer('Karma earned for having an answer accepted'),
'karma_gen_answer_flagged': fields.integer('Karma earned for having an answer flagged'),
# karma-based actions
'karma_ask': fields.integer('Karma to ask a new question'),
'karma_answer': fields.integer('Karma to answer a question'),
'karma_edit_own': fields.integer('Karma to edit its own posts'),
'karma_edit_all': fields.integer('Karma to edit all posts'),
'karma_close_own': fields.integer('Karma to close its own posts'),
'karma_close_all': fields.integer('Karma to close all posts'),
'karma_unlink_own': fields.integer('Karma to delete its own posts'),
'karma_unlink_all': fields.integer('Karma to delete all posts'),
'karma_upvote': fields.integer('Karma to upvote'),
'karma_downvote': fields.integer('Karma to downvote'),
'karma_answer_accept_own': fields.integer('Karma to accept an answer on its own questions'),
'karma_answer_accept_all': fields.integer('Karma to accept an answers to all questions'),
'karma_editor_link_files': fields.integer('Karma for linking files (Editor)'),
'karma_editor_clickable_link': fields.integer('Karma for clickable links (Editor)'),
'karma_comment_own': fields.integer('Karma to comment its own posts'),
'karma_comment_all': fields.integer('Karma to comment all posts'),
'karma_comment_convert_own': fields.integer('Karma to convert its own answers to comments and vice versa'),
'karma_comment_convert_all': fields.integer('Karma to convert all answers to answers and vice versa'),
'karma_comment_unlink_own': fields.integer('Karma to unlink its own comments'),
'karma_comment_unlink_all': fields.integer('Karma to unlinnk all comments'),
'karma_retag': fields.integer('Karma to change question tags'),
'karma_flag': fields.integer('Karma to flag a post as offensive'),
} }
def _get_default_faq(self, cr, uid, context=None): def _get_default_faq(self, cr, uid, context=None):
@ -56,6 +69,36 @@ class Forum(osv.Model):
_defaults = { _defaults = {
'description': 'This community is for professionals and enthusiasts of our products and services.', 'description': 'This community is for professionals and enthusiasts of our products and services.',
'faq': _get_default_faq, 'faq': _get_default_faq,
'karma_gen_question_new': 2,
'karma_gen_question_upvote': 5,
'karma_gen_question_downvote': -2,
'karma_gen_answer_upvote': 10,
'karma_gen_answer_downvote': -2,
'karma_gen_answer_accept': 2,
'karma_gen_answer_accepted': 15,
'karma_gen_answer_flagged': -100,
'karma_ask': 0,
'karma_answer': 0,
'karma_edit_own': 1,
'karma_edit_all': 300,
'karma_close_own': 100,
'karma_close_all': 500,
'karma_unlink_own': 500,
'karma_unlink_all': 1000,
'karma_upvote': 5,
'karma_downvote': 50,
'karma_answer_accept_own': 20,
'karma_answer_accept_all': 500,
'karma_editor_link_files': 20,
'karma_editor_clickable_link': 20,
'karma_comment_own': 1,
'karma_comment_all': 1,
'karma_comment_convert_own': 50,
'karma_comment_convert_all': 500,
'karma_comment_unlink_own': 50,
'karma_comment_unlink_all': 500,
'karma_retag': 75,
'karma_flag': 500,
} }
def create(self, cr, uid, values, context=None): def create(self, cr, uid, values, context=None):
@ -139,6 +182,36 @@ class Post(osv.Model):
res[post.id] = post.parent_id and post.parent_id.create_uid == post.create_uid or False res[post.id] = post.parent_id and post.parent_id.create_uid == post.create_uid or False
return res return res
def _get_post_karma_rights(self, cr, uid, ids, field_name, arg, context=None):
user = self.pool['res.users'].browse(cr, uid, uid, context=context)
res = dict.fromkeys(ids, False)
for post in self.browse(cr, uid, ids, context=context):
res[post.id] = {
'karma_ask': post.forum_id.karma_ask,
'karma_answer': post.forum_id.karma_answer,
'karma_accept': post.parent_id and post.parent_id.create_uid.id == uid and post.forum_id.karma_answer_accept_own or post.forum_id.karma_answer_accept_all,
'karma_edit': post.create_uid.id == uid and post.forum_id.karma_edit_own or post.forum_id.karma_edit_all,
'karma_close': post.create_uid.id == uid and post.forum_id.karma_close_own or post.forum_id.karma_close_all,
'karma_unlink': post.create_uid.id == uid and post.forum_id.karma_unlink_own or post.forum_id.karma_unlink_all,
'karma_upvote': post.forum_id.karma_upvote,
'karma_downvote': post.forum_id.karma_downvote,
'karma_comment': post.create_uid.id == uid and post.forum_id.karma_comment_own or post.forum_id.karma_comment_all,
'karma_comment_convert': post.create_uid.id == uid and post.forum_id.karma_comment_convert_own or post.forum_id.karma_comment_convert_all,
}
res[post.id].update({
'can_ask': uid == SUPERUSER_ID or user.karma >= res[post.id]['karma_ask'],
'can_answer': uid == SUPERUSER_ID or user.karma >= res[post.id]['karma_answer'],
'can_accept': uid == SUPERUSER_ID or user.karma >= res[post.id]['karma_accept'],
'can_edit': uid == SUPERUSER_ID or user.karma >= res[post.id]['karma_edit'],
'can_close': uid == SUPERUSER_ID or user.karma >= res[post.id]['karma_close'],
'can_unlink': uid == SUPERUSER_ID or user.karma >= res[post.id]['karma_unlink'],
'can_upvote': uid == SUPERUSER_ID or user.karma >= res[post.id]['karma_upvote'],
'can_downvote': uid == SUPERUSER_ID or user.karma >= res[post.id]['karma_downvote'],
'can_comment': uid == SUPERUSER_ID or user.karma >= res[post.id]['karma_comment'],
'can_comment_convert': uid == SUPERUSER_ID or user.karma >= res[post.id]['karma_comment_convert'],
})
return res
_columns = { _columns = {
'name': fields.char('Title'), 'name': fields.char('Title'),
'forum_id': fields.many2one('forum.forum', 'Forum', required=True), 'forum_id': fields.many2one('forum.forum', 'Forum', required=True),
@ -151,7 +224,7 @@ class Post(osv.Model):
'website_message_ids': fields.one2many( 'website_message_ids': fields.one2many(
'mail.message', 'res_id', 'mail.message', 'res_id',
domain=lambda self: [ domain=lambda self: [
'&', ('model', '=', self._name), ('type', '=', 'comment') '&', ('model', '=', self._name), ('type', 'in', ['email', 'comment'])
], ],
string='Post Messages', help="Comments on forum post", string='Post Messages', help="Comments on forum post",
), ),
@ -203,6 +276,28 @@ class Post(osv.Model):
'closed_reason_id': fields.many2one('forum.post.reason', 'Reason'), 'closed_reason_id': fields.many2one('forum.post.reason', 'Reason'),
'closed_uid': fields.many2one('res.users', 'Closed by', select=1), 'closed_uid': fields.many2one('res.users', 'Closed by', select=1),
'closed_date': fields.datetime('Closed on', readonly=True), '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'),
'karma_unlink': fields.function(_get_post_karma_rights, string='Karma to unlink', type='integer', multi='_get_post_karma_rights'),
'karma_upvote': fields.function(_get_post_karma_rights, string='Karma to upvote', type='integer', multi='_get_post_karma_rights'),
'karma_downvote': fields.function(_get_post_karma_rights, string='Karma to downvote', type='integer', multi='_get_post_karma_rights'),
'karma_comment': fields.function(_get_post_karma_rights, string='Karma to comment', type='integer', multi='_get_post_karma_rights'),
'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'),
'can_unlink': fields.function(_get_post_karma_rights, string='Can Unlink', type='boolean', multi='_get_post_karma_rights'),
'can_upvote': fields.function(_get_post_karma_rights, string='Can Upvote', type='boolean', multi='_get_post_karma_rights'),
'can_downvote': fields.function(_get_post_karma_rights, string='Can Downvote', type='boolean', multi='_get_post_karma_rights'),
'can_comment': fields.function(_get_post_karma_rights, string='Can Comment', type='boolean', multi='_get_post_karma_rights'),
'can_comment_convert': fields.function(_get_post_karma_rights, string='Can Convert to Comment', type='boolean', multi='_get_post_karma_rights'),
} }
_defaults = { _defaults = {
@ -219,41 +314,93 @@ class Post(osv.Model):
context = {} context = {}
create_context = dict(context, mail_create_nolog=True) create_context = dict(context, mail_create_nolog=True)
post_id = super(Post, self).create(cr, uid, vals, context=create_context) post_id = super(Post, self).create(cr, uid, vals, context=create_context)
# post message + subtype depending on parent_id post = self.browse(cr, SUPERUSER_ID, post_id, context=context) # SUPERUSER_ID to avoid read access rights issues when creating
if vals.get("parent_id"): # karma-based access
parent = self.browse(cr, SUPERUSER_ID, vals['parent_id'], context=context) if post.parent_id and not post.can_ask:
body = _('<p><a href="forum/%s/question/%s">New Answer Posted</a></p>' % (slug(parent.forum_id), slug(parent))) raise KarmaError('Not enough karma to create a new question')
self.message_post(cr, uid, parent.id, subject=_('Re: %s') % parent.name, body=body, subtype='website_forum.mt_answer_new', context=context) elif not post.parent_id and not post.can_answer:
raise KarmaError('Not enough karma to answer to a question')
# messaging and chatter
base_url = self.pool['ir.config_parameter'].get_param(cr, uid, 'web.base.url')
if post.parent_id:
body = _(
'<p>A new answer for <i>%s</i> has been posted. <a href="%s/forum/%s/question/%s">Click here to access the post.</a></p>' %
(post.parent_id.name, base_url, slug(post.parent_id.forum_id), slug(post.parent_id))
)
self.message_post(cr, uid, post.parent_id.id, subject=_('Re: %s') % post.parent_id.name, body=body, subtype='website_forum.mt_answer_new', context=context)
else: else:
self.message_post(cr, uid, post_id, subject=vals.get('name', ''), body=_('New Question Created'), subtype='website_forum.mt_question_new', context=context) body = _(
self.pool['res.users'].add_karma(cr, SUPERUSER_ID, [uid], self.pool['forum.forum']._karma_gen_quest_new, context=context) '<p>A new question <i>%s</i> has been asked on %s. <a href="%s/forum/%s/question/%s">Click here to access the question.</a></p>' %
(post.name, post.forum_id.name, base_url, slug(post.forum_id), slug(post))
)
self.message_post(cr, uid, post_id, subject=post.name, body=body, subtype='website_forum.mt_question_new', context=context)
self.pool['res.users'].add_karma(cr, SUPERUSER_ID, [uid], post.forum_id.karma_gen_question_new, context=context)
return post_id return post_id
def write(self, cr, uid, ids, vals, context=None): def write(self, cr, uid, ids, vals, context=None):
Forum = self.pool['forum.forum'] posts = self.browse(cr, uid, ids, context=context)
# update karma when accepting/rejecting answers if 'state' in vals:
if vals['state'] in ['active', 'close'] and any(not post.can_close for post in posts):
raise KarmaError('Not enough karma to close or reopen a post.')
if 'active' in vals:
if any(not post.can_unlink for post in posts):
raise KarmaError('Not enough karma to delete or reactivate a post')
if 'is_correct' in vals: if 'is_correct' in vals:
if any(not post.can_accept for post in posts):
raise KarmaError('Not enough karma to accept or refuse an answer')
# update karma except for self-acceptance
mult = 1 if vals['is_correct'] else -1 mult = 1 if vals['is_correct'] else -1
for post in self.browse(cr, uid, ids, context=context): for post in self.browse(cr, uid, ids, context=context):
if vals['is_correct'] != post.is_correct: if vals['is_correct'] != post.is_correct and post.create_uid.id != uid:
self.pool['res.users'].add_karma(cr, SUPERUSER_ID, [post.create_uid.id], Forum._karma_gen_ans_accepted * mult, context=context) self.pool['res.users'].add_karma(cr, SUPERUSER_ID, [post.create_uid.id], post.forum_id.karma_gen_answer_accepted * mult, context=context)
self.pool['res.users'].add_karma(cr, SUPERUSER_ID, [uid], Forum._karma_gen_ans_accept * mult, context=context) self.pool['res.users'].add_karma(cr, SUPERUSER_ID, [uid], post.forum_id.karma_gen_answer_accept * mult, context=context)
if any(key not in ['state', 'active', 'is_correct', 'closed_uid', 'closed_date', 'closed_reason_id'] for key in vals.keys()) and any(not post.can_edit for post in posts):
raise KarmaError('Not enough karma to edit a post.')
res = super(Post, self).write(cr, uid, ids, vals, context=context) res = super(Post, self).write(cr, uid, ids, vals, context=context)
# if post content modify, notify followers # if post content modify, notify followers
if 'content' in vals or 'name' in vals: if 'content' in vals or 'name' in vals:
for post in self.browse(cr, uid, ids, context=context): for post in posts:
if post.parent_id: if post.parent_id:
body, subtype = _('Answer Edited'), 'website_forum.mt_answer_edit' body, subtype = _('Answer Edited'), 'website_forum.mt_answer_edit'
obj_id = post.parent_id.id obj_id = post.parent_id.id
else: else:
body, subtype = _('Question Edited'), 'website_forum.mt_question_edit' body, subtype = _('Question Edited'), 'website_forum.mt_question_edit'
obj_id = post.id obj_id = post.id
self.message_post(cr, uid, obj_id, body=_(body), subtype=subtype, context=context) self.message_post(cr, uid, obj_id, body=body, subtype=subtype, context=context)
return res return res
def close(self, cr, uid, ids, reason_id, context=None):
if any(post.parent_id for post in self.browse(cr, uid, ids, context=context)):
return False
return self.pool['forum.post'].write(cr, uid, ids, {
'state': 'close',
'closed_uid': uid,
'closed_date': datetime.today().strftime(tools.DEFAULT_SERVER_DATETIME_FORMAT),
'closed_reason_id': reason_id,
}, context=context)
def unlink(self, cr, uid, ids, context=None):
posts = self.browse(cr, uid, ids, context=context)
if any(not post.can_unlink for post in posts):
raise KarmaError('Not enough karma to unlink a post')
# if unlinking an answer with accepted answer: remove provided karma
for post in posts:
if post.is_correct:
self.pool['res.users'].add_karma(cr, SUPERUSER_ID, [post.create_uid.id], post.forum_id.karma_gen_answer_accepted * -1, context=context)
self.pool['res.users'].add_karma(cr, SUPERUSER_ID, [uid], post.forum_id.karma_gen_answer_accept * -1, context=context)
return super(Post, self).unlink(cr, uid, ids, context=context)
def vote(self, cr, uid, ids, upvote=True, context=None): def vote(self, cr, uid, ids, upvote=True, context=None):
posts = self.browse(cr, uid, ids, context=context)
if upvote and any(not post.can_upvote for post in posts):
raise KarmaError('Not enough karma to upvote.')
elif not upvote and any(not post.can_downvote for post in posts):
raise KarmaError('Not enough karma to downvote.')
Vote = self.pool['forum.post.vote'] Vote = self.pool['forum.post.vote']
vote_ids = Vote.search(cr, uid, [('post_id', 'in', ids), ('user_id', '=', uid)], context=context) vote_ids = Vote.search(cr, uid, [('post_id', 'in', ids), ('user_id', '=', uid)], limit=1, context=context)
if vote_ids: if vote_ids:
for vote in Vote.browse(cr, uid, vote_ids, context=context): for vote in Vote.browse(cr, uid, vote_ids, context=context):
if upvote: if upvote:
@ -267,6 +414,89 @@ class Post(osv.Model):
Vote.create(cr, uid, {'post_id': post_id, 'vote': new_vote}, context=context) Vote.create(cr, uid, {'post_id': post_id, 'vote': new_vote}, context=context)
return {'vote_count': self._get_vote_count(cr, uid, ids, None, None, context=context)[ids[0]]} return {'vote_count': self._get_vote_count(cr, uid, ids, None, None, context=context)[ids[0]]}
def convert_answer_to_comment(self, cr, uid, id, context=None):
""" Tools to convert an answer (forum.post) to a comment (mail.message).
The original post is unlinked and a new comment is posted on the question
using the post create_uid as the comment's author. """
post = self.browse(cr, uid, id, context=context)
if not post.parent_id:
return False
# karma-based action check: use the post field that computed own/all value
if not post.can_comment_convert:
raise KarmaError('Not enough karma to convert an answer to a comment')
# post the message
question = post.parent_id
values = {
'author_id': post.create_uid.partner_id.id,
'body': html2plaintext(post.content),
'type': 'comment',
'subtype': 'mail.mt_comment',
'date': post.create_date,
}
message_id = self.pool['forum.post'].message_post(
cr, uid, question.id,
context=dict(context, mail_create_nosubcribe=True),
**values)
# unlink the original answer, using SUPERUSER_ID to avoid karma issues
self.pool['forum.post'].unlink(cr, SUPERUSER_ID, [post.id], context=context)
return message_id
def convert_comment_to_answer(self, cr, uid, message_id, default=None, context=None):
""" Tool to convert a comment (mail.message) into an answer (forum.post).
The original comment is unlinked and a new answer from the comment's author
is created. Nothing is done if the comment's author already answered the
question. """
comment = self.pool['mail.message'].browse(cr, SUPERUSER_ID, message_id, context=context)
post = self.pool['forum.post'].browse(cr, uid, comment.res_id, context=context)
user = self.pool['res.users'].browse(cr, uid, uid, context=context)
if not comment.author_id or not comment.author_id.user_ids: # only comment posted by users can be converted
return False
# karma-based action check: must check the message's author to know if own / all
karma_convert = comment.author_id.id == user.partner_id.id and post.forum_id.karma_comment_convert_own or post.forum_id.karma_comment_convert_all
can_convert = uid == SUPERUSER_ID or user.karma >= karma_convert
if not can_convert:
raise KarmaError('Not enough karma to convert a comment to an answer')
# check the message's author has not already an answer
question = post.parent_id if post.parent_id else post
post_create_uid = comment.author_id.user_ids[0]
if any(answer.create_uid.id == post_create_uid.id for answer in question.child_ids):
return False
# create the new post
post_values = {
'forum_id': question.forum_id.id,
'content': comment.body,
'parent_id': question.id,
}
# done with the author user to have create_uid correctly set
new_post_id = self.pool['forum.post'].create(cr, post_create_uid.id, post_values, context=context)
# delete comment
self.pool['mail.message'].unlink(cr, SUPERUSER_ID, [comment.id], context=context)
return new_post_id
def unlink_comment(self, cr, uid, id, message_id, context=None):
comment = self.pool['mail.message'].browse(cr, SUPERUSER_ID, message_id, context=context)
post = self.pool['forum.post'].browse(cr, uid, id, context=context)
user = self.pool['res.users'].browse(cr, SUPERUSER_ID, uid, context=context)
if not comment.model == 'forum.post' or not comment.res_id == id:
return False
# karma-based action check: must check the message's author to know if own or all
karma_unlink = comment.author_id.id == user.partner_id.id and post.forum_id.karma_comment_unlink_own or post.forum_id.karma_comment_unlink_all
can_unlink = uid == SUPERUSER_ID or user.karma >= karma_unlink
if not can_unlink:
raise KarmaError('Not enough karma to unlink a comment')
return self.pool['mail.message'].unlink(cr, SUPERUSER_ID, [message_id], context=context)
def set_viewed(self, cr, uid, ids, context=None): def set_viewed(self, cr, uid, ids, context=None):
cr.execute("""UPDATE forum_post SET views = views+1 WHERE id IN %s""", (tuple(ids),)) cr.execute("""UPDATE forum_post SET views = views+1 WHERE id IN %s""", (tuple(ids),))
return True return True
@ -300,31 +530,31 @@ class Vote(osv.Model):
'vote': lambda *args: '1', 'vote': lambda *args: '1',
} }
def create(self, cr, uid, vals, context=None): def _get_karma_value(self, old_vote, new_vote, up_karma, down_karma):
vote_id = super(Vote, self).create(cr, uid, vals, context=context)
if vals.get('vote', '1') == '1':
karma = self.pool['forum.forum']._karma_upvote
elif vals.get('vote', '1') == '-1':
karma = self.pool['forum.forum']._karma_downvote
post = self.pool['forum.post'].browse(cr, uid, vals['post_id'], context=context)
self.pool['res.users'].add_karma(cr, SUPERUSER_ID, [post.create_uid.id], karma, context=context)
return vote_id
def write(self, cr, uid, ids, values, context=None):
def _get_karma_value(old_vote, new_vote, up_karma, down_karma):
_karma_upd = { _karma_upd = {
'-1': {'-1': 0, '0': -1 * down_karma, '1': -1 * down_karma + up_karma}, '-1': {'-1': 0, '0': -1 * down_karma, '1': -1 * down_karma + up_karma},
'0': {'-1': 1 * down_karma, '0': 0, '1': up_karma}, '0': {'-1': 1 * down_karma, '0': 0, '1': up_karma},
'1': {'-1': -1 * up_karma + down_karma, '0': -1 * up_karma, '1': 0} '1': {'-1': -1 * up_karma + down_karma, '0': -1 * up_karma, '1': 0}
} }
return _karma_upd[old_vote][new_vote] return _karma_upd[old_vote][new_vote]
def create(self, cr, uid, vals, context=None):
vote_id = super(Vote, self).create(cr, uid, vals, context=context)
vote = self.browse(cr, uid, vote_id, context=context)
if vote.post_id.parent_id:
karma_value = self._get_karma_value('0', vote.vote, vote.post_id.forum_id.karma_gen_answer_upvote, vote.post_id.forum_id.karma_gen_answer_downvote)
else:
karma_value = self._get_karma_value('0', vote.vote, vote.post_id.forum_id.karma_gen_question_upvote, vote.post_id.forum_id.karma_gen_question_downvote)
self.pool['res.users'].add_karma(cr, SUPERUSER_ID, [vote.post_id.create_uid.id], karma_value, context=context)
return vote_id
def write(self, cr, uid, ids, values, context=None):
if 'vote' in values: if 'vote' in values:
Forum = self.pool['forum.forum']
for vote in self.browse(cr, uid, ids, context=context): for vote in self.browse(cr, uid, ids, context=context):
if vote.post_id.parent_id: if vote.post_id.parent_id:
karma_value = _get_karma_value(vote.vote, values['vote'], Forum._karma_gen_upvote_ans, Forum._karma_gen_downvote_ans) karma_value = self._get_karma_value(vote.vote, values['vote'], vote.post_id.forum_id.karma_gen_answer_upvote, vote.post_id.forum_id.karma_gen_answer_downvote)
else: else:
karma_value = _get_karma_value(vote.vote, values['vote'], Forum._karma_gen_upvote_quest, Forum._karma_gen_downvote_quest) karma_value = self._get_karma_value(vote.vote, values['vote'], vote.post_id.forum_id.karma_gen_question_upvote, vote.post_id.forum_id.karma_gen_question_downvote)
self.pool['res.users'].add_karma(cr, SUPERUSER_ID, [vote.post_id.create_uid.id], karma_value, context=context) self.pool['res.users'].add_karma(cr, SUPERUSER_ID, [vote.post_id.create_uid.id], karma_value, context=context)
res = super(Vote, self).write(cr, uid, ids, values, context=context) res = super(Vote, self).write(cr, uid, ids, values, context=context)
return res return res

View File

@ -104,6 +104,14 @@ a.no-decoration {
height: 1.2em !important; height: 1.2em !important;
} }
.oe_forum_alert {
position: absolute;
margin-top: -30px;
margin-left: 90px;
width: 300px;
z-index: 9999;
}
button.btn-link.text-muted { button.btn-link.text-muted {
color: #999999; color: #999999;
} }

View File

@ -84,5 +84,12 @@ a.no-decoration
font: 1.2em "Helvetica Neue", Helvetica, Arial, sans-serif !important font: 1.2em "Helvetica Neue", Helvetica, Arial, sans-serif !important
height: 1.2em !important height: 1.2em !important
.oe_forum_alert
position: absolute
margin-top: -30px
margin-left: 90px
width: 300px
z-index: 9999
button.btn-link.text-muted button.btn-link.text-muted
color: #999 color: #999

View File

@ -1,28 +1,37 @@
$(document).ready(function () { $(document).ready(function () {
$('.vote_up ,.vote_down').on('click', function (ev) { $('.karma_required').on('click', function (ev) {
var karma = $(ev.currentTarget).data('karma');
if (karma) {
ev.preventDefault();
var $warning = $('<div class="alert alert-danger alert-dismissable oe_forum_alert" id="karma_alert">'+
'<button type="button" class="close notification_close" data-dismiss="alert" aria-hidden="true">&times;</button>'+
karma + ' karma is required to perform this action. You can earn karma by answering questions or having '+
'your answers upvoted by the community.</div>');
var vote_alert = $(ev.currentTarget).parent().find("#vote_alert");
if (vote_alert.length == 0) {
$(ev.currentTarget).parent().append($warning);
}
}
});
$('.vote_up,.vote_down').not('.karma_required').on('click', function (ev) {
ev.preventDefault(); ev.preventDefault();
var $link = $(ev.currentTarget); var $link = $(ev.currentTarget);
openerp.jsonRpc($link.data('href'), 'call', {}) openerp.jsonRpc($link.data('href'), 'call', {})
.then(function (data) { .then(function (data) {
if (data['error']){ if (data['error']){
if (data['error'] == 'own_post'){ if (data['error'] == 'own_post'){
var $warning = $('<div class="alert alert-danger alert-dismissable" id="vote_alert" style="position:absolute; margin-top: -30px; margin-left: 90px;">'+ var $warning = $('<div class="alert alert-danger alert-dismissable oe_forum_alert" id="vote_alert">'+
'<button type="button" class="close notification_close" data-dismiss="alert" aria-hidden="true">&times;</button>'+ '<button type="button" class="close notification_close" data-dismiss="alert" aria-hidden="true">&times;</button>'+
'Sorry, you cannot vote for your own posts'+ 'Sorry, you cannot vote for your own posts'+
'</div>'); '</div>');
} else if (data['error'] == 'anonymous_user'){ } else if (data['error'] == 'anonymous_user'){
var $warning = $('<div class="alert alert-danger alert-dismissable" id="vote_alert" style="position:absolute; margin-top: -30px; margin-left: 90px;">'+ var $warning = $('<div class="alert alert-danger alert-dismissable oe_forum_alert" id="vote_alert">'+
'<button type="button" class="close notification_close" data-dismiss="alert" aria-hidden="true">&times;</button>'+ '<button type="button" class="close notification_close" data-dismiss="alert" aria-hidden="true">&times;</button>'+
'Sorry you must be logged to vote'+ 'Sorry you must be logged to vote'+
'</div>'); '</div>');
} }
else if (data['error'] == 'not_enough_karma') {
var $warning = $('<div class="alert alert-danger alert-dismissable" id="vote_alert" style="max-width: 500px; position:absolute; margin-top: -30px; margin-left: 90px;">'+
'<button type="button" class="close notification_close" data-dismiss="alert" aria-hidden="true">&times;</button>'+
'Sorry, at least ' + data['karma'] + ' karma is required to vote. You can gain karma by answering questions and receiving votes.'+
'</div>');
}
vote_alert = $link.parent().find("#vote_alert"); vote_alert = $link.parent().find("#vote_alert");
if (vote_alert.length == 0) { if (vote_alert.length == 0) {
$link.parent().append($warning); $link.parent().append($warning);
@ -44,21 +53,16 @@ $(document).ready(function () {
return true; return true;
}); });
$('.accept_answer').on('click', function (ev) { $('.accept_answer').not('.karma_required').on('click', function (ev) {
ev.preventDefault(); ev.preventDefault();
var $link = $(ev.currentTarget); var $link = $(ev.currentTarget);
openerp.jsonRpc($link.data('href'), 'call', {}).then(function (data) { openerp.jsonRpc($link.data('href'), 'call', {}).then(function (data) {
if (data['error']) { if (data['error']) {
if (data['error'] == 'anonymous_user'){ if (data['error'] == 'anonymous_user') {
var $warning = $('<div class="alert alert-danger alert-dismissable" id="correct_answer_alert" style="position:absolute; margin-top: -30px; margin-left: 90px;">'+ var $warning = $('<div class="alert alert-danger alert-dismissable" id="correct_answer_alert" style="position:absolute; margin-top: -30px; margin-left: 90px;">'+
'<button type="button" class="close notification_close" data-dismiss="alert" aria-hidden="true">&times;</button>'+ '<button type="button" class="close notification_close" data-dismiss="alert" aria-hidden="true">&times;</button>'+
'Sorry, anonymous users cannot choose correct answer.'+ 'Sorry, anonymous users cannot choose correct answer.'+
'</div>'); '</div>');
} else if (data['error'] == 'not_enough_karma') {
var $warning = $('<div class="alert alert-danger alert-dismissable" id="vote_alert" style="max-width: 500px; position:absolute; margin-top: -30px; margin-left: 90px;">'+
'<button type="button" class="close notification_close" data-dismiss="alert" aria-hidden="true">&times;</button>'+
'Sorry, at least ' + data['karma'] + ' karma is required to accept this answer. You can gain karma by answering questions and receiving votes.'+
'</div>');
} }
correct_answer_alert = $link.parent().find("#correct_answer_alert"); correct_answer_alert = $link.parent().find("#correct_answer_alert");
if (correct_answer_alert.length == 0) { if (correct_answer_alert.length == 0) {

View File

@ -24,6 +24,27 @@
<sheet> <sheet>
<group> <group>
<field name="name"/> <field name="name"/>
<field name="karma_ask"/>
<field name="karma_edit_own"/>
<field name="karma_edit_all"/>
<field name="karma_close_own"/>
<field name="karma_close_all"/>
<field name="karma_unlink_own"/>
<field name="karma_unlink_all"/>
</group>
<group>
<field name="karma_upvote"/>
<field name="karma_downvote"/>
<field name="karma_answer_accept_own"/>
<field name="karma_answer_accept_all"/>
<field name="karma_editor_link_files"/>
<field name="karma_editor_clickable_link"/>
<field name="karma_comment_own"/>
<field name="karma_comment_all"/>
<field name="karma_comment_convert_own"/>
<field name="karma_comment_convert_all"/>
<field name="karma_comment_unlink_own"/>
<field name="karma_comment_unlink_all"/>
</group> </group>
</sheet> </sheet>
<div class="oe_chatter"> <div class="oe_chatter">

View File

@ -25,7 +25,7 @@
<!-- helper --> <!-- helper -->
<template id="link_button"> <template id="link_button">
<form method="POST" t-att-action="url"> <form method="POST" t-att-action="url">
<button t-attf-class="fa btn-link #{classes}"> <button t-attf-class="fa btn-link #{classes} #{karma and 'karma_required text-muted' or ''}" t-attf-data-karma="#{karma}">
<t t-esc="label"/></button> <t t-esc="label"/></button>
</form> </form>
</template> </template>
@ -163,7 +163,7 @@
<div t-if="question.child_count&lt;=1" class="subtitle">Answer</div> <div t-if="question.child_count&lt;=1" class="subtitle">Answer</div>
</div> </div>
</div> </div>
<div class="col-md-10"> <div class="col-md-10 clearfix">
<div class="question-name"> <div class="question-name">
<a t-attf-href="/forum/#{ slug(forum) }/question/#{ slug(question) }" t-field="question.name"/> <a t-attf-href="/forum/#{ slug(forum) }/question/#{ slug(question) }" t-field="question.name"/>
<span t-if="not question.active"><b> [Deleted]</b></span> <span t-if="not question.active"><b> [Deleted]</b></span>
@ -419,10 +419,12 @@
<template id="vote"> <template id="vote">
<div t-attf-class="box oe_grey"> <div t-attf-class="box oe_grey">
<a t-attf-class="vote_up fa fa-thumbs-up no-decoration #{post.user_vote == 1 and 'text-success' or ''}" <a t-attf-class="vote_up fa fa-thumbs-up no-decoration #{post.user_vote == 1 and 'text-success' or ''} #{((post.user_vote == 1 and not post.can_downvote) or not post.can_upvote) and 'karma_required' or ''}"
t-attf-data-karma="#{post.user_vote == 1 and post.karma_downvote or post.karma_upvote}"
t-attf-data-href="/forum/#{slug(post.forum_id)}/post/#{slug(post)}/upvote"/> t-attf-data-href="/forum/#{slug(post.forum_id)}/post/#{slug(post)}/upvote"/>
<span id="vote_count" t-esc="post.vote_count"/> <span id="vote_count" t-esc="post.vote_count"/>
<a t-attf-class="vote_down fa fa-thumbs-down no-decoration #{post.user_vote == -1 and 'text-warning' or ''}" <a t-attf-class="vote_down fa fa-thumbs-down no-decoration #{post.user_vote == -1 and 'text-warning' or ''} #{((post.user_vote == -1 and not post.can_upvote) or not post.can_downvote) and 'karma_required' or ''}"
t-attf-data-karma="#{post.user_vote == -1 and post.karma_uovote or post.karma_downvote}"
t-attf-data-href="/forum/#{slug(post.forum_id)}/post/#{slug(post)}/downvote"/> t-attf-data-href="/forum/#{slug(post.forum_id)}/post/#{slug(post)}/downvote"/>
<div t-if="vote_count &gt; 1" class="subtitle"> <div t-if="vote_count &gt; 1" class="subtitle">
votes votes
@ -449,7 +451,7 @@
t-attf-class="favourite_question no-decoration fa fa-2x fa-star #{question.user_favourite and 'forum_favourite_question' or ''}"/> t-attf-class="favourite_question no-decoration fa fa-2x fa-star #{question.user_favourite and 'forum_favourite_question' or ''}"/>
</div> </div>
</div> </div>
<div style="col-md-10"> <div class="col-md-10">
<h1 class="mt0"> <h1 class="mt0">
<a t-attf-href="/forum/#{ slug(forum) }/question/#{ slug(question) }" t-field="question.name"/> <a t-attf-href="/forum/#{ slug(forum) }/question/#{ slug(question) }" t-field="question.name"/>
<span t-if="not question.active"><b> [Deleted]</b></span> <span t-if="not question.active"><b> [Deleted]</b></span>
@ -466,11 +468,12 @@
style="display: inline-block;"/></b> style="display: inline-block;"/></b>
</t> </t>
<b>on <span t-field="question.closed_date"/></b> <b>on <span t-field="question.closed_date"/></b>
<div t-if="question.state == 'close' and user.karma&gt;=500" class="mt16 mb24 text-center"> <div class="mt16 mb24 text-center">
<t t-call="website_forum.link_button"> <t t-call="website_forum.link_button">
<t t-set="url" t-value="'/forum/' + slug(forum) + '/question/' + slug(question) + '/reopen'"/> <t t-set="url" t-value="'/forum/' + slug(forum) + '/question/' + slug(question) + '/reopen'"/>
<t t-set="label" t-value="'Reopen'"/> <t t-set="label" t-value="'Reopen'"/>
<t t-set="classes" t-value="'fa-arrow-right'"/> <t t-set="classes" t-value="'fa-arrow-right'"/>
<t t-set="karma" t-value="not question.can_close and question.karma_close or 0"/>
</t> </t>
</div> </div>
</div> </div>
@ -483,45 +486,52 @@
</t> </t>
</div> </div>
<ul class="list-inline" id="options"> <ul class="list-inline" id="options">
<li t-if="user.id == question.create_uid.id or user.karma&gt;=50"> <li>
<a style="cursor: pointer" data-toggle="collapse" class="text-muted fa fa-comment-o" <a style="cursor: pointer" data-toggle="collapse"
t-attf-class="fa fa-comment-o #{not question.can_comment and 'karma_required text-muted' or ''}"
t-attf-data-karma="#{not question.can_comment and question.karma_comment or 0}"
t-attf-data-target="#comment#{ question._name.replace('.','') + '-' + str(question.id) }"> t-attf-data-target="#comment#{ question._name.replace('.','') + '-' + str(question.id) }">
Comment Comment
</a> </a>
</li> </li>
<li t-if="question.state != 'close' and ((user.id == question.create_uid.id and can_close_own) or can_close_all)"> <li t-if="question.state != 'close'">
<t t-call="website_forum.link_button"> <t t-call="website_forum.link_button">
<t t-set="url" t-value="'/forum/' + slug(forum) +'/question/' + slug(question) + '/ask_for_close'"/> <t t-set="url" t-value="'/forum/' + slug(forum) +'/question/' + slug(question) + '/ask_for_close'"/>
<t t-set="label" t-value="'Close'"/> <t t-set="label" t-value="'Close'"/>
<t t-set="classes" t-vaoue="'text-muted fa-times'"/> <t t-set="classes" t-value="'fa-times'"/>
<t t-set="karma" t-value="not question.can_close and question.karma_close or 0"/>
</t> </t>
</li> </li>
<li t-if="question.state == 'close' and ((user.id == question.create_uid.id and can_close_own) or can_close_all)"> <li t-if="question.state == 'close'">
<t t-call="website_forum.link_button"> <t t-call="website_forum.link_button">
<t t-set="url" t-value="'/forum/' + slug(forum) +'/question/' + slug(question) + '/reopen'"/> <t t-set="url" t-value="'/forum/' + slug(forum) +'/question/' + slug(question) + '/reopen'"/>
<t t-set="label" t-value="'Reopen'"/> <t t-set="label" t-value="'Reopen'"/>
<t t-set="classes" t-value="'text-muted fa-undo'"/> <t t-set="classes" t-value="'fa-undo'"/>
<t t-set="karma" t-value="not question.can_close and question.karma_close or 0"/>
</t> </t>
</li> </li>
<li t-if="(user.id == question.create_uid.id and can_edit_own) or can_edit_all"> <li>
<t t-call="website_forum.link_button"> <t t-call="website_forum.link_button">
<t t-set="url" t-value="'/forum/' + slug(forum) +'/post/' + slug(question) + '/edit'"/> <t t-set="url" t-value="'/forum/' + slug(forum) +'/post/' + slug(question) + '/edit'"/>
<t t-set="label" t-value="'Edit'"/> <t t-set="label" t-value="'Edit'"/>
<t t-set="classes" t-value="'text-muted fa-edit'"/> <t t-set="classes" t-value="'fa-edit'"/>
<t t-set="karma" t-value="not question.can_edit and question.karma_edit or 0"/>
</t> </t>
</li> </li>
<li t-if="question.active and ((user.id == question.create_uid.id and can_unlink_own) or can_unlink_all)"> <li t-if="question.active">
<t t-call="website_forum.link_button"> <t t-call="website_forum.link_button">
<t t-set="url" t-value="'/forum/' + slug(forum) +'/question/' + slug(question) + '/delete'"/> <t t-set="url" t-value="'/forum/' + slug(forum) +'/question/' + slug(question) + '/delete'"/>
<t t-set="label" t-value="'Delete'"/> <t t-set="label" t-value="'Delete'"/>
<t t-set="classes" t-value="'text-muted fa-trash-o'"/> <t t-set="classes" t-value="'fa-trash-o'"/>
<t t-set="karma" t-value="not question.can_unlink and question.karma_unlink or 0"/>
</t> </t>
</li> </li>
<li t-if="not question.active and ((user.id == question.create_uid.id and can_unlink_own) or can_unlink_all)"> <li t-if="not question.active">
<t t-call="website_forum.link_button"> <t t-call="website_forum.link_button">
<t t-set="url" t-value="'/forum/' + slug(forum) +'/question/' + slug(question) + '/undelete'"/> <t t-set="url" t-value="'/forum/' + slug(forum) +'/question/' + slug(question) + '/undelete'"/>
<t t-set="label" t-value="'Undelete'"/> <t t-set="label" t-value="'Undelete'"/>
<t t-set="classes" t-value="'text-muted fa-trash-o'"/> <t t-set="classes" t-value="'fa-trash-o'"/>
<t t-set="karma" t-value="not question.can_unlink and question.karma_unlink or 0"/>
</t> </t>
</li> </li>
</ul> </ul>
@ -565,7 +575,8 @@
<t t-set="post" t-value="answer"/> <t t-set="post" t-value="answer"/>
</t> </t>
<div class="text-muted mt8"> <div class="text-muted mt8">
<a t-attf-class="accept_answer fa fa-2x fa-check-circle no-decoration #{answer.is_correct and 'oe_answer_true' or 'oe_answer_false'}" <a t-attf-class="accept_answer fa fa-2x fa-check-circle no-decoration #{answer.is_correct and 'oe_answer_true' or 'oe_answer_false'} #{not answer.can_accept and 'karma_required' or ''}"
t-attf-data-karma="#{answer.karma_accept}"
t-attf-data-href="/forum/#{slug(question.forum_id)}/post/#{slug(answer)}/toggle_correct"/> t-attf-data-href="/forum/#{slug(question.forum_id)}/post/#{slug(answer)}/toggle_correct"/>
</div> </div>
</div> </div>
@ -573,26 +584,35 @@
<t t-raw="answer.content"/> <t t-raw="answer.content"/>
<div class="mt16"> <div class="mt16">
<ul class="list-inline pull-right"> <ul class="list-inline pull-right">
<li t-if="user.id == answer.create_uid.id or user.karma&gt;=50"> <li>
<a style="cursor: pointer" data-toggle="collapse" class="text-muted fa fa-comment-o" <a t-attf-class="fa fa-comment-o #{not answer.can_comment and 'karma_required text-muted' or ''}"
t-attf-data-karma="#{not answer.can_comment and answer.karma_comment or 0}"
style="cursor: pointer" data-toggle="collapse"
t-attf-data-target="#comment#{ answer._name.replace('.','') + '-' + str(answer.id) }"> Comment t-attf-data-target="#comment#{ answer._name.replace('.','') + '-' + str(answer.id) }"> Comment
</a> </a>
</li> </li>
<li t-if="(user.id == answer.create_uid.id and can_edit_own) or can_edit_all"> <li>
<a class="text-muted fa fa-edit" 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>
<li t-if="(user.id == answer.create_uid.id and can_unlink_own) or can_unlink_all"> <li>
<t t-call="website_forum.link_button"> <t t-call="website_forum.link_button">
<t t-set="url" t-value="'/forum/' + slug(forum) + '/post/' + slug(answer) + '/delete'"/> <t t-set="url" t-value="'/forum/' + slug(forum) + '/post/' + slug(answer) + '/delete'"/>
<t t-set="label" t-value="'Delete'"/> <t t-set="label" t-value="'Delete'"/>
<t t-set="classes" t-value="'text-muted fa-trash-o'"/> <t t-set="classes" t-value="'fa-trash-o'"/>
<t t-set="karma" t-value="not answer.can_unlink and answer.karma_unlink or 0"/>
</t> </t>
</li> </li>
<li t-if="user.id == answer.create_uid.id"> <li>
<t t-call="website_forum.link_button"> <t t-call="website_forum.link_button">
<t t-set="url" t-value="'/forum/' + slug(forum) + '/post/' + slug(answer) + '/convert_to_comment'"/> <t t-set="url" t-value="'/forum/' + slug(forum) + '/post/' + slug(answer) + '/convert_to_comment'"/>
<t t-set="label" t-value="'Convert as a comment'"/> <t t-set="label" t-value="'Convert as a comment'"/>
<t t-set="classes" t-value="'text-muted fa-magic'"/> <t t-set="classes" t-value="'fa-magic'"/>
<t t-set="karma" t-value="not answer.can_comment_convert and answer.karma_comment_convert or 0"/>
</t> </t>
</li> </li>
</ul> </ul>
@ -610,7 +630,8 @@
<t t-set="post" t-value="answer"/> <t t-set="post" t-value="answer"/>
</t> </t>
<div class="text-muted mt8"> <div class="text-muted mt8">
<a t-attf-class="accept_answer fa fa-2x fa-check-circle no-decoration #{answer.is_correct and 'oe_answer_true' or 'oe_answer_false'}" <a t-attf-class="accept_answer fa fa-2x fa-check-circle no-decoration #{answer.is_correct and 'oe_answer_true' or 'oe_answer_false'} #{not answer.can_accept and 'karma_required' or ''}"
t-attf-data-karma="#{answer.karma_accept}"
t-attf-data-href="/forum/#{slug(question.forum_id)}/post/#{slug(answer)}/toggle_correct"/> t-attf-data-href="/forum/#{slug(question.forum_id)}/post/#{slug(answer)}/toggle_correct"/>
</div> </div>
</div> </div>
@ -641,10 +662,12 @@
t-attf-data-href="/forum/#{slug(forum)}/post/#{slug(object)}/comment/#{slug(message)}/delete" t-attf-data-href="/forum/#{slug(forum)}/post/#{slug(object)}/comment/#{slug(message)}/delete"
class="close comment_delete">&amp;times;</button> class="close comment_delete">&amp;times;</button>
<span t-field="message.body"/> <span t-field="message.body"/>
<t t-set="required_karma" t-value="message.author_id.id == user.partner_id.id and object.forum_id.karma_comment_convert_own or object.forum_id.karma_comment_convert_all"/>
<t t-call="website_forum.link_button"> <t t-call="website_forum.link_button">
<t t-set="url" t-value="'/forum/' + slug(forum) + '/post/' + slug(object) + '/comment/' + slug(message) + '/convert_to_answer'"/> <t t-set="url" t-value="'/forum/' + slug(forum) + '/post/' + slug(object) + '/comment/' + slug(message) + '/convert_to_answer'"/>
<t t-set="label" t-value="'Convert as an answer'"/> <t t-set="label" t-value="'Convert as an answer'"/>
<t t-set="classes" t-value="'text-muted fa-magic pull-right'"/> <t t-set="karma" t-value="user.karma&lt;required_karma and required_karma or 0"/>
<t t-set="classes" t-value="'fa-magic pull-right'"/>
</t> </t>
<a t-attf-href="/forum/#{slug(forum)}/partner/#{message.author_id.id}" <a t-attf-href="/forum/#{slug(forum)}/partner/#{message.author_id.id}"
t-field="message.author_id" t-field-options='{"widget": "contact", "country_image": true, "fields": ["name", "country_id"]}' t-field="message.author_id" t-field-options='{"widget": "contact", "country_image": true, "fields": ["name", "country_id"]}'

View File

@ -4,7 +4,7 @@
<template id="header" inherit_id="website.layout" name="LiveChat Import (internal) Scripts"> <template id="header" inherit_id="website.layout" name="LiveChat Import (internal) Scripts">
<xpath expr="//head" position="inside"> <xpath expr="//head" position="inside">
<t t-if="website.channel_id"> <t t-if="website and website.channel_id">
<t t-raw="website.channel_id.script_internal"/> <t t-raw="website.channel_id.script_internal"/>
</t> </t>
</xpath> </xpath>

View File

@ -1,92 +1,47 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<openerp> <openerp>
<data> <data>
<template id="editor_head" inherit_id="report.html_container" name="Editor" groups="base.group_website_publisher">
<template id="layout" inherit_id="website.layout" primary="True">
<!-- Add report attributes -->
<xpath expr="//html" position="attributes"> <xpath expr="//html" position="attributes">
<attribute name="t-att-data-website-id">website.id if editable and website else None</attribute> <attribute name="t-att-data-report-margin-top">data_report_margin_top if data_report_margin_top else None</attribute>
<attribute name="t-att-data-translatable">'1' if translatable else None</attribute> <attribute name="t-att-data-report-header-spacing">data_report_header_spacing if data_report_header_spacing else None</attribute>
<attribute name="t-att-data-report-dpi">data_report_dpi if data_report_dpi else None</attribute>
</xpath>
<!-- Add report style -->
<xpath expr="//head" position="inside">
<style type="text/css">
<t t-call="report.style"/>
</style>
</xpath> </xpath>
<xpath expr="//body" position="attributes"> <xpath expr="//body" position="attributes">
<attribute name="style">padding-top: 51px;</attribute> <attribute name="class">container</attribute>
</xpath> </xpath>
<xpath expr="//body" position="inside"> <xpath expr="//header" position="replace"/>
<div id="website-top-navbar-placeholder" class="navbar navbar-inverse navbar-fixed-top hidden-xs"> <xpath expr="//footer" position="replace">
<div class="navbar-header"> <ul class="list-inline js_language_selector mt16" t-if="(request and request.website_multilang and len(languages) &gt; 1) or (website and editable)">
<form class="navbar-form navbar-left">
<button type="button" class="btn btn-primary">Edit</button>
</form>
</div>
<div class="collapse navbar-collapse navbar-edit-collapse">
<ul class="nav navbar-nav navbar-right">
<li><a href="#" onclick="return false;"><i class="fa fa-mobile" title="Mobile preview"/></a></li>
<li class="divider-vertical"/>
<li><a href="#" onclick="return false;"><span title="Promote page on the web">Promote</span></a></li>
<li class="dropdown">
<a href="#" onclick="return false;">Content <span class="caret"/></a>
</li>
<li class="dropdown">
<a href="#" onclick="return false;">Customize <span class="caret"/></a>
</li>
<li class="dropdown">
<a href="#" onclick="return false;">Help <span class="caret"/></a>
</li>
</ul>
</div>
</div>
</xpath>
<xpath expr='//t[@name="layout_head"]' position="before">
<link rel='stylesheet' href='/website/static/src/css/snippets.css'/>
<link rel='stylesheet' href='/website/static/src/css/editor.css'/>
<link rel="stylesheet" href="/web/static/lib/select2/select2.css"/>
<link rel="stylesheet" href="/website/static/lib/select2-bootstrap-css/select2-bootstrap.css"/>
<link rel='stylesheet' href="/web/static/lib/jquery.ui/css/smoothness/jquery-ui-1.9.1.custom.css"/>
<script type="text/javascript" src="/web/static/lib/select2/select2.js"></script>
<script type="text/javascript" src="/web/static/lib/ckeditor/ckeditor.js"></script>
<script type="text/javascript" src="/website/static/lib/ace/ace.js"></script>
<script type="text/javascript" src="/website/static/lib/vkbeautify/vkbeautify.0.99.00.beta.js"></script>
<script type="text/javascript" src="/web/static/lib/jquery.ui/js/jquery-ui-1.9.1.custom.js"></script>
<!-- mutation observers shim backed by mutation events (8 < IE < 11, Safari < 6, FF < 14, Chrome < 17) -->
<script type="text/javascript" src="/website/static/lib//jquery.mjs.nestedSortable/jquery.mjs.nestedSortable.js"></script>
<script type="text/javascript" src="/website/static/lib/MutationObservers/test/sidetable.js"></script>
<script type="text/javascript" src='/website/static/lib/nearest/jquery.nearest.js'></script>
<script type="text/javascript" src="/website/static/lib/MutationObservers/MutationObserver.js"></script>
<script type="text/javascript" src="/website/static/lib/jquery.placeholder/jquery.placeholder.js"></script>
<script type="text/javascript" src="/website/static/src/js/website.editor.js"></script>
<script type="text/javascript" src="/website/static/src/js/website.menu.js"></script>
<script type="text/javascript" src="/website/static/src/js/website.editor.newpage.js"></script> <!-- groups="base.group_website_designer" -->
<script type="text/javascript" src="/website/static/src/js/website.contentMenu.js"></script> <!-- groups="base.group_website_designer" -->
<script type="text/javascript" src="/website/static/src/js/website.mobile.js"></script>
<script type="text/javascript" src="/website/static/src/js/website.seo.js"></script>
<script type="text/javascript" src="/website/static/src/js/website.snippets.editor.js"></script>
<script type="text/javascript" src="/website/static/src/js/website.ace.js"></script>
<script type="text/javascript" src="/website/static/src/js/website.translator.js"></script>
<script type="text/javascript" src="/website/static/src/js/jQuery.transfo.js"></script>
</xpath>
<xpath expr='//body[@class="container"]/div[@id="wrapwrap"]' position="before">
<t t-set="languages" t-value="website.get_languages() if website else None"/>
<t t-if="languages">
<ul class="list-inline js_language_selector mt16" t-if="(len(languages) &gt; 1 or editable)">
<li t-foreach="languages" t-as="lg"> <li t-foreach="languages" t-as="lg">
<a t-att-href="url_for('', lang=lg[0]) + '?' + keep_query()" <a t-att-href="url_for(request.httprequest.path + '?' + keep_query(), lang=lg[0])"
t-att-data-default-lang="editable and 'true' if lg[0] == website.default_lang_code else None"> t-att-data-default-lang="editable and 'true' if website and lg[0] == website.default_lang_code else None">
<t t-esc="lg[1].split('/').pop()"/> <t t-esc="lg[1].split('/').pop()"/>
</a> </a>
</li> </li>
<li groups="base.group_website_publisher"> <li groups="base.group_website_publisher">
<t t-set="url_return" t-value="url_for('', '[lang]') + '?' + keep_query()"/> <t t-set="url_return" t-value="url_for('', '[lang]') + '?' + keep_query()"/>
<a t-attf-href="/web#action=base.action_view_base_language_install&amp;website_id=#{website.id}&amp;url_return=#{url_return}"> <a t-attf-href="/web#action=base.action_view_base_language_install&amp;website_id=#{website.id if website else ''}&amp;url_return=#{url_return}">
<i class="fa fa-plus-circle"/> <i class="fa fa-plus-circle"/>
Add a language... Add a language...
</a> </a>
</li> </li>
</ul> </ul>
</xpath>
</template>
<template id="website_html_container" inherit_id="report.html_container">
<xpath expr="//t[@t-call='report.layout']" position="replace">
<t t-call="website_report.layout">
<t t-raw="0"/>
</t> </t>
</xpath> </xpath>
</template> </template>

View File

@ -55,6 +55,7 @@
}, },
{ {
waitNot: '.product_price .oe_currency_value:visible:containsExact(1.00)', waitNot: '.product_price .oe_currency_value:visible:containsExact(1.00)',
waitFor: '#snippet_structure',
element: '#wrap img.product_detail_img', element: '#wrap img.product_detail_img',
placement: 'top', placement: 'top',
title: _t("Update image"), title: _t("Update image"),

View File

@ -68,7 +68,7 @@ def cmd_setup_git():
git_dir = os.getcwd() git_dir = os.getcwd()
if git_dir: if git_dir:
# push sane config for git < 2.0, and hooks # push sane config for git < 2.0, and hooks
run('git','config','push.default','simple') #run('git','config','push.default','simple')
# alias # alias
run('git','config','alias.st','status') run('git','config','alias.st','status')
# merge bzr style # merge bzr style

View File

@ -36,15 +36,30 @@ class ir_filters(osv.osv):
default.update({'name':_('%s (copy)') % name}) default.update({'name':_('%s (copy)') % name})
return super(ir_filters, self).copy(cr, uid, id, default, context) return super(ir_filters, self).copy(cr, uid, id, default, context)
def get_filters(self, cr, uid, model): def _get_action_domain(self, cr, uid, action_id=None):
"""Return a domain component for matching filters that are visible in the
same context (menu/view) as the given action."""
if action_id:
# filters specific to this menu + global ones
return [('action_id', 'in' , [action_id, False])]
# only global ones
return [('action_id', '=', False)]
def get_filters(self, cr, uid, model, action_id=None):
"""Obtain the list of filters available for the user on the given model. """Obtain the list of filters available for the user on the given model.
:param action_id: optional ID of action to restrict filters to this action
plus global filters. If missing only global filters are returned.
The action does not have to correspond to the model, it may only be
a contextual action.
:return: list of :meth:`~osv.read`-like dicts containing the :return: list of :meth:`~osv.read`-like dicts containing the
``name``, ``is_default``, ``domain``, ``user_id`` (m2o tuple) and ``name``, ``is_default``, ``domain``, ``user_id`` (m2o tuple),
``context`` of the matching ``ir.filters``. ``action_id`` (m2o tuple) and ``context`` of the matching ``ir.filters``.
""" """
# available filters: private filters (user_id=uid) and public filters (uid=NULL) # available filters: private filters (user_id=uid) and public filters (uid=NULL),
filter_ids = self.search(cr, uid, # and filters for the action (action_id=action_id) or global (action_id=NULL)
action_domain = self._get_action_domain(cr, uid, action_id)
filter_ids = self.search(cr, uid, action_domain +
[('model_id','=',model),('user_id','in',[uid, False])]) [('model_id','=',model),('user_id','in',[uid, False])])
my_filters = self.read(cr, uid, filter_ids, my_filters = self.read(cr, uid, filter_ids,
['name', 'is_default', 'domain', 'context', 'user_id']) ['name', 'is_default', 'domain', 'context', 'user_id'])
@ -66,7 +81,8 @@ class ir_filters(osv.osv):
:raises openerp.exceptions.Warning: if there is an existing default and :raises openerp.exceptions.Warning: if there is an existing default and
we're not updating it we're not updating it
""" """
existing_default = self.search(cr, uid, [ action_domain = self._get_action_domain(cr, uid, vals.get('action_id'))
existing_default = self.search(cr, uid, action_domain + [
('model_id', '=', vals['model_id']), ('model_id', '=', vals['model_id']),
('user_id', '=', False), ('user_id', '=', False),
('is_default', '=', True)], context=context) ('is_default', '=', True)], context=context)
@ -83,7 +99,9 @@ class ir_filters(osv.osv):
def create_or_replace(self, cr, uid, vals, context=None): def create_or_replace(self, cr, uid, vals, context=None):
lower_name = vals['name'].lower() lower_name = vals['name'].lower()
matching_filters = [f for f in self.get_filters(cr, uid, vals['model_id']) action_id = vals.get('action_id')
current_filters = self.get_filters(cr, uid, vals['model_id'], action_id)
matching_filters = [f for f in current_filters
if f['name'].lower() == lower_name if f['name'].lower() == lower_name
# next line looks for matching user_ids (specific or global), i.e. # next line looks for matching user_ids (specific or global), i.e.
# f.user_id is False and vals.user_id is False or missing, # f.user_id is False and vals.user_id is False or missing,
@ -92,18 +110,22 @@ class ir_filters(osv.osv):
if vals.get('is_default'): if vals.get('is_default'):
if vals.get('user_id'): if vals.get('user_id'):
act_ids = self.search(cr, uid, [ # Setting new default: any other default that belongs to the user
# should be turned off
action_domain = self._get_action_domain(cr, uid, action_id)
act_ids = self.search(cr, uid, action_domain + [
('model_id', '=', vals['model_id']), ('model_id', '=', vals['model_id']),
('user_id', '=', vals['user_id']), ('user_id', '=', vals['user_id']),
('is_default', '=', True), ('is_default', '=', True),
], context=context) ], context=context)
if act_ids:
self.write(cr, uid, act_ids, {'is_default': False}, context=context) self.write(cr, uid, act_ids, {'is_default': False}, context=context)
else: else:
self._check_global_default( self._check_global_default(
cr, uid, vals, matching_filters, context=None) cr, uid, vals, matching_filters, context=None)
# When a filter exists for the same (name, model, user) triple, we simply # When a filter exists for the same (name, model, user) triple, we simply
# replace its definition. # replace its definition (considering action_id irrelevant here)
if matching_filters: if matching_filters:
self.write(cr, uid, matching_filters[0]['id'], vals, context) self.write(cr, uid, matching_filters[0]['id'], vals, context)
return matching_filters[0]['id'] return matching_filters[0]['id']
@ -114,16 +136,17 @@ class ir_filters(osv.osv):
# Partial constraint, complemented by unique index (see below) # Partial constraint, complemented by unique index (see below)
# Still useful to keep because it provides a proper error message when a violation # Still useful to keep because it provides a proper error message when a violation
# occurs, as it shares the same prefix as the unique index. # occurs, as it shares the same prefix as the unique index.
('name_model_uid_unique', 'unique (name, model_id, user_id)', 'Filter names must be unique'), ('name_model_uid_unique', 'unique (name, model_id, user_id, action_id)', 'Filter names must be unique'),
] ]
def _auto_init(self, cr, context=None): def _auto_init(self, cr, context=None):
super(ir_filters, self)._auto_init(cr, context) super(ir_filters, self)._auto_init(cr, context)
# Use unique index to implement unique constraint on the lowercase name (not possible using a constraint) # Use unique index to implement unique constraint on the lowercase name (not possible using a constraint)
cr.execute("SELECT indexname FROM pg_indexes WHERE indexname = 'ir_filters_name_model_uid_unique_index'") cr.execute("DROP INDEX IF EXISTS ir_filters_name_model_uid_unique_index") # drop old index w/o action
cr.execute("SELECT indexname FROM pg_indexes WHERE indexname = 'ir_filters_name_model_uid_unique_action_index'")
if not cr.fetchone(): if not cr.fetchone():
cr.execute("""CREATE UNIQUE INDEX "ir_filters_name_model_uid_unique_index" ON ir_filters cr.execute("""CREATE UNIQUE INDEX "ir_filters_name_model_uid_unique_action_index" ON ir_filters
(lower(name), model_id, COALESCE(user_id,-1))""") (lower(name), model_id, COALESCE(user_id,-1), COALESCE(action_id,-1))""")
_columns = { _columns = {
'name': fields.char('Filter Name', translate=True, required=True), 'name': fields.char('Filter Name', translate=True, required=True),
@ -133,7 +156,11 @@ class ir_filters(osv.osv):
'domain': fields.text('Domain', required=True), 'domain': fields.text('Domain', required=True),
'context': fields.text('Context', required=True), 'context': fields.text('Context', required=True),
'model_id': fields.selection(_list_all_models, 'Model', required=True), 'model_id': fields.selection(_list_all_models, 'Model', required=True),
'is_default': fields.boolean('Default filter') 'is_default': fields.boolean('Default filter'),
'action_id': fields.many2one('ir.actions.actions', 'Action', ondelete='cascade',
help="The menu action this filter applies to. "
"When left empty the filter applies to all menus "
"for this model.")
} }
_defaults = { _defaults = {
'domain': '[]', 'domain': '[]',
@ -141,5 +168,6 @@ class ir_filters(osv.osv):
'user_id': lambda self,cr,uid,context=None: uid, 'user_id': lambda self,cr,uid,context=None: uid,
'is_default': False 'is_default': False
} }
_order = 'model_id, name, id desc'
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -20,6 +20,7 @@
<field name="user_id"/> <field name="user_id"/>
<field name="model_id"/> <field name="model_id"/>
<field name="is_default"/> <field name="is_default"/>
<field name="action_id"/>
</group> </group>
<group> <group>
<field name="domain"/> <field name="domain"/>
@ -37,6 +38,7 @@
<field name="model_id"/> <field name="model_id"/>
<field name="user_id"/> <field name="user_id"/>
<field name="is_default"/> <field name="is_default"/>
<field name="action_id"/>
<field name="domain" groups="base.group_no_one"/> <field name="domain" groups="base.group_no_one"/>
<field name="context" groups="base.group_no_one"/> <field name="context" groups="base.group_no_one"/>
</tree> </tree>

View File

@ -349,6 +349,18 @@ class ir_ui_menu(osv.osv):
menu_domain = [('parent_id', '=', False)] menu_domain = [('parent_id', '=', False)]
return self.search(cr, uid, menu_domain, context=context) return self.search(cr, uid, menu_domain, context=context)
def load_menus_root(self, cr, uid, context=None):
fields = ['name', 'sequence', 'parent_id', 'action']
menu_root_ids = self.get_user_roots(cr, uid, context=context)
menu_roots = self.read(cr, uid, menu_root_ids, fields, context=context) if menu_root_ids else []
return {
'id': False,
'name': 'root',
'parent_id': [-1, ''],
'children': menu_roots,
'all_menu_ids': menu_root_ids,
}
def load_menus(self, cr, uid, context=None): def load_menus(self, cr, uid, context=None):
""" Loads all menu items (all applications and their sub-menus). """ Loads all menu items (all applications and their sub-menus).

View File

@ -986,10 +986,15 @@ class view(osv.osv):
) )
qcontext.update(values) qcontext.update(values)
# TODO: remove this as soon as the following branch is merged # TODO: This helper can be used by any template that wants to embedd the backend.
# lp:~openerp-dev/openerp-web/trunk-module-closure-style-msh # 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 from openerp.addons.web.controllers.main import module_boot
qcontext['modules'] = simplejson.dumps(module_boot()) if request else None return simplejson.dumps(module_boot())
return '[]'
qcontext['get_modules_order'] = get_modules_order
def loader(name): def loader(name):
return self.read_template(cr, uid, name, context=context) return self.read_template(cr, uid, name, context=context)

View File

@ -1943,18 +1943,6 @@
<field name="rate">39.6622695</field> <field name="rate">39.6622695</field>
</record> </record>
<record id="ROL" model="res.currency">
<field name="name">ROL</field>
<field name="symbol">L</field>
<field name="rounding">0.01</field>
<field name="accuracy">4</field>
</record>
<record id="rateROL" model="res.currency.rate">
<field name="currency_id" ref="ROL" />
<field eval="time.strftime('%Y-01-01')" name="name"/>
<field name="rate">45638.59</field>
</record>
<record id="QAR" model="res.currency"> <record id="QAR" model="res.currency">
<field name="name">QAR</field> <field name="name">QAR</field>
<field name="symbol">QR</field> <field name="symbol">QR</field>

View File

@ -5,10 +5,9 @@ from openerp import exceptions
from openerp.tests import common from openerp.tests import common
def noid(d): def noid(d):
""" Removes `id` key from a dict so we don't have to keep these things """ Removes values that are not relevant for the test comparisons """
around when trying to match d.pop('id', None)
""" d.pop('action_id', None)
if 'id' in d: del d['id']
return d return d
class FiltersCase(common.TransactionCase): class FiltersCase(common.TransactionCase):

View File

@ -5,6 +5,7 @@ helpers and classes to write tests.
""" """
import errno import errno
import glob
import json import json
import logging import logging
import os import os
@ -170,7 +171,6 @@ class HttpCase(TransactionCase):
self.session_id = self.session.sid self.session_id = self.session.sid
self.session.db = DB self.session.db = DB
openerp.http.root.session_store.save(self.session) openerp.http.root.session_store.save(self.session)
self.localstorage_path = mkdtemp()
# setup an url opener helper # setup an url opener helper
self.opener = urllib2.OpenerDirector() self.opener = urllib2.OpenerDirector()
self.opener.add_handler(urllib2.UnknownHandler()) self.opener.add_handler(urllib2.UnknownHandler())
@ -181,7 +181,6 @@ class HttpCase(TransactionCase):
self.opener.addheaders.append(('Cookie', 'session_id=%s' % self.session_id)) self.opener.addheaders.append(('Cookie', 'session_id=%s' % self.session_id))
def tearDown(self): def tearDown(self):
rmtree(self.localstorage_path)
self.registry.leave_test_mode() self.registry.leave_test_mode()
super(HttpCase, self).tearDown() super(HttpCase, self).tearDown()
@ -254,6 +253,11 @@ class HttpCase(TransactionCase):
def phantom_run(self, cmd, timeout): def phantom_run(self, cmd, timeout):
_logger.info('phantom_run executing %s', ' '.join(cmd)) _logger.info('phantom_run executing %s', ' '.join(cmd))
ls_glob = os.path.expanduser('~/.qws/share/data/Ofi Labs/PhantomJS/http_localhost_%s.*'%PORT)
for i in glob.glob(ls_glob):
_logger.info('phantomjs unlink localstorage %s', i)
os.unlink(i)
try: try:
phantom = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) phantom = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
except OSError: except OSError:
@ -296,7 +300,6 @@ class HttpCase(TransactionCase):
# phantom.args[1] == options # phantom.args[1] == options
cmd = [ cmd = [
'phantomjs', 'phantomjs',
'--local-storage-path', self.localstorage_path,
jsfile, phantomtest, json.dumps(options) jsfile, phantomtest, json.dumps(options)
] ]
self.phantom_run(cmd, timeout) self.phantom_run(cmd, timeout)

View File

@ -116,6 +116,8 @@ function PhantomTest() {
phantom.exit(1); phantom.exit(1);
} else { } else {
console.log('loaded', url, status); console.log('loaded', url, status);
// clear localstorage leftovers
self.page.evaluate(function () { localStorage.clear() });
// process ready // process ready
waitFor(function() { waitFor(function() {
console.log("PhantomTest.run: wait for condition:", ready); console.log("PhantomTest.run: wait for condition:", ready);