[MERGE] branch merged with lp:~openerp-dev/openobject-addons/trunk-dev-addons1

bzr revid: mso@mso-20100406052452-ufpxq49ut7ovje1y
This commit is contained in:
mso 2010-04-06 10:54:52 +05:30
commit 6f6276df3d
336 changed files with 17406 additions and 10525 deletions

View File

@ -46,7 +46,17 @@ module named account_voucherss
'security/account_security.xml',
'security/ir.model.access.csv',
'account_menuitem.xml',
'wizard/account_invoice_refund_view.xml',
'wizard/account_period_close_view.xml',
'wizard/account_fiscalyear_close_state.xml',
'wizard/account_chart_view.xml',
'wizard/account_move_bank_reconcile_view.xml',
'wizard/account_move_line_reconcile_select_view.xml',
'wizard/account_move_journal_view.xml',
'account_wizard.xml',
'wizard/account_move_line_unreconcile_select_view.xml',
'wizard/account_subscription_generate_view.xml',
'project/wizard/project_account_analytic_line_view.xml',
'account_view.xml',
'account_end_fy.xml',
'account_invoice_view.xml',

View File

@ -153,7 +153,7 @@
<field name="period_id" groups="base.group_user"/>
<group colspan="2" col="1" groups="base.group_user">
<label align="0.0" string="(keep empty to use the current period)"/>
</group>
</group>
</group>
<notebook colspan="4">
<page string="Invoice">

View File

@ -24,3 +24,4 @@
</data>
</openerp>

View File

@ -668,9 +668,9 @@
<field eval="True" name="object"/>
</record>
<wizard id="action_move_journal_line_form" menu="False" model="account.move.line" name="account.move.journal" string="Entries by Line"/>
<!-- <wizard id="action_move_journal_line_form" menu="False" model="account.move.line" name="account.move.journal" string="Entries by Line"/>
<menuitem icon="STOCK_JUSTIFY_FILL" action="action_move_journal_line_form" id="menu_action_move_journal_line_form" parent="account.menu_finance_entries" type="wizard" sequence="5"/>
-->
<!--
Entries lines
-->
@ -1007,9 +1007,9 @@
<menuitem action="action_move_line_search" id="menu_action_move_line_search" parent="account.next_id_29"/>
<menuitem id="menu_finance_charts" name="Charts" parent="account.menu_finance" sequence="4"/>
<wizard id="wizard_account_chart" menu="False" model="account.account" name="account.chart" string="Chart of Accounts"/>
<!-- <wizard id="wizard_account_chart" menu="False" model="account.account" name="account.chart" string="Chart of Accounts"/>
<menuitem icon="STOCK_INDENT" action="wizard_account_chart" id="menu_action_account_tree2" parent="account.menu_finance_charts" type="wizard"/>
-->
<record id="view_bank_statement_reconcile_form" model="ir.ui.view">
@ -1079,8 +1079,14 @@
groups="group_account_user"/>
<menuitem action="action_bank_statement_reconciliation_form" id="menu_action_account_bank_reconcile_tree" parent="next_id_30"/>
<wizard id="action_account_bank_reconcile_tree" menu="False" model="account.move.line" name="account.move.bank.reconcile" string="Bank reconciliation"/>
<menuitem action="action_account_bank_reconcile_tree" id="menu_action_account_bank_reconcile_check_tree" parent="account.next_id_30" type="wizard"/>
<!-- <wizard id="action_account_bank_reconcile_tree" menu="False" model="account.move.line" name="account.move.bank.reconcile" string="Bank reconciliation"/> -->
<!-- <menuitem action="action_account_bank_reconcile_tree" id="menu_action_account_bank_reconcile_check_tree" parent="account.next_id_30" type="wizard"/> -->
<!-- bank reconsilation -->
<menuitem action="action_account_bank_reconcile_tree"
id="menu_action_account_bank_reconcile_check_tree"
parent="account.next_id_30" />
<act_window
domain="[('account_id', '=', active_id)]"

View File

@ -7,7 +7,14 @@
name="account.balance.account.balance.report"
keyword="client_print_multi"
id="wizard_account_balance_compare_report"/>
<wizard id="wizard_invoice_refund" model="account.invoice" name="account.invoice.refund" string="Credit Note" groups="base.group_user"/>
<!-- <wizard id="wizard_invoice_refund" model="account.invoice" name="account.invoice.refund" string="Credit Note" groups="base.group_user"/> -->
<!-- <wizard id="wizard_invoice_refund" model="account.invoice" name="account.invoice.refund" string="Credit Note" groups="base.group_user"/>-->
<!-- for test only -->
<wizard id="wizard_invoice_pay" model="account.invoice" name="account.invoice.pay" string="Pay invoice" groups="base.group_user"/>
@ -21,8 +28,9 @@
type="wizard"
sequence="1"/>
<wizard id="wizard_fiscalyear_close_state" menu="False" model="account.fiscalyear" name="account.fiscalyear.close.state" string="Close a Fiscal Year"/>
<!-- <wizard id="wizard_fiscalyear_close_state" menu="False" model="account.fiscalyear" name="account.fiscalyear.close.state" string="Close a Fiscal Year"/>
<menuitem action="wizard_fiscalyear_close_state" id="menu_wizard_fy_close_state" parent="menu_account_end_year_treatments" type="wizard"/>
-->
<wizard
id="wizard_open_closed_fiscalyear"
@ -35,12 +43,19 @@
id="menu_wizard_open_closed_fy"
sequence="2"
parent="account.menu_account_end_year_treatments" type="wizard"/>
<!-- period close
<wizard id="wizard_period_close" model="account.period" name="account.period.close" string="Close a Period"/>
<wizard id="wizard_period_close" model="account.period" name="account.period.close" string="Close a Period"/>
<wizard id="action_account_period_close" model="account.period" name="account.period.close" string="Close a Period"/>
-->
<!-- automatic reconcile -->
<wizard id="wizard_automatic_reconcile" menu="False" model="account.account" name="account.automatic.reconcile" string="Automatic reconciliation"/>
<menuitem id="next_id_20" name="Reconciliation" parent="menu_finance_periodical_processing"/><menuitem action="wizard_automatic_reconcile" id="menu_automatic_reconcile" parent="next_id_20" type="wizard"/>
<menuitem id="next_id_20" name="Reconciliation" parent="menu_finance_periodical_processing"/>
<menuitem action="wizard_automatic_reconcile" id="menu_automatic_reconcile" parent="next_id_20" type="wizard"/>
<!-- Import entry in statement -->
@ -57,19 +72,24 @@
<wizard id="wizard_reconcile_unreconcile" model="account.move.reconcile" name="account.reconcile.unreconcile" string="Unreconcile Entries"/>
<wizard id="wizard_reconcile_select" menu="False" model="account.move.line" name="account.move.line.reconcile.select" string="Reconcile entries"/>
<!-- <wizard id="wizard_reconcile_select" menu="False" model="account.move.line" name="account.move.line.reconcile.select" string="Reconcile entries"/>
<menuitem action="wizard_reconcile_select" id="menu_reconcile_select" parent="account.next_id_20" type="wizard"/>
-->
<!-- unreconcile -->
<wizard id="wizard_unreconcile" model="account.move.line" name="account.move.line.unreconcile" string="Unreconcile Entries"/>
<!-- unreconcile
<wizard id="wizard_unreconcile_select" menu="False" model="account.move.line" name="account.move.line.unreconcile.select" string="Unreconcile entries"/>
<menuitem action="wizard_unreconcile_select" id="menu_unreconcile_select" parent="account.next_id_20" type="wizard"/>
-->
<!-- subscriptions -->
<!--
<wizard id="wizard_generate_subscription" menu="False" model="account.subscription" name="account.subscription.generate" string="Create subscription entries"/>
<menuitem action="wizard_generate_subscription" id="menu_generate_subscription" parent="account.menu_finance_periodical_processing" type="wizard"/>
-->
<!-- Aged partner balance -->
<wizard id="wizard_aged_trial_balance" menu="False" model="res.partner" name="account.aged.trial.balance" string="Aged Partner Balance"/>
<menuitem id="next_id_22" name="Partner Accounts" parent="menu_finance_generic_reporting" sequence="1"/>

View File

@ -204,10 +204,14 @@
<field name="view_type">form</field>
<field name="view_id" ref="view_account_analytic_line_tree"/>
</record>
<wizard id="action_account_analytic_line" menu="False" model="account.analytic.line" name="account.analytic.line" string="Entries by Line"/>
<wizard id="action_account_analytic_line" menu="False" model="account.analytic.line" name="account.analytic.line" string="Entries by Line"/>
<menuitem id="next_id_41" name="Analytic Entries" parent="account.menu_finance_entries"/>
<menuitem type="wizard" icon="STOCK_JUSTIFY_FILL" action="action_account_analytic_line" id="account_entries_analytic_entries" parent="next_id_41"/>
<!-- Entries by Line -->
<menuitem icon="STOCK_JUSTIFY_FILL"
action="action_project_account_analytic_line_form"
id="account_entries_analytic_entries"
parent="next_id_41"/>
<record id="action_account_tree1" model="ir.actions.act_window">
<field name="name">Analytic Entries</field>

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
##############################################################################
#
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
#
@ -15,7 +15,7 @@
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
@ -25,7 +25,7 @@ import wizard_account_analytic_inverted_balance_report
import wizard_account_analytic_cost_ledger_report
import wizard_account_analytic_cost_ledger_for_journal_report
import wizard_account_analytic_analytic_check
import wizard_account_analytic_line
import project_account_analytic_line
import wizard_analytic_account_chart
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -0,0 +1,58 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from osv import fields, osv
from tools.translate import _
class project_account_analytic_line(osv.osv_memory):
_name = "project.account.analytic.line"
_description = "Analytic Entries by line"
_columns = {
'from_date': fields.date('From'),
'to_date': fields.date('To'),
}
def action_open_window(self, cr, uid, ids, context={}):
mod_obj =self.pool.get('ir.model.data')
domain = []
for data in self.read(cr, uid, ids, context=context):
from_date = data['from_date']
to_date = data['to_date']
if from_date and to_date:
domain = [('date','>=',from_date), ('date','<=',to_date)]
elif from_date:
domain = [('date','>=',from_date)]
elif to_date:
domain = [('date','<=',to_date)]
result = mod_obj._get_id(cr, uid, 'account', 'view_account_analytic_line_filter')
id = mod_obj.read(cr, uid, result, ['res_id'], context=context)
return {
'name': _('Analytic Entries by line'),
'view_type': 'form',
"view_mode": 'tree,form',
'res_model': 'account.analytic.line',
'type': 'ir.actions.act_window',
'domain': domain,
'search_view_id': id['res_id'],
}
project_account_analytic_line()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -0,0 +1,36 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<record id="view_project_account_analytic_line_form" model="ir.ui.view">
<field name="name">project.account.analytic.line.form</field>
<field name="model">project.account.analytic.line</field>
<field name="type">form</field>
<field name="arch" type="xml">
<form string="View Account Analytic Lines">
<group colspan="4" >
<field name="from_date"/>
<newline/>
<field name="to_date" />
</group>
<label string ="(Keep empty to open the current situation)" />
<separator string="" colspan="4" />
<group colspan="4" col="6">
<button icon="gtk-cancel" special="cancel" string="Cancel"/>
<button icon="gtk-execute" string="Open Entries" name="action_open_window" type="object"/>
</group>
</form>
</field>
</record>
<record id="action_project_account_analytic_line_form" model="ir.actions.act_window">
<field name="name">Entries By Line</field>
<field name="res_model">project.account.analytic.line</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
<field name="view_id" ref="view_project_account_analytic_line_form"/>
<field name="target">new</field>
</record>
</data>
</openerp>

View File

@ -1,84 +0,0 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
import wizard
import pooler
import time
from tools.translate import _
def _action_open_window(self, cr, uid, data, context):
domain = []
from_date = data['form']['from_date']
to_date = data['form']['to_date']
if from_date and to_date:
domain = [('date','>=',from_date),('date','<=',to_date)]
elif from_date:
domain = [('date','>=',from_date)]
elif to_date:
domain = [('date','<=',to_date)]
mod_obj = pooler.get_pool(cr.dbname).get('ir.model.data')
result = mod_obj._get_id(cr, uid, 'account', 'view_account_analytic_line_filter')
id = mod_obj.read(cr, uid, result, ['res_id'])
return {
'name': _('Analytic Entries by line'),
'view_type': 'form',
"view_mode": 'tree,form',
'res_model': 'account.analytic.line',
'type': 'ir.actions.act_window',
'domain': domain,
'search_view_id': id['res_id'],}
class account_analytic_line(wizard.interface):
form1 = '''<?xml version="1.0"?>
<form string="View Account Analytic Lines">
<separator string="Account Analytic Lines Analysis" colspan="4"/>
<field name="from_date"/>
<newline/>
<field name="to_date"/>
<newline/>
<label string=""/>
<label string="(Keep empty to open the current situation)" align="0.0" colspan="3"/>
</form>'''
form1_fields = {
'from_date': {
'string': 'From',
'type': 'date',
},
'to_date': {
'string': 'To',
'type': 'date',
},
}
states = {
'init': {
'actions': [],
'result': {'type': 'form', 'arch':form1, 'fields':form1_fields, 'state': [('end', 'Cancel','gtk-cancel'),('open', 'Open Entries','gtk-ok')]}
},
'open': {
'actions': [],
'result': {'type': 'action', 'action': _action_open_window, 'state':'end'}
}
}
account_analytic_line('account.analytic.line')
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
##############################################################################
#
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
#
@ -15,40 +15,36 @@
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
import wizard_automatic_reconcile
import wizard_reconcile_select
import wizard_unreconcile_select
import account_move_line_reconcile_select
import account_move_line_unreconcile_select
import wizard_reconcile
import wizard_unreconcile
import wizard_refund
import account_invoice_refund
import wizard_pay_invoice
import wizard_journal
import account_move_journal
import wizard_journal_select
import wizard_bank_reconcile
import wizard_subscription_generate
import account_move_bank_reconcile
import account_subscription_generate
import wizard_aged_trial_balance
import wizard_general_ledger_report
import wizard_third_party_ledger
import wizard_account_balance_report
import wizard_partner_balance_report
import wizard_period_close
import account_period_close
import wizard_fiscalyear_close
import wizard_fiscalyear_close_state
import account_fiscalyear_close_state
import wizard_open_closed_fiscalyear
import wizard_vat
import wizard_compare_account_balance_report
import wizard_invoice_state
import wizard_account_duplicate
import wizard_account_chart
import account_chart
import wizard_move_line_select
import wizard_validate_account_move
@ -64,5 +60,4 @@ import wizard_change_currency
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -0,0 +1,74 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from osv import fields, osv
from tools.translate import _
import tools
class account_chart(osv.osv_memory):
"""
For Chart of Accounrs
"""
_name = "account.chart"
_description = "chart"
_columns = {
'fiscalyear': fields.many2one('account.fiscalyear', \
'Fiscal year', \
help = 'Keep empty for all open fiscal years'),
'target_move': fields.selection([
('all', 'All Entries'),
('posted', 'All Posted Entries'),
], 'Target Moves', required = True),
}
def _get_defaults(self, cr, uid, context={}):
"""Return default Fiscalyear value"""
fiscalyear_obj = self.pool.get('account.fiscalyear')
fiscalyear = fiscalyear_obj.find(cr, uid)
return fiscalyear
def account_chart_open_window(self, cr, uid, ids, context={}):
"""
Opens chart of Accounts
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param ids: List of account charts IDs
@return: dictionary of Open account chart window on given fiscalyear and all Entries or posted entries
"""
mod_obj = self.pool.get('ir.model.data')
act_obj = self.pool.get('ir.actions.act_window')
for data in self.read(cr, uid, ids,context=context):
result = mod_obj._get_id(cr, uid, 'account', 'action_account_tree')
id = mod_obj.read(cr, uid, [result], ['res_id'],context=context)[0]['res_id']
result = act_obj.read(cr, uid, [id], context=context)[0]
result['context'] = str({'fiscalyear': data['fiscalyear'], \
'state': data['target_move']})
if data['fiscalyear']:
result['name'] += ':' + self.pool.get('account.fiscalyear').read(cr, uid, [data['fiscalyear']],context=context)[0]['code']
return result
_defaults = {
'fiscalyear': _get_defaults,
'target_move': lambda * a: 'all'
}
account_chart()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -0,0 +1,40 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<record id="view_account_chart" model="ir.ui.view">
<field name="name">account.chart.form</field>
<field name="model">account.chart</field>
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Account charts">
<group colspan="4" >
<field name="fiscalyear"/>
<label align="0.7" colspan="6" string="(If you do not select Fiscal year it will take all open fiscal years)"/>
<field name="target_move"/>
</group>
<separator string="" colspan="4" />
<group colspan="4" col="6">
<label string ="" colspan="2"/>
<button icon="gtk-cancel" special="cancel" string="Cancel" />
<button icon="gtk-open" string="Open Charts"
name="account_chart_open_window" type="object" />
</group>
</form>
</field>
</record>
<record id="action_account_chart" model="ir.actions.act_window">
<field name="name">Chart of Accounts</field>
<field name="res_model">account.chart</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
<field name="view_id" ref="view_account_chart"/>
<field name="target">new</field>
</record>
<menuitem icon="STOCK_INDENT" action="action_account_chart"
id="menu_action_account_tree2"
parent="account.menu_finance_charts" />
</data>
</openerp>

View File

@ -0,0 +1,65 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from osv import fields, osv
from tools.translate import _
import tools
class account_fiscalyear_close_state(osv.osv_memory):
"""
Closes Account Fiscalyear
"""
_name = "account.fiscalyear.close.state"
_description = "Fiscalyear Close state"
_columns = {
'fy_id': fields.many2one('account.fiscalyear', \
'Fiscal Year to close', required=True),
'sure': fields.boolean('Check this box', required=False)
}
def data_save(self, cr, uid, ids, context={}):
"""
This function close account fiscalyear
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param ids: List of Account fiscalyear close states IDs
"""
for data in self.read(cr, uid, ids,context=context):
if not data['sure']:
raise osv.except_osv(_('UserError'), _('Closing of states \
cancelled, please check the box !'))
fy_id = data['fy_id']
cr.execute('UPDATE account_journal_period ' \
'SET state = %s ' \
'WHERE period_id IN (SELECT id FROM account_period \
WHERE fiscalyear_id = %s)',
('done', fy_id))
cr.execute('UPDATE account_period SET state = %s ' \
'WHERE fiscalyear_id = %s', ('done', fy_id))
cr.execute('UPDATE account_fiscalyear ' \
'SET state = %s WHERE id = %s', ('done', fy_id))
return {}
account_fiscalyear_close_state()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -0,0 +1,39 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<record id="view_account_fiscalyear_close_state" model="ir.ui.view">
<field name="name">account.fiscalyear.close.state.form</field>
<field name="model">account.fiscalyear.close.state</field>
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Close states of Fiscal year and periods">
<group colspan="4" >
<field name="fy_id" domain = "[('state','=','draft')]"/>
<separator string="Are you sure you want to close the fiscal year ?" colspan="4"/>
<field name="sure"/>
</group>
<separator string="" colspan="4" />
<group colspan="4" col="6">
<label string ="" colspan="2"/>
<button icon="gtk-cancel" special="cancel" string="Cancel"/>
<button icon="gtk-close" string="Close states" name="data_save" type="object"/>
</group>
</form>
</field>
</record>
<record id="action_account_fiscalyear_close_state" model="ir.actions.act_window">
<field name="name">Close a Fiscal Year</field>
<field name="res_model">account.fiscalyear.close.state</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
<field name="view_id" ref="view_account_fiscalyear_close_state"/>
<field name="target">new</field>
</record>
<menuitem action="action_account_fiscalyear_close_state"
id="menu_wizard_fy_close_state"
parent="menu_account_end_year_treatments" />
</data>
</openerp>

View File

@ -0,0 +1,191 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from osv import fields, osv
from tools.translate import _
import tools
import netsvc
class account_invoice_refund(osv.osv_memory):
"""Refunds invoice."""
_name = "account.invoice.refund"
_description = "Invoice Refund"
_columns = {
'date': fields.date('Operation date', required=False),
'period': fields.many2one('account.period', 'Force period', required=False),
'description': fields.char('Description', size=150, required=True),
}
def compute_refund(self, cr, uid, ids, mode, context={}):
"""
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param ids: the account invoice refunds ID or list of IDs
"""
inv_obj = self.pool.get('account.invoice')
reconcile_obj = self.pool.get('account.move.reconcile')
account_m_line_obj = self.pool.get('account.move.line')
mod_obj = self.pool.get('ir.model.data')
act_obj = self.pool.get('ir.actions.act_window')
for form in self.read(cr, uid, ids,context=context):
created_inv = []
date = False
period = False
description = False
for inv in inv_obj.browse(cr, uid, context['active_ids'],context=context):
if inv.state in ['draft', 'proforma2', 'cancel']:
raise osv.except_osv(_('Error !'), _('Can not %s draft/proforma/cancel invoice.') % (mode))
if form['period'] :
period = form['period']
else:
period = inv.period_id and inv.period_id.id or False
if form['date'] :
date = form['date']
if not form['period'] :
cr.execute("select name from ir_model_fields \
where model = 'account.period' \
and name = 'company_id'")
result_query = cr.fetchone()
if result_query:
cr.execute("""SELECT id
from account_period where date('%s')
between date_start AND date_stop \
and company_id = %s limit 1 """,
(form['date'], self.pool.get('res.users').browse(cr, uid, uid,context=context).company_id.id,))
else:
cr.execute("""SELECT id
from account_period where date('%s')
between date_start AND date_stop \
limit 1 """, (form['date'],))
res = cr.fetchone()
if res:
period = res[0]
else:
date = inv.date_invoice
if form['description'] :
description = form['description']
else:
description = inv.name
if not period:
raise osv.except_osv(_('Data Insufficient !'), \
_('No Period found on Invoice!'))
refund_id = inv_obj.refund(cr, uid, [inv.id], date, period, description)
refund = inv_obj.browse(cr, uid, refund_id[0],context=context)
inv_obj.write(cr, uid, [refund.id], {'date_due': date,
'check_total': inv.check_total})
inv_obj.button_compute(cr, uid, refund_id)
created_inv.append(refund_id[0])
if mode in ('cancel', 'modify'):
movelines = inv.move_id.line_id
to_reconcile_ids = {}
for line in movelines :
if line.account_id.id == inv.account_id.id :
to_reconcile_ids[line.account_id.id] = [line.id]
if type(line.reconcile_id) != osv.orm.browse_null :
reconcile_obj.unlink(cr, uid, line.reconcile_id.id)
wf_service = netsvc.LocalService('workflow')
wf_service.trg_validate(uid, 'account.invoice', \
refund.id, 'invoice_open', cr)
refund = inv_obj.browse(cr, uid, refund_id[0],context=context)
for tmpline in refund.move_id.line_id :
if tmpline.account_id.id == inv.account_id.id :
to_reconcile_ids[tmpline.account_id.id].append(tmpline.id)
for account in to_reconcile_ids :
account_m_line_obj.reconcile(cr, uid, to_reconcile_ids[account],
writeoff_period_id=period,
writeoff_journal_id=inv.journal_id.id,
writeoff_acc_id=inv.account_id.id
)
if mode == 'modify' :
invoice = inv_obj.read(cr, uid, [inv.id],
['name', 'type', 'number', 'reference',
'comment', 'date_due', 'partner_id',
'address_contact_id', 'address_invoice_id',
'partner_insite', 'partner_contact',
'partner_ref', 'payment_term', 'account_id',
'currency_id', 'invoice_line', 'tax_line',
'journal_id', 'period_id'],context=context)
invoice = invoice[0]
del invoice['id']
invoice_lines = self.pool.get('account.invoice.line').read(cr, uid, invoice['invoice_line'],context=context)
invoice_lines = inv_obj._refund_cleanup_lines(cr, uid, invoice_lines)
tax_lines = self.pool.get('account.invoice.tax').read(
cr, uid, invoice['tax_line'],context=context)
tax_lines = inv_obj._refund_cleanup_lines(cr, uid, tax_lines)
invoice.update({
'type': inv.type,
'date_invoice': date,
'state': 'draft',
'number': False,
'invoice_line': invoice_lines,
'tax_line': tax_lines,
'period_id': period,
'name': description
})
for field in ('address_contact_id', 'address_invoice_id', 'partner_id',
'account_id', 'currency_id', 'payment_term', 'journal_id'):
invoice[field] = invoice[field] and invoice[field][0]
inv_id = inv_obj.create(cr, uid, invoice, {})
if inv.payment_term.id:
data = inv_obj.onchange_payment_term_date_invoice(cr, uid, [inv_id], inv.payment_term.id, date)
if 'value' in data and data['value']:
inv_obj.write(cr, uid, [inv_id], data['value'])
created_inv.append(inv_id)
if inv.type == 'out_invoice':
xml_id = 'action_invoice_tree1'
elif inv.type == 'in_invoice':
xml_id = 'action_invoice_tree2'
elif type == 'out_refund':
xml_id = 'action_invoice_tree3'
else:
xml_id = 'action_invoice_tree4'
result = mod_obj._get_id(cr, uid, 'account', xml_id)
id = mod_obj.read(cr, uid, result, ['res_id'],context=context)['res_id']
result = act_obj.read(cr, uid, id,context=context)
result['res_id'] = created_inv
return result
def invoice_refund(self, cr, uid, ids, context={}):
return self.compute_refund(cr, uid, ids, 'refund', context=context)
def invoice_cancel(self, cr, uid, ids, context={}):
return self.compute_refund(cr, uid, ids, 'cancel', context=context)
def invoice_modify(self, cr, uid, ids, context={}):
return self.compute_refund(cr, uid, ids, 'modify', context=context)
account_invoice_refund()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<record id="view_account_invoice_refund" model="ir.ui.view">
<field name="name">account.invoice.refund.form</field>
<field name="model">account.invoice.refund</field>
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Credit Note">
<group colspan="4" >
<label string="Are you sure you want to refund this invoice ?" colspan="2"/>
<newline/>
<field name="date"/>
<field name="period"/>
<field name="description"/>
</group>
<separator string="" colspan="4" />
<group colspan="4" col="6">
<label string ="" colspan="2"/>
<button icon="gtk-cancel" special="cancel" string="Cancel"/>
<button icon="gtk-redo" string="Refund Invoice" name="invoice_refund" type="object"/>
<button icon="gtk-ok" string="Cancel Invoice" name="invoice_cancel" type="object"/>
<button icon="gtk-undo" string="Modify Invoice" name="invoice_modify" type="object"/>
</group>
</form>
</field>
</record>
<record id="action_account_invoice_refund" model="ir.actions.act_window">
<field name="name">Credit Note</field>
<field name="res_model">account.invoice.refund</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
<field name="view_id" ref="view_account_invoice_refund"/>
<field name="target">new</field>
</record>
<act_window id="action_account_invoice_refund"
key2 = "client_action_multi" name="Credit Note"
res_model="account.invoice.refund" src_model="account.invoice"
view_mode="form" target="new" view_type="form" />
</data>
</openerp>

View File

@ -0,0 +1,63 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
import tools
from osv import fields, osv
from tools.translate import _
class account_move_bank_reconcile(osv.osv_memory):
"""
Bank Reconciliation
"""
_name = "account.move.bank.reconcile"
_description = "Move bank reconcile"
_columns = {
'journal_id': fields.many2one('account.journal', 'Journal', required=True),
}
def action_open_window(self, cr, uid, ids, context={}):
"""
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param ids: account move bank reconciles ID or list of IDs
@return: dictionary of Open account move line on given journal_id.
"""
for data in self.read(cr, uid, ids,context=context):
cr.execute('select default_credit_account_id \
from account_journal where id=%s', (data['journal_id'],))
account_id = cr.fetchone()[0]
if not account_id:
raise osv.except_osv(_('Error'), _('You have to define \
the bank account\nin the journal definition for reconciliation.'))
return {
'domain': "[('journal_id','=',%d), ('account_id','=',%d), ('state','<>','draft')]" % (data['journal_id'], account_id),
'name': _('Standard Encoding'),
'view_type': 'form',
'view_mode': 'tree,form',
'res_model': 'account.move.line',
'view_id': False,
'context': "{'journal_id': %d}" % (data['journal_id'],),
'type': 'ir.actions.act_window'
}
account_move_bank_reconcile()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<record id="view_account_move_bank_reconcile" model="ir.ui.view">
<field name="name">account.move.bank.reconcile.form</field>
<field name="model">account.move.bank.reconcile</field>
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Bank reconciliation">
<group colspan="4" >
<field name="journal_id"/>
</group>
<separator string="" colspan="4" />
<group colspan="4" col="6">
<label string ="" colspan="2"/>
<button icon="gtk-cancel" special="cancel" string="Cancel"/>
<button icon="gtk-open" string="Open for bank reconciliation" name="action_open_window" type="object"/>
</group>
</form>
</field>
</record>
<record id="action_account_bank_reconcile_tree" model="ir.actions.act_window">
<field name="name">Bank reconciliation</field>
<field name="res_model">account.move.bank.reconcile</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
<field name="view_id" ref="view_account_move_bank_reconcile"/>
<field name="target">new</field>
</record>
</data>
</openerp>

View File

@ -0,0 +1,98 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from osv import fields, osv
from tools.translate import _
import tools
class account_move_journal(osv.osv_memory):
_name = "account.move.journal"
_description = "Move journal"
_columns = {
'journal_id': fields.many2one('account.journal', 'Journal', required=True),
'period_id': fields.many2one('account.period', 'Period', required=True),
}
def _get_period(self, cr, uid, context={}):
"""Return default account period value"""
ids = self.pool.get('account.period').find(cr, uid, context=context)
period_id = False
if len(ids):
period_id = ids[0]
return period_id
def action_open_window(self, cr, uid, ids, context={}):
"""
This function Open action move line window on given period and Journal/Payment Mode
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param ids: account move journals ID or list of IDs
@return: dictionary of Open action move line window on given period and Journal/Payment Mode
"""
jp = self.pool.get('account.journal.period')
mod_obj = self.pool.get('ir.model.data')
for data in self.read(cr, uid, ids, ['journal_id', 'period_id'],context=context):
cr.execute('select id,name from ir_ui_view where model=%s and type=%s', ('account.move.line', 'form'))
view_res = cr.fetchone()
journal_id = data['journal_id']
period_id = data['period_id']
ids = jp.search(cr, uid, [('journal_id', '=', journal_id), \
('period_id', '=', period_id)],context=context)
if not len(ids):
name = self.pool.get('account.journal').read(cr, uid, [journal_id])[0]['name']
state = self.pool.get('account.period').read(cr, uid, [period_id])[0]['state']
if state == 'done':
raise osv.except_osv(_('UserError'), _('This period is already closed !'))
company = self.pool.get('account.period').read(cr, uid, [period_id])[0]['company_id'][0]
jp.create(cr, uid, {'name': name, 'period_id': period_id, 'journal_id': journal_id, 'company_id': company},context=context)
ids = jp.search(cr, uid, [('journal_id', '=', journal_id), ('period_id', '=', period_id)],context=context)
jp = jp.browse(cr, uid, ids, context=context)[0]
name = (jp.journal_id.code or '') + ':' + (jp.period_id.code or '')
result = mod_obj._get_id(cr, uid, 'account', 'view_account_move_line_filter')
res = mod_obj.read(cr, uid, result, ['res_id'],context=context)
return {
'domain': "[('journal_id','=',%d), ('period_id','=',%d)]" % (journal_id, period_id),
'name': name,
'view_type': 'form',
'view_mode': 'tree,form',
'res_model': 'account.move.line',
'view_id': view_res,
'context': "{'journal_id': %d, 'period_id': %d}" % (journal_id, period_id),
'type': 'ir.actions.act_window',
'search_view_id': res['res_id']
}
_defaults = {
'period_id': _get_period
}
account_move_journal()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -0,0 +1,41 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<record id="view_account_move_journal_form" model="ir.ui.view">
<field name="name">account.move.journal.form</field>
<field name="model">account.move.journal</field>
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Standard entries">
<group colspan="4" >
<field name="journal_id"/>
<newline/>
<field name="period_id" />
</group>
<separator string="" colspan="4" />
<group colspan="4" col="6">
<label string ="" colspan="2"/>
<button icon="gtk-cancel" special="cancel" string="Cancel"/>
<button icon="gtk-open" string="Open Journal" name="action_open_window" type="object"/>
</group>
</form>
</field>
</record>
<record id="action_account_move_journal_line_form" model="ir.actions.act_window">
<field name="name">Entries by Line</field>
<field name="res_model">account.move.journal</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
<field name="view_id" ref="view_account_move_journal_form"/>
<field name="target">new</field>
</record>
<menuitem icon="STOCK_JUSTIFY_FILL"
action="action_account_move_journal_line_form"
id="menu_action_move_journal_line_form"
parent="account.menu_finance_entries" sequence="5" />
</data>
</openerp>

View File

@ -0,0 +1,58 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from osv import fields, osv
from tools.translate import _
import tools
class account_move_line_reconcile_select(osv.osv_memory):
_name = "account.move.line.reconcile.select"
_description = "Move line reconcile select"
_columns = {
'account_id': fields.many2one('account.account', 'Account', \
domain = [('reconcile', '=', 1)], required=True),
}
def action_open_window(self, cr, uid, ids, context={}):
"""
This function Open account move line window for reconcile on given account id
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param ids: account move line reconcile selects ID or list of IDs
@return: dictionary of Open account move line window for reconcile on given account id
"""
for data in self.read(cr, uid, ids,context=context):
return {
'domain': "[('account_id','=',%d),('reconcile_id','=',False),('state','<>','draft')]" % data['account_id'],
'name': _('Reconciliation'),
'view_type': 'form',
'view_mode': 'tree,form',
'view_id': False,
'res_model': 'account.move.line',
'type': 'ir.actions.act_window'
}
account_move_line_reconcile_select()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -0,0 +1,41 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<record id="view_account_move_line_reconcile_select" model="ir.ui.view">
<field name="name">account.move.line.reconcile.select.form</field>
<field name="model">account.move.line.reconcile.select</field>
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Reconciliation">
<group colspan="4" >
<field name="account_id"/>
</group>
<separator string="" colspan="4" />
<group colspan="4" col="6">
<label string ="" colspan="2"/>
<button icon="gtk-cancel" special="cancel" string="Cancel" />
<button icon="gtk-open"
string="Open for reconciliation" name="action_open_window"
type="object" />
</group>
</form>
</field>
</record>
<record id="action_account_reconcile_select" model="ir.actions.act_window">
<field name="name">Reconcile entries</field>
<field name="res_model">account.move.line.reconcile.select</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
<field name="view_id" ref="view_account_move_line_reconcile_select"/>
<field name="target">new</field>
</record>
<menuitem id="next_id_20" name="Reconciliation"
parent="menu_finance_periodical_processing" />
<menuitem action="action_account_reconcile_select"
id="menu_reconcile_select" parent="account.next_id_20" />
</data>
</openerp>

View File

@ -0,0 +1,52 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from osv import fields, osv
from tools.translate import _
import tools
class account_move_line_unreconcile_select(osv.osv_memory):
_name = "account.move.line.unreconcile.select"
_description = "Unreconciliation"
_columns ={
'account_id': fields.many2one('account.account','Account',required=True),
}
def action_open_window(self, cr, uid, ids, context={}):
for data in self.read(cr, uid, ids):
return {
'domain': "[('account_id','=',%d),('reconcile_id','<>',False),('state','<>','draft')]" % data['account_id'],
'name': 'Unreconciliation',
'view_type': 'form',
'view_mode': 'tree,form',
'view_id': False,
'res_model': 'account.move.line',
'type': 'ir.actions.act_window'
}
account_move_line_unreconcile_select()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -0,0 +1,38 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<record id="view_account_move_line_unreconcile_select" model="ir.ui.view">
<field name="name">account.move.line.unreconcile.select.form</field>
<field name="model">account.move.line.unreconcile.select</field>
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Unreconciliation">
<group colspan="4" >
<field name="account_id"/>
</group>
<separator string="" colspan="4" />
<group colspan="4" col="6">
<label string ="" colspan="2"/>
<button icon="gtk-cancel" special="cancel" string="Cancel" />
<button icon="gtk-open"
string="Open for Unreconciliation" name="action_open_window"
type="object" />
</group>
</form>
</field>
</record>
<record id="action_account_unreconcile_select" model="ir.actions.act_window">
<field name="name">Unreconcile entries</field>
<field name="res_model">account.move.line.unreconcile.select</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
<field name="view_id" ref="view_account_move_line_unreconcile_select"/>
<field name="target">new</field>
</record>
<menuitem action="action_account_unreconcile_select"
id="menu_unreconcile_select" parent="account.next_id_20" />
</data>
</openerp>

View File

@ -0,0 +1,54 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from osv import fields, osv
from tools.translate import _
import tools
class account_period_close(osv.osv_memory):
"""
close period
"""
_name = "account.period.close"
_description = "period close"
_columns = {
'sure': fields.boolean('Check this box', required=False),
}
def data_save(self, cr, uid, ids, context={}):
"""
This function close period
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param ids: account period closes ID or list of IDs
"""
mode = 'done'
for form in self.read(cr, uid, ids,context=context):
if form['sure']:
for id in context['active_ids']:
cr.execute('update account_journal_period set state=%s where period_id=%s', (mode, id))
cr.execute('update account_period set state=%s where id=%s', (mode, id))
return {}
account_period_close()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<record id="view_account_period_close" model="ir.ui.view">
<field name="name">account.period.close.form</field>
<field name="model">account.period.close</field>
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Close Period">
<group colspan="4" >
<separator string="Are you sure ?" colspan="4"/>
<field name="sure"/>
</group>
<separator string="" colspan="4" />
<group colspan="4" col="6">
<label string ="" colspan="2"/>
<button icon="gtk-cancel" special="cancel" string="Cancel"/>
<button icon="gtk-close" string="Close Period" name="data_save" type="object"/>
</group>
</form>
</field>
</record>
<record id="action_account_period_close" model="ir.actions.act_window">
<field name="name">Close a Period</field>
<field name="res_model">account.period.close</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
<field name="view_id" ref="view_account_period_close"/>
<field name="target">new</field>
</record>
<record id="action_idea_post_vote_values" model="ir.values">
<field name="model_id" ref="model_account_period" />
<field name="object" eval="1" />
<field name="name"></field>
<field name="key2">client_action_multi</field>
<field name="value" eval="'ir.actions.act_window,' + str(ref('action_account_period_close'))"/>
<field name="key">action</field>
<field name="model">account.period</field>
</record>
</data>
</openerp>

View File

@ -0,0 +1,43 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
import time
from osv import fields, osv
class account_subscription_generate(osv.osv_memory):
_name = "account.subscription.generate"
_description = "Subscription Compute"
_columns = {
'date': fields.date('Date', required=True),
}
_defaults = {
'date': lambda *a: time.strftime('%Y-%m-%d'),
}
def action_generate(self, cr, uid, ids, context={}):
for data in self.read(cr, uid, ids, context=context):
cr.execute('select id from account_subscription_line where date<%s and move_id is null', (data['date'],))
ids = map(lambda x: x[0], cr.fetchall())
self.pool.get('account.subscription.line').move_create(cr, uid, ids, context=context)
return {}
account_subscription_generate()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -0,0 +1,38 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<record id="view_account_subscription_generate" model="ir.ui.view">
<field name="name">account.subscription.generate.form</field>
<field name="model">account.subscription.generate</field>
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Subscription Compute">
<group colspan="4" >
<separator string="Generate entries before:" colspan="4"/>
<field name="date"/>
</group>
<separator string="" colspan="4" />
<group colspan="4" col="6">
<label string ="" colspan="2"/>
<button icon="gtk-cancel" special="cancel" string="Cancel" />
<button icon="gtk-execute" string="Compute Entry Dates"
name="action_generate" type="object" />
</group>
</form>
</field>
</record>
<record id="action_account_subscription_generate" model="ir.actions.act_window">
<field name="name">Create Subscription Entries</field>
<field name="res_model">account.subscription.generate</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
<field name="view_id" ref="view_account_subscription_generate"/>
<field name="target">new</field>
</record>
<menuitem action="action_account_subscription_generate" id="menu_generate_subscription" parent="account.menu_finance_periodical_processing" />
</data>
</openerp>

View File

@ -1,80 +0,0 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
import wizard
import pooler
class wizard_account_chart(wizard.interface):
_account_chart_arch = '''<?xml version="1.0"?>
<form string="Open account charts">
<field name="fiscalyear"/>
<label align="0.7" colspan="6" string="(If you do not select Fiscal year it will take all open fiscal years)"/>
<field name="target_move"/>
</form>'''
_account_chart_fields = {
'fiscalyear': {
'string': 'Fiscal year',
'type': 'many2one',
'relation': 'account.fiscalyear',
'help': 'Keep empty for all open fiscal years',
},
'target_move': {
'string': 'Target Moves',
'type': 'selection',
'selection': [('all','All Entries'),('posted','All Posted Entries')],
'required': True,
'default': lambda *a:"all",
},
}
def _get_defaults(self, cr, uid, data, context):
fiscalyear_obj = pooler.get_pool(cr.dbname).get('account.fiscalyear')
data['form']['fiscalyear'] = fiscalyear_obj.find(cr, uid)
return data['form']
def _account_chart_open_window(self, cr, uid, data, context):
mod_obj = pooler.get_pool(cr.dbname).get('ir.model.data')
act_obj = pooler.get_pool(cr.dbname).get('ir.actions.act_window')
result = mod_obj._get_id(cr, uid, 'account', 'action_account_tree')
id = mod_obj.read(cr, uid, [result], ['res_id'])[0]['res_id']
result = act_obj.read(cr, uid, [id], context=context)[0]
result['context'] = str({'fiscalyear': data['form']['fiscalyear'],'state':data['form']['target_move']})
if data['form']['fiscalyear']:
result['name']+=':'+pooler.get_pool(cr.dbname).get('account.fiscalyear').read(cr,uid,[data['form']['fiscalyear']])[0]['code']
return result
states = {
'init': {
'actions': [_get_defaults],
'result': {'type': 'form', 'arch':_account_chart_arch, 'fields':_account_chart_fields, 'state': [('end', 'Cancel', 'gtk-cancel'), ('open', 'Open', 'gtk-ok')]}
},
'open': {
'actions': [],
'result': {'type': 'action', 'action':_account_chart_open_window, 'state':'end'}
}
}
wizard_account_chart('account.chart')
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -1,67 +0,0 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
import wizard
from tools.translate import _
_journal_form = '''<?xml version="1.0"?>
<form string="%s">
<field name="journal_id"/>
</form>''' % ('Open Bank reconciliation',)
_journal_fields = {
'journal_id': {'string':'Journal', 'type':'many2one', 'relation':'account.journal', 'required':True},
}
def _action_open_window(self, cr, uid, data, context):
form = data['form']
cr.execute('select default_credit_account_id from account_journal where id=%s', (form['journal_id'],))
account_id = cr.fetchone()[0]
if not account_id:
raise wizard.except_wizard(_('Error'), _('You have to define the bank account\nin the journal definition for reconciliation.'))
return {
'domain': "[('journal_id','=',%d), ('account_id','=',%d), ('state','<>','draft')]" % (form['journal_id'],account_id),
'name': _('Standard Encoding'),
'view_type': 'form',
'view_mode': 'tree,form',
'res_model': 'account.move.line',
'view_id': False,
'context': "{'journal_id':%d}" % (form['journal_id'],),
'type': 'ir.actions.act_window'
}
class wiz_journal(wizard.interface):
states = {
'init': {
'actions': [],
'result': {'type': 'form', 'arch':_journal_form, 'fields':_journal_fields, 'state':[('end','Cancel', 'gtk-cancel'),('open','Open', 'gtk-ok')]}
},
'open': {
'actions': [],
'result': {'type': 'action', 'action': _action_open_window, 'state':'end'}
}
}
wiz_journal('account.move.bank.reconcile')
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -1,71 +0,0 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
import wizard
import osv
import pooler
from tools.translate import _
_transaction_form = '''<?xml version="1.0"?>
<form string=" Close states of Fiscal year and periods">
<field name="fy_id"/>
<separator string="Are you sure you want to close the fiscal year ?" colspan="4"/>
<field name="sure"/>
</form>'''
_transaction_fields = {
'fy_id': {'string':'Fiscal Year to close', 'type':'many2one', 'relation': 'account.fiscalyear','required':True, 'domain':[('state','=','draft')]},
'sure': {'string':'Check this box', 'type':'boolean'},
}
def _data_save(self, cr, uid, data, context):
if not data['form']['sure']:
raise wizard.except_wizard(_('UserError'), _('Closing of states cancelled, please check the box !'))
pool = pooler.get_pool(cr.dbname)
fy_id = data['form']['fy_id']
cr.execute('UPDATE account_journal_period ' \
'SET state = %s ' \
'WHERE period_id IN (SELECT id FROM account_period WHERE fiscalyear_id = %s)',
('done',fy_id))
cr.execute('UPDATE account_period SET state = %s ' \
'WHERE fiscalyear_id = %s', ('done',fy_id))
cr.execute('UPDATE account_fiscalyear ' \
'SET state = %s WHERE id = %s', ('done', fy_id))
return {}
class wiz_journal_close_state(wizard.interface):
states = {
'init': {
'actions': [],
'result': {'type': 'form', 'arch':_transaction_form, 'fields':_transaction_fields, 'state':[('end','Cancel', 'gtk-cancel'),('close','Close states', 'gtk-ok')]}
},
'close': {
'actions': [_data_save],
'result': {'type': 'state', 'state':'end'}
}
}
wiz_journal_close_state('account.fiscalyear.close.state')
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -1,99 +0,0 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
import wizard
from osv import osv
import pooler
from tools.translate import _
_journal_form = '''<?xml version="1.0"?>
<form string="Standard entries">
<field name="journal_id"/>
<newline/>
<field name="period_id"/>
</form>'''
def _period_get(self, cr, uid, datas, ctx={}):
try:
pool = pooler.get_pool(cr.dbname)
ids = pool.get('account.period').find(cr, uid, context=ctx)
return {'period_id': ids[0]}
except:
return {}
_journal_fields = {
'journal_id': {'string':'Journal', 'type':'many2one', 'relation':'account.journal', 'required':True},
'period_id': {
'string':'Period',
'type':'many2one',
'relation':'account.period',
'required':True,
}
}
def _action_open_window(self, cr, uid, data, context):
form = data['form']
cr.execute('select id,name from ir_ui_view where model=%s and type=%s', ('account.move.line', 'form'))
view_res = cr.fetchone()
jp = pooler.get_pool(cr.dbname).get('account.journal.period')
ids = jp.search(cr, uid, [('journal_id','=',form['journal_id']), ('period_id','=',form['period_id'])])
if not len(ids):
name = pooler.get_pool(cr.dbname).get('account.journal').read(cr, uid, [form['journal_id']])[0]['name']
state = pooler.get_pool(cr.dbname).get('account.period').read(cr, uid, [form['period_id']])[0]['state']
if state == 'done':
raise wizard.except_wizard(_('UserError'), _('This period is already closed !'))
company = pooler.get_pool(cr.dbname).get('account.period').read(cr, uid, [form['period_id']])[0]['company_id'][0]
jp.create(cr, uid, {'name':name, 'period_id': form['period_id'], 'journal_id':form['journal_id'], 'company_id':company})
ids = jp.search(cr, uid, [('journal_id','=',form['journal_id']), ('period_id','=',form['period_id'])])
jp = jp.browse(cr, uid, ids, context=context)[0]
name = (jp.journal_id.code or '') + ':' + (jp.period_id.code or '')
mod_obj = pooler.get_pool(cr.dbname).get('ir.model.data')
result = mod_obj._get_id(cr, uid, 'account', 'view_account_move_line_filter')
id = mod_obj.read(cr, uid, result, ['res_id'])
return {
'domain': "[('journal_id','=',%d), ('period_id','=',%d)]" % (form['journal_id'],form['period_id']),
'name': name,
'view_type': 'form',
'view_mode': 'tree,form',
'res_model': 'account.move.line',
'view_id': view_res,
'context': "{'journal_id':%d, 'period_id':%d}" % (form['journal_id'],form['period_id']),
'type': 'ir.actions.act_window',
'search_view_id': id['res_id']
}
class wiz_journal(wizard.interface):
states = {
'init': {
'actions': [_period_get],
'result': {'type': 'form', 'arch':_journal_form, 'fields':_journal_fields, 'state':[('end','Cancel', 'gtk-cancel'),('open','Open Journal', 'gtk-ok')]}
},
'open': {
'actions': [],
'result': {'type': 'action', 'action': _action_open_window, 'state':'end'}
}
}
wiz_journal('account.move.journal')
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -1,61 +0,0 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
import wizard
from tools.translate import _
_journal_form = '''<?xml version="1.0"?>
<form string="%s">
<field name="account_id"/>
</form>''' % ('Reconciliation',)
_journal_fields = {
'account_id': {'string':'Account', 'type':'many2one', 'relation':'account.account','domain': [('reconcile','=',1)], 'required':True},
}
def _action_open_window(self, cr, uid, data, context):
return {
'domain': "[('account_id','=',%d),('reconcile_id','=',False),('state','<>','draft')]" % data['form']['account_id'],
'name': _('Reconciliation'),
'view_type': 'form',
'view_mode': 'tree,form',
'view_id': False,
'res_model': 'account.move.line',
'type': 'ir.actions.act_window'
}
class wiz_rec_select(wizard.interface):
states = {
'init': {
'actions': [],
'result': {'type': 'form', 'arch':_journal_form, 'fields':_journal_fields, 'state':[('end','Cancel'),('open','Open for reconciliation')]}
},
'open': {
'actions': [],
'result': {'type': 'action', 'action': _action_open_window, 'state':'end'}
}
}
wiz_rec_select('account.move.line.reconcile.select')
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -1,226 +0,0 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
import wizard
import pooler
import osv
import netsvc
import time
from tools.translate import _
sur_form = '''<?xml version="1.0"?>
<form string="Credit Note">
<label string="Are you sure you want to refund this invoice ?" colspan="2"/>
<newline />
<field name="date" />
<field name="period" />
<field name="description" width="150" />
</form>'''
sur_fields = {
'date': {'string':'Operation date','type':'date', 'required':'False'},
'period':{'string': 'Force period', 'type': 'many2one',
'relation': 'account.period', 'required': False},
'description':{'string':'Description', 'type':'char', 'required':'True'},
}
class wiz_refund(wizard.interface):
def _invoice_refund(self, cr, uid, data, context):
return self._compute_refund(cr, uid, data, 'refund', context)
def _invoice_cancel(self, cr, uid, data, context):
return self._compute_refund(cr, uid, data, 'cancel', context)
def _invoice_modify(self, cr, uid, data, context):
return self._compute_refund(cr, uid, data, 'modify', context)
def _compute_refund(self, cr, uid, data, mode, context):
form = data['form']
pool = pooler.get_pool(cr.dbname)
reconcile_obj = pool.get('account.move.reconcile')
account_m_line_obj = pool.get('account.move.line')
created_inv = []
date = False
period = False
description = False
for inv in pool.get('account.invoice').browse(cr, uid, data['ids']):
if inv.state in ['draft', 'proforma2', 'cancel']:
raise wizard.except_wizard(_('Error !'), _('Can not %s draft/proforma/cancel invoice.') % (mode))
if form['period'] :
period = form['period']
else:
period = inv.period_id and inv.period_id.id or False
if form['date'] :
date = form['date']
if not form['period'] :
cr.execute("select name from ir_model_fields where model='account.period' and name='company_id'")
result_query = cr.fetchone()
if result_query:
#in multi company mode
cr.execute("""SELECT id
from account_period where date('%s')
between date_start AND date_stop and company_id = %s limit 1 """,
(form['date'],pool.get('res.users').browse(cr,uid,uid).company_id.id,))
else:
#in mono company mode
cr.execute("""SELECT id
from account_period where date('%s')
between date_start AND date_stop limit 1 """,
(form['date'],))
res = cr.fetchone()
if res:
period = res[0]
else:
date = inv.date_invoice
if form['description'] :
description = form['description']
else:
description = inv.name
if not period:
raise wizard.except_wizard(_('Data Insufficient !'), _('No Period found on Invoice!'))
refund_id = pool.get('account.invoice').refund(cr, uid, [inv.id],date, period, description)
refund = pool.get('account.invoice').browse(cr, uid, refund_id[0])
# we compute due date
#!!!due date = date inv date on formdate
pool.get('account.invoice').write(cr, uid, [refund.id],{'date_due':date,'check_total':inv.check_total})
# to make the taxes calculated
pool.get('account.invoice').button_compute(cr, uid, refund_id)
created_inv.append(refund_id[0])
#if inv is paid we unreconcile
if mode in ('cancel','modify'):
movelines = inv.move_id.line_id
#we unreconcile the lines
to_reconcile_ids = {}
for line in movelines :
#if the account of the line is the as the one in the invoice
#we reconcile
if line.account_id.id == inv.account_id.id :
to_reconcile_ids[line.account_id.id] =[line.id]
if type(line.reconcile_id) != osv.orm.browse_null :
reconcile_obj.unlink(cr,uid, line.reconcile_id.id)
#we advance the workflow of the refund to open
wf_service = netsvc.LocalService('workflow')
wf_service.trg_validate(uid, 'account.invoice', refund.id, 'invoice_open', cr)
#we reload the browse record
refund = pool.get('account.invoice').browse(cr, uid, refund_id[0])
#we match the line to reconcile
for tmpline in refund.move_id.line_id :
if tmpline.account_id.id == inv.account_id.id :
to_reconcile_ids[tmpline.account_id.id].append(tmpline.id)
for account in to_reconcile_ids :
account_m_line_obj.reconcile(cr, uid, to_reconcile_ids[account],
writeoff_period_id=period,
writeoff_journal_id=inv.journal_id.id,
writeoff_acc_id=inv.account_id.id
)
#we create a new invoice that is the copy of the original
if mode == 'modify' :
invoice = pool.get('account.invoice').read(cr, uid, [inv.id],
['name', 'type', 'number', 'reference',
'comment', 'date_due', 'partner_id', 'address_contact_id',
'address_invoice_id', 'partner_insite','partner_contact',
'partner_ref', 'payment_term', 'account_id', 'currency_id',
'invoice_line', 'tax_line', 'journal_id','period_id'
]
)
invoice = invoice[0]
del invoice['id']
invoice_lines = pool.get('account.invoice.line').read(cr, uid, invoice['invoice_line'])
invoice_lines = pool.get('account.invoice')._refund_cleanup_lines(cr, uid, invoice_lines)
tax_lines = pool.get('account.invoice.tax').read(
cr, uid, invoice['tax_line'])
tax_lines = pool.get('account.invoice')._refund_cleanup_lines(cr, uid, tax_lines)
invoice.update({
'type': inv.type,
'date_invoice': date,
'state': 'draft',
'number': False,
'invoice_line': invoice_lines,
'tax_line': tax_lines,
'period_id': period,
'name':description
})
#take the id part of the tuple returned for many2one fields
for field in ('address_contact_id', 'address_invoice_id', 'partner_id',
'account_id', 'currency_id', 'payment_term', 'journal_id'):
invoice[field] = invoice[field] and invoice[field][0]
# create the new invoice
inv_id = pool.get('account.invoice').create(cr, uid, invoice,{})
# we compute due date
if inv.payment_term.id:
data = pool.get('account.invoice').onchange_payment_term_date_invoice(cr, uid, [inv_id],inv.payment_term.id,date)
if 'value' in data and data['value']:
pool.get('account.invoice').write(cr, uid, [inv_id],data['value'])
created_inv.append(inv_id)
#we get the view id
mod_obj = pool.get('ir.model.data')
act_obj = pool.get('ir.actions.act_window')
if inv.type == 'out_invoice':
xml_id = 'action_invoice_tree5'
elif inv.type == 'in_invoice':
xml_id = 'action_invoice_tree8'
elif type == 'out_refund':
xml_id = 'action_invoice_tree10'
else:
xml_id = 'action_invoice_tree12'
#we get the model
result = mod_obj._get_id(cr, uid, 'account', xml_id)
id = mod_obj.read(cr, uid, result, ['res_id'])['res_id']
# we read the act window
result = act_obj.read(cr, uid, id)
result['res_id'] = created_inv
return result
states = {
'init': {
'actions': [],
'result': {'type':'form', 'arch':sur_form, 'fields':sur_fields, 'state':[('end','Cancel'),('refund','Refund Invoice'),('cancel_invoice','Cancel Invoice'),('modify_invoice','Modify Invoice')]}
},
'refund': {
'actions': [],
'result': {'type':'action', 'action':_invoice_refund, 'state':'end'},
},
'cancel_invoice': {
'actions': [],
'result': {'type':'action', 'action':_invoice_cancel, 'state':'end'},
},
'modify_invoice': {
'actions': [],
'result': {'type':'action', 'action':_invoice_modify, 'state':'end'},
},
}
wiz_refund('account.invoice.refund')
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -1,61 +0,0 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
import time
import wizard
import pooler
#
# TODO: add an intermediate screen for checks
#
_subscription_form = '''<?xml version="1.0"?>
<form string="%s">
<separator string="Generate entries before:" colspan="4"/>
<field name="date"/>
</form>''' % ('Subscription Compute',)
_subscription_fields = {
'date': {'string':'Date', 'type':'date', 'default':lambda *a: time.strftime('%Y-%m-%d'), 'required':True},
}
class wiz_subscription(wizard.interface):
def _action_generate(self, cr, uid, data, context={}):
cr.execute('select id from account_subscription_line where date<%s and move_id is null', (data['form']['date'],))
ids = map(lambda x: x[0], cr.fetchall())
pooler.get_pool(cr.dbname).get('account.subscription.line').move_create(cr, uid, ids)
return {}
states = {
'init': {
'actions': [],
'result': {'type': 'form', 'arch':_subscription_form, 'fields':_subscription_fields, 'state':[('end','Cancel', 'gtk-cancel'),('generate','Compute', 'gtk-ok')]}
},
'generate': {
'actions': [_action_generate],
'result': {'type': 'state', 'state':'end'}
}
}
wiz_subscription('account.subscription.generate')
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -1,59 +0,0 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
import wizard
_journal_form = '''<?xml version="1.0"?>
<form string="%s">
<field name="account_id"/>
</form>''' % ('Unreconciliation',)
_journal_fields = {
'account_id': {'string':'Account', 'type':'many2one', 'relation':'account.account', 'required':True},
}
def _action_open_window(self, cr, uid, data, context):
return {
'domain': "[('account_id','=',%d),('reconcile_id','<>',False),('state','<>','draft')]" % data['form']['account_id'],
'name': 'Unreconciliation',
'view_type': 'form',
'view_mode': 'tree,form',
'view_id': False,
'res_model': 'account.move.line',
'type': 'ir.actions.act_window'
}
class wiz_unrec_select(wizard.interface):
states = {
'init': {
'actions': [],
'result': {'type': 'form', 'arch':_journal_form, 'fields':_journal_fields, 'state':[('end','Cancel', 'gtk-cancel'),('open','Open for unreconciliation', 'gtk-ok')]}
},
'open': {
'actions': [],
'result': {'type': 'action', 'action': _action_open_window, 'state':'end'}
}
}
wiz_unrec_select('account.move.line.unreconcile.select')
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -37,6 +37,7 @@
'depends': ['base'],
'init_xml': [],
'update_xml': [
'wizard/audittrail_view_log_view.xml',
'audittrail_view.xml',
'security/ir.model.access.csv',
'security/audittrail_security.xml'

View File

@ -19,35 +19,39 @@
#
##############################################################################
from osv import fields, osv
from osv.osv import osv_pool
from tools.translate import _
import ir
import netsvc
import pooler
import string
import time, copy
import time
class audittrail_rule(osv.osv):
"""
For Auddittrail Rule
"""
_name = 'audittrail.rule'
_description = "Audittrail Rule"
_columns = {
"name": fields.char("Rule Name", size=32, required=True),
"object_id": fields.many2one('ir.model', 'Object', required=True),
"user_id": fields.many2many('res.users', 'audittail_rules_users', 'user_id', 'rule_id', 'Users'),
"log_read": fields.boolean("Log reads"),
"log_write": fields.boolean("Log writes"),
"log_unlink": fields.boolean("Log deletes"),
"log_create": fields.boolean("Log creates"),
"state": fields.selection((("draft", "Draft"), ("subscribed", "Subscribed")), "State", required=True),
"action_id":fields.many2one('ir.actions.act_window', "Action ID"),
"name": fields.char("Rule Name", size=32, required=True),
"object_id": fields.many2one('ir.model', 'Object', required=True),
"user_id": fields.many2many('res.users', 'audittail_rules_users',
'user_id', 'rule_id', 'Users'),
"log_read": fields.boolean("Log reads"),
"log_write": fields.boolean("Log writes"),
"log_unlink": fields.boolean("Log deletes"),
"log_create": fields.boolean("Log creates"),
"state": fields.selection((("draft", "Draft"),
("subscribed", "Subscribed")),
"State", required=True),
"action_id": fields.many2one('ir.actions.act_window', "Action ID"),
}
_defaults = {
'state': lambda *a: 'draft',
'log_create': lambda *a: 1,
'log_unlink': lambda *a: 1,
'log_write': lambda *a: 1,
'state': lambda *a: 'draft',
'log_create': lambda *a: 1,
'log_unlink': lambda *a: 1,
'log_write': lambda *a: 1,
}
_sql_constraints = [
@ -56,59 +60,86 @@ class audittrail_rule(osv.osv):
__functions = {}
def subscribe(self, cr, uid, ids, *args):
"""
Subscribe Rule for auditing changes on object and apply shortcut for logs on that object.
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param ids: List of Auddittrail Rules IDs.
@return: True
"""
obj_action = self.pool.get('ir.actions.act_window')
obj_model = self.pool.get('ir.model.data')
#start Loop
for thisrule in self.browse(cr, uid, ids):
obj = self.pool.get(thisrule.object_id.model)
if not obj:
raise osv.except_osv(
_('WARNING:audittrail is not part of the pool'),
_('WARNING: audittrail is not part of the pool'),
_('Change audittrail depends -- Setting rule as DRAFT'))
self.write(cr, uid, [thisrule.id], {"state": "draft"})
val={
"name":'View Log',
"res_model":'audittrail.log',
"src_model":thisrule.object_id.model,
"domain":"[('object_id','=',"+str(thisrule.object_id.id)+"),('res_id', '=', active_id)]"
val = {
"name": 'View Log',
"res_model": 'audittrail.log',
"src_model": thisrule.object_id.model,
"domain": "[('object_id','=', " + str(thisrule.object_id.id) + "), ('res_id', '=', active_id)]"
}
id=self.pool.get('ir.actions.act_window').create(cr, uid, val)
self.write(cr, uid, [thisrule.id], {"state": "subscribed", "action_id":id})
id = obj_action.create(cr, uid, val)
self.write(cr, uid, [thisrule.id], {"state": "subscribed", "action_id": id})
keyword = 'client_action_relate'
value = 'ir.actions.act_window,'+str(id)
res=self.pool.get('ir.model.data').ir_set(cr, uid, 'action', keyword, 'View_log_'+thisrule.object_id.model, [thisrule.object_id.model], value, replace=True, isobject=True, xml_id=False)
value = 'ir.actions.act_window,' + str(id)
res = obj_model.ir_set(cr, uid, 'action', keyword, 'View_log_' + thisrule.object_id.model, [thisrule.object_id.model], value, replace=True, isobject=True, xml_id=False)
#End Loop
return True
def unsubscribe(self, cr, uid, ids, *args):
"""
Unsubscribe Auditing Rule on object
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param ids: List of Auddittrail Rules IDs.
@return: True
"""
obj_action = self.pool.get('ir.actions.act_window')
val_obj = self.pool.get('ir.values')
#start Loop
for thisrule in self.browse(cr, uid, ids):
if thisrule.id in self.__functions :
if thisrule.id in self.__functions:
for function in self.__functions[thisrule.id]:
setattr(function[0], function[1], function[2])
w_id=self.pool.get('ir.actions.act_window').search(cr, uid, [('name', '=', 'View Log'), ('res_model', '=', 'audittrail.log'), ('src_model', '=', thisrule.object_id.model)])
self.pool.get('ir.actions.act_window').unlink(cr, uid, w_id)
val_obj=self.pool.get('ir.values')
value="ir.actions.act_window"+','+str(w_id[0])
val_id=val_obj.search(cr, uid, [('model', '=', thisrule.object_id.model), ('value', '=', value)])
w_id = obj_action.search(cr, uid, [('name', '=', 'View Log'), ('res_model', '=', 'audittrail.log'), ('src_model', '=', thisrule.object_id.model)])
obj_action.unlink(cr, uid, w_id)
value = "ir.actions.act_window" + ',' + str(w_id[0])
val_id = val_obj.search(cr, uid, [('model', '=', thisrule.object_id.model), ('value', '=', value)])
if val_id:
res = ir.ir_del(cr, uid, val_id[0])
self.write(cr, uid, [thisrule.id], {"state": "draft"})
#End Loop
return True
audittrail_rule()
class audittrail_log(osv.osv):
"""
For Audittrail Log
"""
_name = 'audittrail.log'
_description = "Audittrail Log"
_columns = {
"name": fields.char("Name", size=32),
"object_id": fields.many2one('ir.model', 'Object'),
"user_id": fields.many2one('res.users', 'User'),
"method": fields.selection((('read', 'Read'), ('write', 'Write'), ('unlink', 'Delete'), ('create', 'Create')), "Method"),
"timestamp": fields.datetime("Date"),
"res_id":fields.integer('Resource Id'),
"line_ids":fields.one2many('audittrail.log.line', 'log_id', 'Log lines'),
"name": fields.char("Name", size=32),
"object_id": fields.many2one('ir.model', 'Object'),
"user_id": fields.many2one('res.users', 'User'),
"method": fields.selection((('read', 'Read'),
('write', 'Write'),
('unlink', 'Delete'),
('create', 'Create')), "Method"),
"timestamp": fields.datetime("Date"),
"res_id": fields.integer('Resource Id'),
"line_ids": fields.one2many('audittrail.log.line', 'log_id', 'Log lines'),
}
_defaults = {
"timestamp": lambda *a: time.strftime("%Y-%m-%d %H:%M:%S")
}
@ -116,235 +147,293 @@ class audittrail_log(osv.osv):
audittrail_log()
class audittrail_log_line(osv.osv):
_name='audittrail.log.line'
_columns={
'field_id': fields.many2one('ir.model.fields', 'Fields', required=True),
'log_id':fields.many2one('audittrail.log', 'Log'),
'log':fields.integer("Log ID"),
'old_value':fields.text("Old Value"),
'new_value':fields.text("New Value"),
'old_value_text':fields.text('Old value Text'),
'new_value_text':fields.text('New value Text'),
'field_description':fields.char('Field Description' , size=64),
}
"""
Audittrail Log Line.
"""
_name = 'audittrail.log.line'
_description = "Log Line"
_columns = {
'field_id': fields.many2one('ir.model.fields', 'Fields', required=True),
'log_id': fields.many2one('audittrail.log', 'Log'),
'log': fields.integer("Log ID"),
'old_value': fields.text("Old Value"),
'new_value': fields.text("New Value"),
'old_value_text': fields.text('Old value Text'),
'new_value_text': fields.text('New value Text'),
'field_description': fields.char('Field Description', size=64),
}
audittrail_log_line()
class audittrail_objects_proxy(osv_pool):
def get_value_text(self, cr, uid, field_name, values, object, context={}):
pool = pooler.get_pool(cr.dbname)
obj=pool.get(object.model)
obj_ids= pool.get('ir.model').search(cr, uid, [('model', '=', object.model)])
model_object=pool.get('ir.model').browse(cr, uid, obj_ids)[0]
f_id= pool.get('ir.model.fields').search(cr, uid, [('name', '=', field_name), ('model_id', '=', object.id)])
if f_id:
field=pool.get('ir.model.fields').read(cr, uid, f_id)[0]
model=field['relation']
""" Uses Object proxy for auditing changes on object of subscribed Rules"""
def get_value_text(self, cr, uid, field_name, values, object, context=None):
"""
Gets textual values for the fields
e.g.: For field of type many2one it gives its name value instead of id
if field['ttype']=='many2one':
if values:
if type(values)==tuple:
values=values[0]
val=pool.get(model).read(cr, uid, [values], [pool.get(model)._rec_name])
if len(val):
return val[0][pool.get(model)._rec_name]
elif field['ttype'] == 'many2many':
value=[]
if values:
for id in values:
val=pool.get(model).read(cr, uid, [id], [pool.get(model)._rec_name])
if len(val):
value.append(val[0][pool.get(model)._rec_name])
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param field_name: List of fields for text values
@param values: Values for field to be converted into textual values
@return: values: List of textual values for given fields
"""
if not context:
context = {}
pool = pooler.get_pool(cr.dbname)
f_id = pool.get('ir.model.fields').search(cr, uid, [('name', '=', field_name), ('model_id', '=', object.id)])
if f_id:
field = pool.get('ir.model.fields').read(cr, uid, f_id)[0]
model = field['relation']
if field['ttype'] == 'many2one':
if values:
if type(values) == tuple:
values = values[0]
val = pool.get(model).read(cr, uid, [values], [pool.get(model)._rec_name])
if val:
return val[0][pool.get(model)._rec_name]
elif field['ttype'] == 'many2many':
value = []
if values:
for id in values:
val = pool.get(model).read(cr, uid, [id], [pool.get(model)._rec_name])
if val:
value.append(val[0][pool.get(model)._rec_name])
return value
elif field['ttype'] == 'one2many':
if values:
value = []
for id in values:
val = pool.get(model).read(cr, uid, [id], [pool.get(model)._rec_name])
if val:
value.append(val[0][pool.get(model)._rec_name])
return value
elif field['ttype'] == 'one2many':
if values:
value=[]
for id in values:
val=pool.get(model).read(cr, uid, [id], [pool.get(model)._rec_name])
if len(val):
value.append(val[0][pool.get(model)._rec_name])
return value
return values
def create_log_line(self, cr, uid, id, object, lines=[]):
"""
Creates lines for changed fields with its old and new values
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param object: Object who's values are being changed
@param lines: List of values for line is to be created
"""
pool = pooler.get_pool(cr.dbname)
obj=pool.get(object.model)
obj_ids= pool.get('ir.model').search(cr, uid, [('model', '=', object.model)])
model_object=pool.get('ir.model').browse(cr, uid, obj_ids)[0]
obj = pool.get(object.model)
#start Loop
for line in lines:
if obj._inherits:
inherits_ids= pool.get('ir.model').search(cr, uid, [('model', '=', obj._inherits.keys()[0])])
f_id= pool.get('ir.model.fields').search(cr, uid, [('name', '=', line['name']), ('model_id', 'in', (object.id, inherits_ids[0]))])
inherits_ids = pool.get('ir.model').search(cr, uid, [('model', '=', obj._inherits.keys()[0])])
f_id = pool.get('ir.model.fields').search(cr, uid, [('name', '=', line['name']), ('model_id', 'in', (object.id, inherits_ids[0]))])
else:
f_id= pool.get('ir.model.fields').search(cr, uid, [('name', '=', line['name']), ('model_id', '=', object.id)])
if len(f_id):
fields=pool.get('ir.model.fields').read(cr, uid, f_id)
old_value='old_value' in line and line['old_value'] or ''
new_value='new_value' in line and line['new_value'] or ''
old_value_text='old_value_text' in line and line['old_value_text'] or ''
new_value_text='new_value_text' in line and line['new_value_text'] or ''
f_id = pool.get('ir.model.fields').search(cr, uid, [('name', '=', line['name']), ('model_id', '=', object.id)])
if f_id:
fields = pool.get('ir.model.fields').read(cr, uid, f_id)
old_value = 'old_value' in line and line['old_value'] or ''
new_value = 'new_value' in line and line['new_value'] or ''
old_value_text = 'old_value_text' in line and line['old_value_text'] or ''
new_value_text = 'new_value_text' in line and line['new_value_text'] or ''
if old_value_text == new_value_text:
continue
if fields[0]['ttype']== 'many2one':
if type(old_value)==tuple:
old_value=old_value[0]
if type(new_value)==tuple:
new_value=new_value[0]
log_line_id = pool.get('audittrail.log.line').create(cr, uid, {"log_id": id, "field_id": f_id[0] , "old_value":old_value , "new_value":new_value, "old_value_text":old_value_text , "new_value_text":new_value_text, "field_description":fields[0]['field_description']})
if fields[0]['ttype'] == 'many2one':
if type(old_value) == tuple:
old_value = old_value[0]
if type(new_value) == tuple:
new_value = new_value[0]
vals = {
"log_id": id,
"field_id": f_id[0],
"old_value": old_value,
"new_value": new_value,
"old_value_text": old_value_text,
"new_value_text": new_value_text,
"field_description": fields[0]['field_description']
}
line_id = pool.get('audittrail.log.line').create(cr, uid, vals)
cr.commit()
#End Loop
return True
def log_fct(self, db, uid, object, method, fct_src, *args):
logged_uids = []
pool = pooler.get_pool(db)
cr = pooler.get_db(db).cursor()
obj=pool.get(object)
obj_ids= pool.get('ir.model').search(cr, uid, [('model', '=', object)])
model_object=pool.get('ir.model').browse(cr, uid, obj_ids)[0]
if method in ('create'):
res_id = fct_src(db, uid, object, method, *args)
"""
Logging function: This function is performs logging oprations according to method
@param db: the current database
@param uid: the current users ID for security checks,
@param object: Object who's values are being changed
@param method: method to log: create, read, write, unlink
@param fct_src: execute method of Object proxy
@return: Returns result as per method of Object proxy
"""
logged_uids = []
pool = pooler.get_pool(db)
cr = pooler.get_db(db).cursor()
obj_ids = pool.get('ir.model').search(cr, uid, [('model', '=', object)])
model_object = pool.get('ir.model').browse(cr, uid, obj_ids)[0]
if method in ('create'):
res_id = fct_src(db, uid, object, method, *args)
cr.commit()
new_value = pool.get(model_object.model).read(cr, uid, [res_id], args[0].keys())[0]
if 'id' in new_value:
del new_value['id']
if not logged_uids or uid in logged_uids:
resource_name = pool.get(model_object.model).name_get(cr, uid, [res_id])
resource_name = resource_name and resource_name[0][1] or ''
vals = {
"method": method,
"object_id": model_object.id,
"user_id": uid, "res_id": res_id,
"name": resource_name
}
id = pool.get('audittrail.log').create(cr, uid, vals)
lines = []
for field in new_value:
if new_value[field]:
line = {
'name': field,
'new_value': new_value[field],
'new_value_text': self.get_value_text(cr, uid, field, new_value[field], model_object)
}
lines.append(line)
self.create_log_line(cr, uid, id, model_object, lines)
cr.commit()
cr.close()
return res_id
if method in ('write'):
res_ids = args[0]
for res_id in res_ids:
old_values = pool.get(model_object.model).read(cr, uid, res_id, args[1].keys())
old_values_text = {}
for field in args[1].keys():
old_values_text[field] = self.get_value_text(cr, uid, field, old_values[field], model_object)
res = fct_src(db, uid, object, method, *args)
cr.commit()
new_value=pool.get(model_object.model).read(cr, uid, [res_id], args[0].keys())[0]
if 'id' in new_value:
del new_value['id']
if not len(logged_uids) or uid in logged_uids:
resource_name = pool.get(model_object.model).name_get(cr, uid, [res_id])
resource_name = resource_name and resource_name[0][1] or ''
id=pool.get('audittrail.log').create(cr, uid, {"method": method , "object_id": model_object.id, "user_id": uid, "res_id": res_id, "name": resource_name})
lines=[]
for field in new_value:
if new_value[field]:
line={
'name':field,
'new_value':new_value[field],
'new_value_text': self.get_value_text(cr, uid, field, new_value[field], model_object)
}
lines.append(line)
self.create_log_line(cr, uid, id, model_object, lines)
cr.commit()
cr.close()
return res_id
if method in ('write'):
res_ids=args[0]
for res_id in res_ids:
old_values=pool.get(model_object.model).read(cr, uid, res_id, args[1].keys())
old_values_text={}
for field in args[1].keys():
old_values_text[field] = self.get_value_text(cr, uid, field, old_values[field], model_object)
res =fct_src(db, uid, object, method, *args)
cr.commit()
if res:
new_values=pool.get(model_object.model).read(cr, uid, res_ids, args[1].keys())[0]
if not len(logged_uids) or uid in logged_uids:
resource_name = pool.get(model_object.model).name_get(cr, uid, [res_id])
resource_name = resource_name and resource_name[0][1] or ''
id=pool.get('audittrail.log').create(cr, uid, {"method": method, "object_id": model_object.id, "user_id": uid, "res_id": res_id, "name": resource_name})
lines=[]
for field in args[1].keys():
if args[1].keys():
line={
'name':field,
'new_value':field in new_values and new_values[field] or '',
'old_value':field in old_values and old_values[field] or '',
'new_value_text': self.get_value_text(cr, uid, field, new_values[field], model_object),
'old_value_text':old_values_text[field]
}
lines.append(line)
cr.commit()
self.create_log_line(cr, uid, id, model_object, lines)
cr.close()
return res
if method in ('read'):
res_ids=args[0]
old_values={}
res =fct_src(db, uid, object, method, *args)
if type(res)==list:
for v in res:
old_values[v['id']]=v
else:
old_values[res['id']]=res
for res_id in old_values:
if not len(logged_uids) or uid in logged_uids:
if res:
new_values = pool.get(model_object.model).read(cr, uid, res_ids, args[1].keys())[0]
if not logged_uids or uid in logged_uids:
resource_name = pool.get(model_object.model).name_get(cr, uid, [res_id])
resource_name = resource_name and resource_name[0][1] or ''
id=pool.get('audittrail.log').create(cr, uid, {"method": method , "object_id": model_object.id, "user_id": uid, "res_id": res_id, "name": resource_name})
lines=[]
for field in old_values[res_id]:
if old_values[res_id][field]:
line={
'name':field,
'old_value':old_values[res_id][field],
'old_value_text': self.get_value_text(cr, uid, field, old_values[res_id][field], model_object)
}
lines.append(line)
cr.commit()
self.create_log_line(cr, uid, id, model_object, lines)
cr.close()
return res
if method in ('unlink'):
res_ids=args[0]
old_values={}
for res_id in res_ids:
old_values[res_id]=pool.get(model_object.model).read(cr, uid, res_id, [])
for res_id in res_ids:
if not len(logged_uids) or uid in logged_uids:
resource_name = pool.get(model_object.model).name_get(cr, uid, [res_id])
resource_name = resource_name and resource_name[0][1] or ''
id=pool.get('audittrail.log').create(cr, uid, {"method": method , "object_id": model_object.id, "user_id": uid, "res_id": res_id, "name": resource_name})
lines=[]
for field in old_values[res_id]:
if old_values[res_id][field]:
line={
'name':field,
'old_value':old_values[res_id][field],
'old_value_text': self.get_value_text(cr, uid, field, old_values[res_id][field], model_object)
id = pool.get('audittrail.log').create(cr, uid, {"method": method, "object_id": model_object.id, "user_id": uid, "res_id": res_id, "name": resource_name})
lines = []
for field in args[1].keys():
if args[1].keys():
line = {
'name': field,
'new_value': field in new_values and new_values[field] or '',
'old_value': field in old_values and old_values[field] or '',
'new_value_text': self.get_value_text(cr, uid, field, new_values[field], model_object),
'old_value_text': old_values_text[field]
}
lines.append(line)
cr.commit()
self.create_log_line(cr, uid, id, model_object, lines)
res =fct_src(db, uid, object, method, *args)
cr.close()
return res
if method in ('read'):
res_ids = args[0]
old_values = {}
res = fct_src(db, uid, object, method, *args)
if type(res) == list:
for v in res:
old_values[v['id']] = v
else:
old_values[res['id']] = res
for res_id in old_values:
if not logged_uids or uid in logged_uids:
resource_name = pool.get(model_object.model).name_get(cr, uid, [res_id])
resource_name = resource_name and resource_name[0][1] or ''
id = pool.get('audittrail.log').create(cr, uid, {"method": method, "object_id": model_object.id, "user_id": uid, "res_id": res_id, "name": resource_name})
lines = []
for field in old_values[res_id]:
if old_values[res_id][field]:
line = {
'name': field,
'old_value': old_values[res_id][field],
'old_value_text': self.get_value_text(cr, uid, field, old_values[res_id][field], model_object)
}
lines.append(line)
cr.commit()
self.create_log_line(cr, uid, id, model_object, lines)
cr.close()
return res
if method in ('unlink'):
res_ids = args[0]
old_values = {}
for res_id in res_ids:
old_values[res_id] = pool.get(model_object.model).read(cr, uid, res_id, [])
for res_id in res_ids:
if not logged_uids or uid in logged_uids:
resource_name = pool.get(model_object.model).name_get(cr, uid, [res_id])
resource_name = resource_name and resource_name[0][1] or ''
id = pool.get('audittrail.log').create(cr, uid, {"method": method, "object_id": model_object.id, "user_id": uid, "res_id": res_id, "name": resource_name})
lines = []
for field in old_values[res_id]:
if old_values[res_id][field]:
line = {
'name': field,
'old_value': old_values[res_id][field],
'old_value_text': self.get_value_text(cr, uid, field, old_values[res_id][field], model_object)
}
lines.append(line)
cr.commit()
self.create_log_line(cr, uid, id, model_object, lines)
res = fct_src(db, uid, object, method, *args)
cr.close()
return res
cr.close()
def execute(self, db, uid, object, method, *args, **kw):
"""
Overrides Object Proxy execute method
@param db: the current database
@param uid: the current users ID for security checks,
@param object: Object who's values are being changed
@param method: method to log: create, read, write, unlink
@return: Returns result as per method of Object proxy
"""
pool = pooler.get_pool(db)
cr = pooler.get_db(db).cursor()
cr.autocommit(True)
obj=pool.get(object)
logged_uids = []
fct_src = super(audittrail_objects_proxy, self).execute
def my_fct(db, uid, object, method, *args):
field = method
rule = False
obj_ids= pool.get('ir.model').search(cr, uid, [('model', '=', object)])
obj_ids = pool.get('ir.model').search(cr, uid, [('model', '=', object)])
for obj_name in pool.obj_list():
if obj_name == 'audittrail.rule':
rule = True
if not rule:
return fct_src(db, uid, object, method, *args)
if not len(obj_ids):
if not obj_ids:
return fct_src(db, uid, object, method, *args)
rule_ids=pool.get('audittrail.rule').search(cr, uid, [('object_id', '=', obj_ids[0]), ('state', '=', 'subscribed')])
if not len(rule_ids):
rule_ids = pool.get('audittrail.rule').search(cr, uid, [('object_id', '=', obj_ids[0]), ('state', '=', 'subscribed')])
if not rule_ids:
return fct_src(db, uid, object, method, *args)
for thisrule in pool.get('audittrail.rule').browse(cr, uid, rule_ids):
for user in thisrule.user_id:
logged_uids.append(user.id)
if not len(logged_uids) or uid in logged_uids:
if not logged_uids or uid in logged_uids:
if field in ('read', 'write', 'create', 'unlink'):
if getattr(thisrule, 'log_'+field):
if getattr(thisrule, 'log_' + field):
return self.log_fct(db, uid, object, method, fct_src, *args)
return fct_src(db, uid, object, method, *args)
res = my_fct(db, uid, object, method, *args)

View File

@ -1,6 +1,9 @@
<?xml version="1.0" ?>
<openerp>
<data>
<!-- Audittrail Rule form -->
<record model="ir.ui.view" id="view_audittrail_rule_form">
<field name="name">audittrail.rule.form</field>
<field name="model">audittrail.rule</field>
@ -17,12 +20,15 @@
<field name="user_id" select="1" colspan="4"/>
<field name="state" select="1" readonly="1" />
<group colspan="2" col="2">
<button string="Subscribe" name="subscribe" type="object" states="draft"/>
<button string="UnSubscribe" name="unsubscribe" type="object" states="subscribed"/>
<button string="Subscribe" name="subscribe"
type="object" states="draft" />
<button string="UnSubscribe" name="unsubscribe"
type="object" states="subscribed" />
</group>
</form>
</field>
</record>
<record model="ir.ui.view" id="view_audittrail_rule_tree">
<field name="name">audittrail.rule.tree</field>
<field name="model">audittrail.rule</field>
@ -40,6 +46,9 @@
</tree>
</field>
</record>
<!-- Action for audittrail rule -->
<record model="ir.actions.act_window" id="action_audittrail_rule_tree">
<field name="name">Rules</field>
<field name="res_model">audittrail.rule</field>
@ -48,8 +57,11 @@
<field name="view_mode">tree,form</field>
<!--<field name="view_id" ref="view_audittrail_rule_form" />-->
</record>
<menuitem name="Audittrails" id="menu_action_audittrail" parent="base.menu_administration"/>
<menuitem name="Rules" id="menu_action_audittrail_rule_tree" action="action_audittrail_rule_tree" parent="menu_action_audittrail"/>
<menuitem name="Audittrails" id="menu_action_audittrail"
parent="base.menu_administration" />
<menuitem name="Rules" id="menu_action_audittrail_rule_tree"
action="action_audittrail_rule_tree" parent="menu_action_audittrail" />
<record model="ir.actions.act_window" id="action_audittrail_rule_tree_sub">
@ -60,6 +72,8 @@
<field name="domain">[('state','=','subscribed')]</field>
<field name="filter" eval="True"/>
</record>
<!-- AuditTrail Log form -->
<record model="ir.ui.view" id="view_audittrail_log_form">
<field name="name">audittrail.log.form</field>
@ -73,23 +87,34 @@
<field name="user_id" select="1" readonly="1"/>
<field name="res_id" readonly="1"/>
<field name="name" readonly="1" select="1"/>
<field name="line_ids" colspan="4" mode="tree,form" widget="one2many_list" readonly="1">
<field name="line_ids" colspan="4" mode="tree,form"
widget="one2many_list" readonly="1">
<form string="Log Lines">
<field name="field_id" colspan="4" readonly="1"/>
<newline/>
<field name="field_description" colspan="4" readonly="1"/>
<newline/>
<separator string="Old Value : " colspan="2"/>
<separator string="New Value : " colspan="2"/>
<newline/>
<field name="old_value" nolabel="1" colspan="2" readonly="1"/>
<field name="new_value" nolabel="1" colspan="2" readonly="1"/>
<newline/>
<separator string="Old Value Text : " colspan="2"/>
<separator string="New Value Text: " colspan="2"/>
<newline/>
<field name="old_value_text" nolabel="1" colspan="2" readonly="1"/>
<field name="new_value_text" nolabel="1" colspan="2" readonly="1"/>
<field name="field_id" colspan="4"
readonly="1" />
<newline />
<field name="field_description" colspan="4"
readonly="1" />
<newline />
<separator string="Old Value : "
colspan="2" />
<separator string="New Value : "
colspan="2" />
<newline />
<field name="old_value" nolabel="1"
colspan="2" readonly="1" />
<field name="new_value" nolabel="1"
colspan="2" readonly="1" />
<newline />
<separator string="Old Value Text : "
colspan="2" />
<separator string="New Value Text: "
colspan="2" />
<newline />
<field name="old_value_text" nolabel="1"
colspan="2" readonly="1" />
<field name="new_value_text" nolabel="1"
colspan="2" readonly="1" />
</form>
<tree string="Log Lines">
@ -117,15 +142,19 @@
</field>
</record>
<!-- Action for Audittrail Log -->
<record model="ir.actions.act_window" id="action_audittrail_log_tree">
<field name="name">Logs</field>
<field name="res_model">audittrail.log</field>
<field name="view_type">form</field>
</record>
<menuitem name="Logs" id="menu_action_audittrail_log_tree" action="action_audittrail_log_tree" parent="menu_action_audittrail"/>
<menuitem name="Logs" id="menu_action_audittrail_log_tree"
action="action_audittrail_log_tree" parent="menu_action_audittrail" />
<wizard string="View log" menu="False" model="audittrail.log" name="audittrail.view.log" id="wizard_audittrail_log"/>
<menuitem name="View Logs" id="menu_action_log_tree2" action="wizard_audittrail_log" type="wizard" parent="menu_action_audittrail"/>
<!-- <wizard string="View log" menu="False" model="audittrail.log" name="audittrail.view.log" id="wizard_audittrail_log"/>-->
<menuitem name="View Logs" id="menu_action_log_tree2"
action="action_audittrail_view_log" parent="menu_action_audittrail" />
</data>
</data>
</openerp>

View File

@ -5,3 +5,4 @@
"access_audittrail_rule_all_users","audittrail rule all","model_audittrail_rule","base.group_user",1,0,0,0
"access_audittrail_log_all_users","audittrail log all","model_audittrail_log","base.group_user",1,0,1,0
"access_audittrail_log_line_all_users","audittrail log line all","model_audittrail_log_line","base.group_user",1,0,1,0
"access_audittrail_view_log","audittrail.view.log","model_audittrail_view_log","base.group_user",1,1,1,1

1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
5 access_audittrail_rule_all_users audittrail rule all model_audittrail_rule base.group_user 1 0 0 0
6 access_audittrail_log_all_users audittrail log all model_audittrail_log base.group_user 1 0 1 0
7 access_audittrail_log_line_all_users audittrail log line all model_audittrail_log_line base.group_user 1 0 1 0
8 access_audittrail_view_log audittrail.view.log model_audittrail_view_log base.group_user 1 1 1 1

View File

@ -19,6 +19,6 @@
#
##############################################################################
import wizard_view_log
import audittrail_view_log
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -0,0 +1,69 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from osv import fields, osv
import time
class audittrail_view_log(osv.osv_memory):
_name = "audittrail.view.log"
_description = "View Log"
_columns = {
'from':fields.datetime('Log From'),
'to':fields.datetime('Log To', required = True)
}
_defaults = {
'to': lambda *a: time.strftime("%Y-%m-%d %H:%M:%S"),
}
def log_open_window(self, cr, uid, ids, context=None):
"""
Open Log form from given date range..
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param ids: List of audittrail view logs IDs.
@return: Dictionary of audittrail log form on given date range.
"""
if not context:
context = {}
mod_obj = self.pool.get('ir.model.data')
act_obj = self.pool.get('ir.actions.act_window')
result = mod_obj._get_id(cr, uid, 'audittrail', 'action_audittrail_log_tree')
id = mod_obj.read(cr, uid, [result], ['res_id'])[0]['res_id']
result = act_obj.read(cr, uid, [id])[0]
#log_obj = self.pool.get(result['res_model'])
#log_id = log_obj.search(cr, uid, [])
#log_model = log_obj.read(cr, uid, log_id, ['object_id'])
#start Loop
for datas in self.read(cr, uid, ids):
if not datas.get('from', None):
if datas.get('to') <> time.strftime("%Y-%m-%d %H:%M:%S"):
result['domain'] = str([('timestamp', '<', datas.get('to'))])
else:
pass
else:
result['domain'] = str([('timestamp', '>', datas.get('from', None)), ('timestamp', '<', datas.get('to'))])
#End Loop
return result
audittrail_view_log()

View File

@ -0,0 +1,41 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<!-- Audittrail View Log wizard-->
<record id="view_audittrail_view_log" model="ir.ui.view">
<field name="name">audittrail.view.log.form</field>
<field name="model">audittrail.view.log</field>
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Audit Logs">
<group colspan="4" >
<field name="from" colspan="4"/>
<newline/>
<field name="to" colspan="4"/>
</group>
<separator string="" colspan="4" />
<group colspan="4" col="6">
<button icon="gtk-cancel" special="cancel"
string="Cancel" />
<button icon="gtk-open" string="Open Logs"
name="log_open_window" type="object" />
</group>
</form>
</field>
</record>
<!-- action for audittrail view log wizard -->
<record id="action_audittrail_view_log" model="ir.actions.act_window">
<field name="name">View log</field>
<field name="res_model">audittrail.view.log</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
<field name="view_id" ref="view_audittrail_view_log"/>
<field name="target">new</field>
</record>
</data>
</openerp>

View File

@ -1,81 +0,0 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
import wizard
import pooler
import time
class wizard_view_log(wizard.interface):
form1 = '''<?xml version="1.0"?>
<form string="Audit Logs">
<field name="from" colspan="4"/>
<newline/>
<field name="to" colspan="4"/>
</form>'''
form1_fields = {
'from': {
'string': 'Log From',
'type': 'datetime',
},
'to': {
'string': 'Log To',
'type': 'datetime',
'default': lambda *a: time.strftime("%Y-%m-%d %H:%M:%S"),
'required':True
},
}
def _log_open_window(self, cr, uid, data, context):
mod_obj = pooler.get_pool(cr.dbname).get('ir.model.data')
act_obj = pooler.get_pool(cr.dbname).get('ir.actions.act_window')
result = mod_obj._get_id(cr, uid, 'audittrail', 'action_audittrail_log_tree')
id = mod_obj.read(cr, uid, [result], ['res_id'])[0]['res_id']
result = act_obj.read(cr, uid, [id])[0]
log_obj= pooler.get_pool(cr.dbname).get(result['res_model'])
log_id = log_obj.search(cr, uid, [])
log_model=log_obj.read(cr, uid,log_id,['object_id'])
if not data['form']['from']:
if data['form']['to'] <> time.strftime("%Y-%m-%d %H:%M:%S"):
result['domain'] = str([('timestamp', '<',data['form']['to'])])
else:
pass
else:
result['domain'] = str([('timestamp', '>',data['form']['from']),('timestamp', '<',data['form']['to'])])
return result
states = {
'init': {
'actions': [],
'result': {'type': 'form', 'arch':form1, 'fields':form1_fields, 'state': [('end', 'Cancel'), ('open', 'Open Logs')]}
},
'open': {
'actions': [],
'result': {'type': 'action', 'action':_log_open_window, 'state':'end'}
}
}
wizard_view_log('audittrail.view.log')
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -29,61 +29,86 @@ from osv.orm import except_orm
from tools.translate import _
class base_action_rule(osv.osv):
""" Base Action Rules """
_name = 'base.action.rule'
_description = 'Action Rules'
_description = 'Action Rules'
_columns = {
'name': fields.many2one('ir.model', 'Model', required=True),
'max_level': fields.integer('Max Level', help='Specifies maximum level.'),
'max_level': fields.integer('Max Level', help='Specifies maximum level.'),
'rule_lines': fields.one2many('base.action.rule.line','rule_id','Rule Lines'),
'create_date': fields.datetime('Create Date', readonly=1),
'active': fields.boolean('Active')
}
_defaults = {
'active': lambda *a: True,
'max_level': lambda *a: 15,
}
def format_body(self, body):
""" Foramat Action rule's body
@param self: The object pointer """
return body and tools.ustr(body) or ''
def format_mail(self, obj, body):
""" Foramat Mail
@param self: The object pointer """
data = {
'object_id': obj.id,
'object_subject': hasattr(obj, 'name') and obj.name or False,
'object_date': hasattr(obj, 'date') and obj.date or False,
'object_description': hasattr(obj, 'description') and obj.description or False,
'object_user': hasattr(obj, 'user_id') and (obj.user_id and obj.user_id.name) or '/',
'object_user_email': hasattr(obj, 'user_id') and (obj.user_id and obj.user_id.address_id and obj.user_id.address_id.email) or '/',
'object_user_phone': hasattr(obj, 'user_id') and (obj.user_id and obj.user_id.address_id and obj.user_id.address_id.phone) or '/',
'object_user_email': hasattr(obj, 'user_id') and (obj.user_id and \
obj.user_id.address_id and obj.user_id.address_id.email) or '/',
'object_user_phone': hasattr(obj, 'user_id') and (obj.user_id and\
obj.user_id.address_id and obj.user_id.address_id.phone) or '/',
'partner': hasattr(obj, 'partner_id') and (obj.partner_id and obj.partner_id.name) or '/',
'partner_email': hasattr(obj, 'partner_address_id') and (obj.partner_address_id and obj.partner_address_id.email) or '/',
'partner_email': hasattr(obj, 'partner_address_id') and (obj.partner_address_id and\
obj.partner_address_id.email) or '/',
}
return self.format_body(body % data)
def email_send(self, cr, uid, obj, emails, body, emailfrom=tools.config.get('email_from',False), context={}):
""" send email
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param email: pass the emails
@param emailfrom: Pass name the email From else False
@param context: A standard dictionary for contextual values """
body = self.format_mail(obj, body)
if not emailfrom:
if hasattr(obj, 'user_id') and obj.user_id and obj.user_id.address_id and obj.user_id.address_id.email:
if hasattr(obj, 'user_id') and obj.user_id and obj.user_id.address_id and\
obj.user_id.address_id.email:
emailfrom = obj.user_id.address_id.email
name = '[%d] %s' % (obj.id, tools.ustr(obj.name))
emailfrom = tools.ustr(emailfrom)
reply_to = emailfrom
reply_to = emailfrom
if not emailfrom:
raise osv.except_osv(_('Error!'),
_("No E-Mail ID Found for your Company address!"))
return tools.email_send(emailfrom, emails, name, body, reply_to=reply_to, openobject_id=str(obj.id))
def do_check(self, cr, uid, action, obj, context={}):
ok = True
if hasattr(obj, 'user_id'):
""" check Action
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param context: A standard dictionary for contextual values """
ok = True
if hasattr(obj, 'user_id'):
ok = ok and (not action.trg_user_id.id or action.trg_user_id.id==obj.user_id.id)
if hasattr(obj, 'partner_id'):
ok = ok and (not action.trg_partner_id.id or action.trg_partner_id.id==obj.partner_id.id)
ok = ok and (not action.trg_partner_id.id or action.trg_partner_id.id==obj.partner_id.id)
ok = ok and (
not action.trg_partner_categ_id.id or
(
@ -113,14 +138,22 @@ class base_action_rule(osv.osv):
return ok
def do_action(self, cr, uid, action, model_obj, obj, context={}):
""" Do Action
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param action: pass action
@param model_obj: pass Model object
@param context: A standard dictionary for contextual values """
if action.server_action_id:
context.update({'active_id':obj.id,'active_ids':[obj.id]})
self.pool.get('ir.actions.server').run(cr, uid, [action.server_action_id.id], context)
write = {}
write = {}
if hasattr(obj, 'user_id') and action.act_user_id:
obj.user_id = action.act_user_id
write['user_id'] = action.act_user_id.id
if hasattr(obj, 'date_action_last'):
if hasattr(obj, 'date_action_last'):
write['date_action_last'] = time.strftime('%Y-%m-%d %H:%M:%S')
if hasattr(obj, 'state') and action.act_state:
obj.state = action.act_state
@ -135,7 +168,7 @@ class base_action_rule(osv.osv):
write['priority'] = action.act_priority
model_obj.write(cr, uid, [obj.id], write, context)
if hasattr(model_obj, 'remind_user') and action.act_remind_user:
model_obj.remind_user(cr, uid, [obj.id], context, attach=action.act_remind_attach)
if hasattr(model_obj, 'remind_partner') and action.act_remind_partner:
@ -146,7 +179,7 @@ class base_action_rule(osv.osv):
if hasattr(obj, 'user_id') and action.act_mail_to_user:
if obj.user_id and obj.user_id.address_id:
emails.append(obj.user_id.address_id.email)
if action.act_mail_to_watchers:
emails += (action.act_email_cc or '').split(',')
if action.act_mail_to_email:
@ -158,11 +191,19 @@ class base_action_rule(osv.osv):
return True
def _action(self, cr, uid, ids, objects, scrit=None, context={}):
""" Do Action
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param ids: List of Basic Action Rules IDs,
@param objects: pass objects
@param context: A standard dictionary for contextual values """
if not scrit:
scrit = []
rule_line_obj = self.pool.get('base.action.rule.line')
for rule in self.browse(cr, uid, ids):
level = rule.max_level
for rule in self.browse(cr, uid, ids):
level = rule.max_level
if not level:
break
newactions = []
@ -184,7 +225,8 @@ class base_action_rule(osv.osv):
base = mx.DateTime.strptime(obj.date_action_last, '%Y-%m-%d %H:%M:%S')
else:
base = mx.DateTime.strptime(obj.create_date[:19], '%Y-%m-%d %H:%M:%S')
elif hasattr(obj, 'date_deadline') and action.trg_date_type=='deadline' and obj.date_deadline:
elif hasattr(obj, 'date_deadline') and action.trg_date_type=='deadline' \
and obj.date_deadline:
base = mx.DateTime.strptime(obj.date_deadline, '%Y-%m-%d %H:%M:%S')
elif hasattr(obj, 'date') and action.trg_date_type=='date' and obj.date:
base = mx.DateTime.strptime(obj.date, '%Y-%m-%d %H:%M:%S')
@ -212,7 +254,7 @@ class base_action_rule(osv.osv):
if ok:
self.do_action(cr, uid, action, model_obj, obj, context)
break
break
level -= 1
return True
base_action_rule()
@ -222,20 +264,46 @@ class base_action_rule_line(osv.osv):
_description = 'Action Rule Lines'
def _state_get(self, cr, uid, context={}):
""" Get State
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param context: A standard dictionary for contextual values """
return self.state_get(cr, uid, context=context)
def _priority_get(self, cr, uid, context={}):
""" Get Priority
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param context: A standard dictionary for contextual values """
return self.priority_get(cr, uid, context=context)
def state_get(self, cr, uid, context={}):
""" Get State
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param context: A standard dictionary for contextual values """
return [('','')]
def priority_get(self, cr, uid, context={}):
""" Get Priority
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param context: A standard dictionary for contextual values """
return [('','')]
_columns = {
'name': fields.char('Rule Name',size=64, required=True),
'rule_id': fields.many2one('base.action.rule','Rule'),
'active': fields.boolean('Active', help="If the active field is set to true, it will allow you to hide the rule without removing it."),
'sequence': fields.integer('Sequence', help="Gives the sequence order when displaying a list of rules."),
'active': fields.boolean('Active', help="If the active field is set to true,\
it will allow you to hide the rule without removing it."),
'sequence': fields.integer('Sequence', help="Gives the sequence order when\
displaying a list of rules."),
'trg_date_type': fields.selection([
('none','None'),
@ -243,11 +311,13 @@ class base_action_rule_line(osv.osv):
('action_last','Last Action Date'),
('date','Date'),
], 'Trigger Date', size=16),
'trg_date_range': fields.integer('Delay after trigger date',help="Delay After Trigger Date, specifies you can put a negative number " \
"if you need a delay before the trigger date, like sending a reminder 15 minutes before a meeting."),
'trg_date_range_type': fields.selection([('minutes', 'Minutes'),('hour','Hours'),('day','Days'),('month','Months')], 'Delay type'),
'trg_date_range': fields.integer('Delay after trigger date',help="Delay After Trigger Date,\
specifies you can put a negative number " \
"if you need a delay before the trigger date, like sending a reminder 15 minutes before a meeting."),
'trg_date_range_type': fields.selection([('minutes', 'Minutes'),('hour','Hours'),\
('day','Days'),('month','Months')], 'Delay type'),
'trg_user_id': fields.many2one('res.users', 'Responsible'),
'trg_partner_id': fields.many2one('res.partner', 'Partner'),
@ -255,42 +325,59 @@ class base_action_rule_line(osv.osv):
'trg_state_from': fields.selection(_state_get, 'State', size=16),
'trg_state_to': fields.selection(_state_get, 'Button Pressed', size=16),
'trg_priority_from': fields.selection(_priority_get, 'Minimum Priority'),
'trg_priority_to': fields.selection(_priority_get, 'Maximum Priority'),
'act_method': fields.char('Call Object Method', size=64),
'act_user_id': fields.many2one('res.users', 'Set responsible to'),
'trg_priority_to': fields.selection(_priority_get, 'Maximum Priority'),
'act_method': fields.char('Call Object Method', size=64),
'act_user_id': fields.many2one('res.users', 'Set responsible to'),
'act_state': fields.selection(_state_get, 'Set state to', size=16),
'act_priority': fields.selection(_priority_get, 'Set priority to'),
'act_email_cc': fields.char('Add watchers (Cc)', size=250, help="These people will receive a copy of the future communication between partner and users by email"),
'act_priority': fields.selection(_priority_get, 'Set priority to'),
'act_email_cc': fields.char('Add watchers (Cc)', size=250, help="These people\
will receive a copy of the future communication between partner and users by email"),
'act_remind_partner': fields.boolean('Remind Partner', help="Check this if you want the rule to send a reminder by email to the partner."),
'act_remind_user': fields.boolean('Remind responsible', help="Check this if you want the rule to send a reminder by email to the user."),
'act_remind_partner': fields.boolean('Remind Partner', help="Check this if\
you want the rule to send a reminder by email to the partner."),
'act_remind_user': fields.boolean('Remind responsible', help="Check this if \
you want the rule to send a reminder by email to the user."),
'act_reply_to': fields.char('Reply-To', size=64),
'act_remind_attach': fields.boolean('Remind with attachment', help="Check this if you want that all documents attached to the object be attached to the reminder email sent."),
'act_remind_attach': fields.boolean('Remind with attachment', help="Check this if\
you want that all documents attached to the object be attached \
to the reminder email sent."),
'act_mail_to_user': fields.boolean('Mail to responsible',help="Check this if you want the rule to send an email to the responsible person."),
'act_mail_to_watchers': fields.boolean('Mail to watchers (CC)',help="Check this if you want the rule to mark CC(mail to any other person defined in actions)."),
'act_mail_to_email': fields.char('Mail to these emails', size=128,help="Email-id of the persons whom mail is to be sent"),
'act_mail_to_user': fields.boolean('Mail to responsible',help="Check this if \
you want the rule to send an email to the responsible person."),
'act_mail_to_watchers': fields.boolean('Mail to watchers (CC)',help="Check this\
if you want the rule to mark CC(mail to any other person\
defined in actions)."),
'act_mail_to_email': fields.char('Mail to these emails', size=128,help="Email-id \
of the persons whom mail is to be sent"),
'act_mail_body': fields.text('Mail body',help="Content of mail"),
'regex_name': fields.char('Regular Expression on Model Name', size=128),
'server_action_id': fields.many2one('ir.actions.server','Server Action',help="Describes the action name." \
"eg:on which object which action to be taken on basis of which condition"),
'server_action_id': fields.many2one('ir.actions.server','Server Action',help="Describes the\
action name." \
"eg:on which object which action to be taken on basis of which condition"),
}
_defaults = {
'active': lambda *a: 1,
'trg_date_type': lambda *a: 'none',
'trg_date_range_type': lambda *a: 'day',
'act_mail_to_user': lambda *a: 0,
'act_remind_partner': lambda *a: 0,
'act_remind_user': lambda *a: 0,
'act_remind_user': lambda *a: 0,
'act_mail_to_watchers': lambda *a: 0,
}
_order = 'sequence'
_order = 'sequence'
def _check_mail(self, cr, uid, ids, context=None):
""" Check Mail
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param ids: List of Action Rules IDs
@param context: A standard dictionary for contextual values """
empty = orm.browse_null()
rule_obj = self.pool.get('base.action.rule')
for rule in self.browse(cr, uid, ids):
@ -300,10 +387,11 @@ class base_action_rule_line(osv.osv):
except (ValueError, KeyError, TypeError):
return False
return True
_constraints = [
(_check_mail, 'Error: The mail is not well formated', ['act_mail_body']),
]
base_action_rule_line()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -1,10 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<menuitem id="menu_base_action_rule" name="Action Rule" parent="base.menu_base_config" sequence="0"/>
<menuitem id="menu_base_action_rule" name="Action Rule"
parent="base.menu_base_config" sequence="0" />
<!--
Action Rule
<!--
Action Rule Form View
-->
<record id="view_base_action_rule_form" model="ir.ui.view">
<field name="name">base.action.rule.form</field>
@ -21,6 +22,8 @@
</field>
</record>
<!-- Action Rule Tree View -->
<record id="view_base_action_rule_tree" model="ir.ui.view">
<field name="name">base.action.rule.tree</field>
<field name="model">base.action.rule</field>
@ -34,6 +37,8 @@
</field>
</record>
<!-- Action Rule Action -->
<record id="base_action_rule_act" model="ir.actions.act_window">
<field name="name">Action Rules</field>
<field name="res_model">base.action.rule</field>
@ -42,10 +47,11 @@
<field name="view_id" ref="view_base_action_rule_tree"/>
</record>
<menuitem id="menu_base_action_rule_form" parent="menu_base_action_rule" action="base_action_rule_act"/>
<menuitem id="menu_base_action_rule_form"
parent="menu_base_action_rule" action="base_action_rule_act" />
<!--
Action Rule Lines
<!--
Action Rule Lines Form View
-->
<record id="view_base_action_rule_line_form" model="ir.ui.view">
<field name="name">base.action.rule.line.form</field>
@ -127,6 +133,9 @@
</form>
</field>
</record>
<!-- Action Rule Lines Tree View -->
<record id="view_base_action_rule_line_tree" model="ir.ui.view">
<field name="name">base.action.rule.line.tree</field>
<field name="model">base.action.rule.line</field>

0
addons/base_calendar/__init__.py Normal file → Executable file
View File

47
addons/base_calendar/__terp__.py Normal file → Executable file
View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
##############################################################################
#
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
#
@ -15,36 +15,33 @@
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
{
"name" : "Basic Calendar Functionality",
"version" : "1.0",
"depends" : [
"base",
],
'description': """
Full featured calendar system that support:
- Alerts (create requests)
- Recurring events (*)
- Invitations to other people
""",
"author" : "Tiny",
'category': 'Generic Modules/Others',
'website': 'http://www.openerp.com',
"name" : "Basic Calendar Functionality",
"version" : "1.0",
"depends" : ["base"],
'description': """Full featured calendar system that support:
- Alerts (create requests)
- Recurring events (*)
- Invitations to others people""",
"author" : "Tiny",
'category': 'Generic Modules/Others',
'website': 'http://www.openerp.com',
"init_xml" : [
'base_calendar_data.xml'
],
"demo_xml" : [],
'base_calendar_data.xml'
],
"demo_xml" : [],
"update_xml" : [
'security/ir.model.access.csv',
'base_calendar_view.xml'
],
"installable" : True,
"active" : False,
'security/ir.model.access.csv',
'wizard/calendar_event_edit_all_view.xml',
'wizard/base_calendar_invite_attendee_view.xml',
'base_calendar_view.xml'
],
"installable" : True,
"active" : False,
}
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

File diff suppressed because it is too large Load Diff

0
addons/base_calendar/base_calendar_data.xml Normal file → Executable file
View File

216
addons/base_calendar/base_calendar_view.xml Normal file → Executable file
View File

@ -1,141 +1,112 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<!-- Attendee invite wizard-->
<record id="view_calendar_invite_attendee_wizard" model="ir.ui.view">
<field name="name">Invite Attendees</field>
<field name="model">base_calendar.invite.attendee</field>
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Invite People">
<field name="type" />
<field name="send_mail" />
<newline/>
<group col="1" colspan="4"
attrs="{'invisible': [('type', '!=', 'external')]}">
<field name="email" colspan="4"
attrs="{'required': [('type', '=', 'external')]}" />
</group>
<group col="1" colspan="4"
attrs="{'invisible': [('type', '!=', 'internal')]}">
<separator string="Users" colspan="4" />
<field name="user_ids" select="1" colspan="4"
nolabel="1" />
<newline />
</group>
<group col="2" colspan="4"
attrs="{'invisible': [('type', '!=', 'partner')]}">
<field name="partner_id" colspan="2"
on_change="onchange_partner_id(partner_id)"
attrs="{'required': [('type', '=', 'partner')]}" />
<newline />
<separator string="Partner Contacts"
colspan="4" />
<field name="contact_ids" select="1" colspan="4"
nolabel="1" domain="[('partner_id', '=', partner_id)]"
attrs="{'readonly': [('type', '!=', 'partner')]}" />
</group>
<newline/>
<separator string="" colspan="6" />
<label string="" colspan="2" />
<button icon='gtk-cancel' special="cancel"
string="Cancel" />
<button name="do_invite" string="Invite"
type="object" icon="gtk-ok" />
</form>
</field>
</record>
<record id="action_view_calendar_invite_attendee_wizard" model="ir.actions.act_window">
<field name="name">Invite Attendees</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">base_calendar.invite.attendee</field>
<field name="view_type">form</field>
<field name="view_mode">form</field>
<field name="target">new</field>
</record>
<record id="base_calendar_attendee_form_view" model="ir.ui.view">
<field name="name">calendar.attendee.form</field>
<field name="model">calendar.attendee</field>
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Invitation details">
<field name="email" string="Invitation To"/>
<field name="sent_by_uid" string="Invitation From"/>
<notebook colspan="4">
<page string="Invitation">
<form string="Invitation details">
<field name="email" string="Invitation To"/>
<field name="sent_by_uid" string="Invitation From" />
<notebook colspan="4">
<page string="Invitation">
<separator string="Invitation Detail" colspan="4" />
<group colspan="4" col="4">
<field name="user_id" string="Invited User"/>
<newline/>
<field name="partner_address_id" string="Partner Contact"/>
<field name="partner_id" string="Partner" readonly="1"/>
<field name="partner_address_id"
string="Partner Contact" />
<field name="partner_id"
string="Partner" readonly="1" />
<field name="role" string="Role" />
<field name="cutype" string="Invitation type" />
<field name="rsvp" />
<field name="rsvp" />
</group>
<separator string="Event Detail" colspan="4" />
<group colspan="4" col="4">
<field name="event_date" />
<field name="event_end_date" />
<field name="language"/>
<field name="ref" colspan="4"/>
<field name="ref" colspan="4" readonly="1"/>
</group>
</page>
<page string="Delegation Info">
<separator string="Delegated From" colspan="4" />
<field name="parent_ids" nolabel="1"
colspan="4" readonly="1" />
<separator string="Delegated To" colspan="4" />
<field name="child_ids" nolabel="1"
colspan="4" readonly="1" />
</page>
</notebook>
<group col="6" colspan="4">
<field name="state" select="2" />
<button name="do_tentative" states="needs-action,declined,accepted"
string="Uncertain" type="object"
icon="terp-crm" />
<button name="do_accept" string="Accept" states="needs-action,tentative,declined"
type="object" icon="gtk-apply" />
<button name="do_decline" string="Decline" states="needs-action,tentative,accepted"
type="object" icon="gtk-cancel" />
<button
name="%(base_calendar.action_view_calendar_invite_attendee_wizard)d"
string="Delegate" type="action"
icon="gtk-sort-descending" states="needs-action,tentative,declined,accepted"
context="{'model' : 'calendar.attendee', 'attendee_field' : 'child_ids'}" />
</group>
</form>
</page>
<page string="Delegation Info">
<separator string="Delegated From" colspan="4" />
<field name="parent_ids" nolabel="1"
colspan="4" readonly="1" />
<separator string="Delegated To" colspan="4" />
<field name="child_ids" nolabel="1"
colspan="4" readonly="1" />
</page>
</notebook>
<group col="6" colspan="4">
<field name="state" />
<button name="do_tentative"
states="needs-action,declined,accepted"
string="Uncertain" type="object"
icon="terp-crm" />
<button name="do_accept" string="Accept"
states="needs-action,tentative,declined"
type="object" icon="gtk-apply" />
<button name="do_decline" string="Decline"
states="needs-action,tentative,accepted"
type="object" icon="gtk-cancel" />
<button
name="%(base_calendar.action_view_calendar_invite_attendee_wizard)d"
string="Delegate" type="action"
icon="gtk-sort-descending"
states="needs-action,tentative,declined,accepted"
context="{'model' : 'calendar.attendee', 'attendee_field' : 'child_ids'}" />
</group>
</form>
</field>
</record>
<record id="base_calendar_attendee_tree_view" model="ir.ui.view">
<record id="base_calendar_attendee_tree_view" model="ir.ui.view">
<field name="name">calendar.attendee.tree</field>
<field name="model">calendar.attendee</field>
<field name="type">tree</field>
<field name="arch" type="xml">
<tree string="Invitation details">
<field name="email" string="Invitation To"/>
<tree string="Invitation details">
<field name="email" string="Invitation To"/>
<field name="partner_id" string="Partner" />
<field name="partner_address_id" string="Contact" />
<field name="role" />
<field name="state" />
</tree>
<field name="role" />
<field name="state" />
</tree>
</field>
</record>
</record>
<record id="base_calendar_attendee_search_view" model="ir.ui.view">
<field name="name">calendar.attendee.search</field>
<field name="model">calendar.attendee</field>
<field name="type">search</field>
<field name="arch" type="xml">
<search string="Search Invitations">
<filter icon="gtk-apply" string="Accepted"
domain="[('state','=', 'accepted')]"
separator="1" help="Accepted Invitations" />
<filter icon="gtk-close" string="Declined"
domain="[('state','=', 'declined')]"
separator="1" help="Declined Invitations" />
<separator orientation="vertical"/>
<field name="email" select='1'/>
<field name="sent_by_uid" select="1" widget="selection"/>
<separator orientation="vertical"/>
<field name="cutype" string="Invitation type" select="1"/>
<field name="event_date" select="1"/>
</search>
</field>
</record>
<record id="res_alarm_form_view" model="ir.ui.view">
<field name="name">res.alarm.form</field>
<field name="model">res.alarm</field>
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Reminder details">
<form string="Reminder details">
<field name="name" />
<field name="active" />
<separator string="Reminder Details" colspan="4" />
@ -146,7 +117,7 @@
<separator string="" colspan="4" />
<field name="duration" />
<field name="repeat" />
</form>
</form>
</field>
</record>
@ -154,34 +125,31 @@
<field name="name">res.alarm.tree</field>
<field name="model">res.alarm</field>
<field name="type">tree</field>
<field name="arch" type="xml">
<tree string="Reminder details">
<field name="name" select="1"/>
<field name="trigger_interval" select="1"/>
<field name="trigger_duration" select="1"/>
<field name="trigger_occurs" select="1"/>
<field name="trigger_related" select="1"/>
</tree>
<field name="arch" type="xml">
<tree string="Reminder details">
<field name="name" select="1"/>
<field name="trigger_interval" select="1"/>
<field name="trigger_duration" select="1"/>
<field name="trigger_occurs" select="1"/>
<field name="trigger_related" select="1"/>
</tree>
</field>
</record>
<record id="action_res_alarm_view" model="ir.actions.act_window">
<field name="name">Available Alarms</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">res.alarm</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
<field name="name">Available Alarms</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">res.alarm</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
</record>
<!-- Available alarms-->
<menuitem id="base.menu_calendar_configuration" name="Calendar"
parent="base.menu_base_config" sequence="10" />
<!--Available alarms-->
<menuitem id="base.menu_calendar_configuration" name="Calendar"
parent="base.menu_base_config" sequence="10" />
<menuitem name="Available Alarms" id="menu_crm_meeting_avail_alarm"
action="base_calendar.action_res_alarm_view"
parent="base.menu_calendar_configuration" />
<menuitem name="Available Alarms" id="menu_crm_meeting_avail_alarm"
action="base_calendar.action_res_alarm_view"
parent="base.menu_calendar_configuration" />
</data>
</openerp>

1
addons/base_calendar/security/ir.model.access.csv Normal file → Executable file
View File

@ -5,3 +5,4 @@
"access_calendar_event_all","calendar.event","model_calendar_event",,1,1,1,1
"access_calendar_todo","calendar.todo","model_calendar_todo",,1,1,1,1
"access_base_calendar_invite_attendee","base_calendar.invite.attendee","model_base_calendar_invite_attendee",,1,1,1,1
"access_calendar_event_edit_all","calendar_event_edit_all","model_calendar_event_edit_all",,1,1,1,1

1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
5 access_calendar_event_all calendar.event model_calendar_event 1 1 1 1
6 access_calendar_todo calendar.todo model_calendar_todo 1 1 1 1
7 access_base_calendar_invite_attendee base_calendar.invite.attendee model_base_calendar_invite_attendee 1 1 1 1
8 access_calendar_event_edit_all calendar_event_edit_all model_calendar_event_edit_all 1 1 1 1

4
addons/base_calendar/wizard/__init__.py Normal file → Executable file
View File

@ -18,7 +18,9 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
import wizard_cal_edit_event
import calendar_event_edit_all
import base_calendar_invite_attendee
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -0,0 +1,170 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from base_calendar import base_calendar
from osv import fields, osv
from tools.translate import _
import tools
class base_calendar_invite_attendee(osv.osv_memory):
"""
Invite attendee.
"""
_name = "base_calendar.invite.attendee"
_description = "Invite Attendees"
_columns = {
'type': fields.selection([('internal', 'Internal User'), \
('external', 'External Email'), \
('partner', 'Partner Contacts')], 'Type', required=True),
'user_ids': fields.many2many('res.users', 'invite_user_rel',
'invite_id', 'user_id', 'Users'),
'partner_id': fields.many2one('res.partner', 'Partner'),
'email': fields.char('Email', size=124),
'contact_ids': fields.many2many('res.partner.address', 'invite_contact_rel',
'invite_id', 'contact_id', 'Contacts'),
'send_mail': fields.boolean('Send mail?', help='Check this if you want to \
send an Email to Invited Person')
}
_defaults = {
'type': lambda *x: 'internal'
}
def do_invite(self, cr, uid, ids, context=None):
"""
Invites attendee for meeting..
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param ids: List of base calendar invite attendees IDs.
@param context: A standard dictionary for contextual values
@return: Dictionary of {}.
"""
if not context:
context = {}
model = False
model_field = False
context_id = context and context.get('active_id', False) or False
if not context or not context.get('model'):
return {}
else:
model = context.get('model')
model_field = context.get('attendee_field', False)
for datas in self.read(cr, uid, ids, context=context):
obj = self.pool.get(model)
res_obj = obj.browse(cr, uid, context_id)
type = datas.get('type')
att_obj = self.pool.get('calendar.attendee')
vals = []
mail_to = []
attendees = []
ref = {}
if not model == 'calendar.attendee':
if context_id:
ref = {'ref': '%s,%s' % (model, base_calendar.base_calendar_id2real_id(context_id))}
else:
return {}
if type == 'internal':
user_obj = self.pool.get('res.users')
if not datas.get('user_ids'):
raise osv.except_osv(_('Error!'), ("Please select any User"))
for user_id in datas.get('user_ids'):
user = user_obj.browse(cr, uid, user_id)
res = {
'user_id': user_id,
'email': user.address_id.email
}
res.update(ref)
vals.append(res)
if user.address_id.email:
mail_to.append(user.address_id.email)
elif type == 'external' and datas.get('email'):
res = {'email': datas['email']}
res.update(ref)
vals.append(res)
mail_to.append(datas['email'])
elif type == 'partner':
add_obj = self.pool.get('res.partner.address')
for contact in add_obj.browse(cr, uid, datas['contact_ids']):
res = {
'partner_address_id': contact.id,
'email': contact.email
}
res.update(ref)
vals.append(res)
if contact.email:
mail_to.append(contact.email)
att = att_obj.browse(cr, uid, context_id)
for att_val in vals:
if model == 'calendar.attendee':
if ref:
att_val.update({
'parent_ids': [(4, att.id)],
'ref': att.ref._name + ',' +str(att.ref.id)
})
attendees.append(att_obj.create(cr, uid, att_val))
if model_field:
for attendee in attendees:
obj.write(cr, uid, res_obj.id, {model_field: [(4, attendee)]})
if datas.get('send_mail'):
if not mail_to:
name = map(lambda x: x[1], filter(lambda x: type==x[0], \
self._columns['type'].selection))
raise osv.except_osv(_('Error!'), ("%s must have an email Address to send mail") %(name[0]))
att_obj._send_mail(cr, uid, attendees, mail_to, \
email_from= tools.config.get('email_from', False))
return {}
def onchange_partner_id(self, cr, uid, ids, partner_id, *args, **argv):
"""
Make entry on contact_ids on change of partner_id field.
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param ids: List of base calendar invite attendees IDs.
@param partner_id: id of Partner
@return: dictionary of value.
"""
if not partner_id:
return {'value': {'contact_ids': []}}
cr.execute('select id from res_partner_address \
where partner_id=%s' % (partner_id))
contacts = map(lambda x: x[0], cr.fetchall())
return {'value': {'contact_ids': contacts}}
base_calendar_invite_attendee()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -0,0 +1,64 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<!-- Attendee invite wizard-->
<record id="view_calendar_invite_attendee_wizard"
model="ir.ui.view">
<field name="name">Invite Attendees</field>
<field name="model">base_calendar.invite.attendee</field>
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Invite People">
<field name="type" />
<field name="send_mail" />
<newline />
<group col="1" colspan="4"
attrs="{'invisible': [('type', '!=', 'external')]}">
<field name="email" colspan="4"
attrs="{'required': [('type', '=', 'external')]}" />
</group>
<group col="1" colspan="4"
attrs="{'invisible': [('type', '!=', 'internal')]}">
<separator string="Users" colspan="4" />
<field name="user_ids" select="1" colspan="4"
nolabel="1" />
<newline />
</group>
<group col="2" colspan="4"
attrs="{'invisible': [('type', '!=', 'partner')]}">
<field name="partner_id" colspan="2"
on_change="onchange_partner_id(partner_id)"
attrs="{'required': [('type', '=', 'partner')]}" />
<newline />
<separator string="Partner Contacts"
colspan="4" />
<field name="contact_ids" select="1" colspan="4"
nolabel="1" domain="[('partner_id', '=', partner_id)]"
attrs="{'readonly': [('type', '!=', 'partner')]}" />
</group>
<newline />
<separator string="" colspan="6" />
<label string="" colspan="2" />
<button icon='gtk-cancel' special="cancel"
string="Cancel" />
<button name="do_invite" string="Invite"
type="object" icon="gtk-ok" />
</form>
</field>
</record>
<!-- Attendee invite action-->
<record id="action_view_calendar_invite_attendee_wizard" model="ir.actions.act_window">
<field name="name">Invite Attendees</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">base_calendar.invite.attendee</field>
<field name="view_type">form</field>
<field name="view_mode">form</field>
<field name="target">new</field>
</record>
</data>
</openerp>

View File

@ -0,0 +1,98 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from osv import osv
from osv import fields
class calendar_event_edit_all(osv.osv_memory):
def _default_values(self, cr, uid, context={}):
""" Get Default value for Start Date
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param context: A standard dictionary for contextual values
@Return: Get Default value for Start Date
"""
context_id = context and context.get('active_id', False) or False
if context_id:
if context.get('date'):
return context.get('date')
else:
model = context.get('model', False)
model_obj = self.pool.get(model)
event = model_obj.read(cr, uid, context_id, ['name', 'location', 'alarm_id'])
return event['date']
def _default_deadline(self, cr, uid, context={}):
""" Get Default value for End Date
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param context: A standard dictionary for contextual values
@return: Get Default value for End Date
"""
context_id = context and context.get('active_id', False) or False
if context_id:
if context.get('date_deadline'):
return context.get('date_deadline')
else:
model = context.get('model', False)
model_obj = self.pool.get(model)
event = model_obj.read(cr, uid, context_id, ['name', 'location', 'alarm_id'])
return event['date_deadline']
def modify_this(self, cr, uid, ids, context=None):
"""
Modify All event for Crm Meeting.
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param ids: List of calendar event edit alls IDs
@return: dictionary {}
"""
if not context:
context = {}
context_id = context and context.get('active_id', False) or False
if context_id:
for datas in self.read(cr, uid, ids):
model = context.get('model', False)
model_obj = self.pool.get(model)
model_obj.modify_all(cr, uid, [context_id], datas, context=context)
return {}
_name = "calendar.event.edit.all"
_description = "Calendar Edit all event"
_columns = {
'name': fields.char('Title', size=64, required=True),
'date': fields.datetime('Start Date', required=True),
'date_deadline': fields.datetime('End Date', required=True),
'location': fields.char('Location', size=124),
'alarm_id': fields.many2one('res.alarm', 'Reminder'),
}
_defaults = {
'date': _default_values,
'date_deadline': _default_deadline
}
calendar_event_edit_all()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -0,0 +1,41 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<record id="view_calendar_event_edit_all" model="ir.ui.view">
<field name="name">calendar.event.edit.all.form</field>
<field name="model">calendar.event.edit.all</field>
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Edit all Occurrences">
<group colspan="4" >
<separator string="" colspan="4" />
<newline/>
<field name='name' colspan="4" />
<newline />
<field name='location' colspan="4" />
<newline />
<field name='date' />
<field name='date_deadline' />
<newline />
<field name='alarm_id'/>
</group>
<separator string="" colspan="4" />
<group colspan="4" col="6">
<button icon="gtk-cancel" special="cancel" string="Cancel"/>
<button icon="gtk-save" string="_Save" name="modify_this" type="object"/>
</group>
</form>
</field>
</record>
<record id="action_calendar_event_edit_all" model="ir.actions.act_window">
<field name="name">Edit all events</field>
<field name="res_model">calendar.event.edit.all</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
<field name="view_id" ref="view_calendar_event_edit_all"/>
<field name="target">new</field>
</record>
</data>
</openerp>

View File

@ -1,78 +0,0 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
import wizard
import pooler
class event_edit_all(wizard.interface):
event_form = """<?xml version="1.0"?>
<form string="Edit all Occurrences">
<separator string="" colspan="4" />
<newline />
<field name='name' colspan="4" />
<newline />
<field name='location' colspan="4" />
<newline />
<field name='date' />
<field name='date_deadline' />
<newline />
<field name='alarm_id'/>
</form>"""
event_fields = {
'name': {'string': 'Title', 'type': 'char', 'size': 64, 'required': True},
'date': {'string': 'Start Date', 'type': 'datetime', 'required': True},
'date_deadline': {'string': 'End Date', 'type': 'datetime', 'required': True},
'location': {'string': 'Location', 'type': 'char', 'size': 124},
'alarm_id': {'string': 'Reminder', 'type': 'many2one', 'relation': 'res.alarm'},
}
def _default_values(self, cr, uid, data, context=None):
model = data.get('model')
model_obj = pooler.get_pool(cr.dbname).get(model)
event = model_obj.read(cr, uid, data['id'], ['name', 'location', 'alarm_id'])
event.update({
'date': context.get('date'),
'date_deadline': context.get('date_deadline')
})
return event
def _modify_all(self, cr, uid, datas, context=None):
model = datas.get('model')
model_obj = pooler.get_pool(cr.dbname).get(model)
model_obj.modify_all(cr, uid, datas['id'], datas['form'], context)
return {}
states = {
'init': {
'actions': [_default_values],
'result': {'type': 'form', 'arch': event_form, 'fields': event_fields,
'state': [('end', 'Cancel', 'gtk-cancel'), ('edit', '_Save', 'gtk-save')]}
},
'edit': {
'actions': [],
'result': {'type': 'action', 'action': _modify_all, 'state': 'end'}
}
}
event_edit_all('calendar.event.edit.all')
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
##############################################################################
#
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
#
@ -15,7 +15,7 @@
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
@ -23,16 +23,19 @@ import netsvc
from osv import fields, osv
class res_partner_contact(osv.osv):
""" Partner Contact """
_name = "res.partner.contact"
_description = "res.partner.contact"
# def init(self, cr):
# address_obj = self.pool.get('res.partner.address')
# job_obj = self.pool.get('res.partner.job')
# address_ids = address_obj.search(cr, 1, [])
# for address in address_obj.browse(cr, 1, address_ids):
# contact_id = self.create(cr, 1, {'name': address.name or 't'})
def _title_get(self,cr, user, context={}):
"""
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param user: the current user,
@param context: A standard dictionary for contextual values
"""
obj = self.pool.get('res.partner.title')
ids = obj.search(cr, user, [])
res = obj.read(cr, user, ids, ['shortcut', 'name','domain'], context)
@ -40,6 +43,16 @@ class res_partner_contact(osv.osv):
return res
def _main_job(self, cr, uid, ids, fields, arg, context=None):
"""
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param ids: List of partner contacts IDs
@fields: Get Fields
@param context: A standard dictionary for contextual values
@param arg: list of tuples of form [(name_of_the_field, operator, value), ...]. """
res = dict.fromkeys(ids, False)
for contact in self.browse(cr, uid, ids, context):
if contact.job_ids:
@ -49,30 +62,43 @@ class res_partner_contact(osv.osv):
_columns = {
'name': fields.char('Last Name', size=30,required=True),
'first_name': fields.char('First Name', size=30),
'mobile':fields.char('Mobile',size=30),
'mobile': fields.char('Mobile',size=30),
'title': fields.selection(_title_get, 'Title'),
'website':fields.char('Website',size=120),
'lang_id':fields.many2one('res.lang','Language'),
'job_ids':fields.one2many('res.partner.job','contact_id','Functions and Addresses'),
'country_id':fields.many2one('res.country','Nationality'),
'birthdate':fields.date('Birth Date'),
'active' : fields.boolean('Active', help="If the active field is set to true, it will allow you to hide the partner contact without removing it."),
'partner_id':fields.related('job_ids','address_id','partner_id',type='many2one', relation='res.partner', string='Main Employer'),
'function_id':fields.related('job_ids','function_id',type='many2one', relation='res.partner.function', string='Main Function'),
'job_id': fields.function(_main_job, method=True, type='many2one', relation='res.partner.job', string='Main Job'),
'website': fields.char('Website',size=120),
'lang_id': fields.many2one('res.lang','Language'),
'job_ids': fields.one2many('res.partner.job','contact_id','Functions and Addresses'),
'country_id': fields.many2one('res.country','Nationality'),
'birthdate': fields.date('Birth Date'),
'active': fields.boolean('Active', help="If the active field is set to true,\
it will allow you to hide the partner contact without removing it."),
'partner_id': fields.related('job_ids','address_id','partner_id',type='many2one',\
relation='res.partner', string='Main Employer'),
'function_id': fields.related('job_ids','function_id',type='many2one', \
relation='res.partner.function', string='Main Function'),
'job_id': fields.function(_main_job, method=True, type='many2one',\
relation='res.partner.job', string='Main Job'),
'email': fields.char('E-Mail', size=240),
'comment' : fields.text('Notes', translate=True),
'photo' : fields.binary('Image'),
'comment': fields.text('Notes', translate=True),
'photo': fields.binary('Image'),
}
_defaults = {
'active' : lambda *a: True,
}
_order = "name,first_name"
def name_get(self, cr, user, ids, context={}):
#will return name and first_name.......
""" will return name and first_name.......
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param user: the current users ID for security checks,
@param ids: List of create menus IDs
@return: name and first_name
@param context: A standard dictionary for contextual values
"""
if not len(ids):
return []
res = []
@ -84,60 +110,93 @@ class res_partner_contact(osv.osv):
addr += (r.get('first_name', '') or '')
res.append((r['id'], addr))
return res
res_partner_contact()
class res_partner_address(osv.osv):
def search(self, cr, user, args, offset=0, limit=None, order=None,
context=None, count=False):
""" search parnter address
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param user: the current user
@param args: list of tuples of form [(name_of_the_field, operator, value), ...].
@param offset: The Number of Results to Pass
@param limit: The Number of Results to Return
@param context: A standard dictionary for contextual values
"""
if context and context.has_key('address_partner_id' ) and context['address_partner_id']:
args.append(('partner_id', '=', context['address_partner_id']))
return super(res_partner_address, self).search(cr, user, args, offset, limit, order, context, count)
#overriding of the name_get defined in base in order to remove the old contact name
def name_get(self, cr, user, ids, context={}):
"""
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param user: the current user,
@param ids: List of partner addresss IDs
@param context: A standard dictionary for contextual values
"""
if not len(ids):
return []
res = []
for r in self.read(cr, user, ids, ['zip','city','partner_id', 'street']):
for r in self.read(cr, user, ids, ['zip', 'city', 'partner_id', 'street']):
if context.get('contact_display', 'contact')=='partner' and r['partner_id']:
res.append((r['id'], r['partner_id'][1]))
else:
addr = str('')
addr += "%s %s %s" % ( r.get('street', '') or '', r.get('zip', '') or '', r.get('city', '') or '' )
addr += "%s %s %s" % (r.get('street', '') or '', r.get('zip', '') \
or '', r.get('city', '') or '')
res.append((r['id'], addr.strip() or '/'))
return res
_name = 'res.partner.address'
_inherit='res.partner.address'
_inherit = 'res.partner.address'
_description ='Partner Address'
_columns = {
'job_id':fields.related('job_ids','contact_id','job_id',type='many2one', relation='res.partner.job', string='Main Job'),
'job_ids':fields.one2many('res.partner.job', 'address_id', 'Contacts'),
'job_id': fields.related('job_ids','contact_id','job_id',type='many2one',\
relation='res.partner.job', string='Main Job'),
'job_ids': fields.one2many('res.partner.job', 'address_id', 'Contacts'),
}
res_partner_address()
class res_partner_job(osv.osv):
# def init(self, cr):
# address_obj = self.pool.get('res.partner.address')
# contact_obj = self.pool.get('res.partner.contact')
# address_ids = address_obj.search(cr, 1, [])
# for address in address_obj.browse(cr, 1, address_ids):
# contact_id = contact_obj.search(cr, 1, [('name','=', address.name)])
# if contact_id:
# contact_id = contact_id[0]
# self.create(cr, 1, {'address_id': address.id, 'contact_id': contact_id})
def name_get(self, cr, uid, ids, context={}):
"""
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param user: the current user,
@param ids: List of partner addresss IDs
@param context: A standard dictionary for contextual values
"""
if not len(ids):
return []
res = []
for r in self.browse(cr, uid, ids):
funct = r.function_id and (", " + r.function_id.name) or ""
res.append((r.id, self.pool.get('res.partner.contact').name_get(cr, uid, [r.contact_id.id])[0][1] + funct))
res.append((r.id, self.pool.get('res.partner.contact').name_get(cr, uid, \
[r.contact_id.id])[0][1] + funct))
return res
def search(self, cr, user, args, offset=0, limit=None, order=None, context=None, count=False):
""" search parnter job
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param user: the current user
@param args: list of tuples of form [(name_of_the_field, operator, value), ...].
@param offset: The Number of Results to Pass
@param limit: The Number of Results to Return
@param context: A standard dictionary for contextual values
"""
job_ids = []
for arg in args:
if arg[0] == 'address_id':
@ -147,12 +206,14 @@ class res_partner_job(osv.osv):
contact_obj = self.pool.get('res.partner.contact')
search_arg = ['|', ('first_name', 'ilike', arg[2]), ('name', 'ilike', arg[2])]
contact_ids = contact_obj.search(cr, user, search_arg, offset=offset, limit=limit, order=order, context=context, count=count)
contact_ids = contact_obj.search(cr, user, search_arg, offset=offset, \
limit=limit, order=order, context=context, count=count)
contacts = contact_obj.browse(cr, user, contact_ids, context=context)
for contact in contacts:
job_ids.extend([item.id for item in contact.job_ids])
res = super(res_partner_job,self).search(cr, user, args, offset=offset, limit=limit, order=order, context=context, count=count)
res = super(res_partner_job,self).search(cr, user, args, offset=offset,\
limit=limit, order=order, context=context, count=count)
if job_ids:
res = list(set(res + job_ids))
@ -161,27 +222,36 @@ class res_partner_job(osv.osv):
_name = 'res.partner.job'
_description ='Contact Partner Function'
_order = 'sequence_contact'
_columns = {
'name': fields.related('address_id','partner_id', type='many2one', relation='res.partner', string='Partner', help="You may enter Address first,Partner will be linked automatically if any."),
'address_id':fields.many2one('res.partner.address','Address', help='Address which is linked to the Partner'),
'contact_id':fields.many2one('res.partner.contact','Contact', required=True, ondelete='cascade'),
'function_id': fields.many2one('res.partner.function','Partner Function', help="Function of this contact with this partner"),
'sequence_contact':fields.integer('Contact Seq.',help='Order of importance of this address in the list of addresses of the linked contact'),
'sequence_partner':fields.integer('Partner Seq.',help='Order of importance of this job title in the list of job title of the linked partner'),
'name': fields.related('address_id','partner_id', type='many2one',\
relation='res.partner', string='Partner', help="You may\
enter Address first,Partner will be linked automatically if any."),
'address_id': fields.many2one('res.partner.address','Address', \
help='Address which is linked to the Partner'),
'contact_id': fields.many2one('res.partner.contact','Contact', required=True, ondelete='cascade'),
'function_id': fields.many2one('res.partner.function','Partner Function', \
help="Function of this contact with this partner"),
'sequence_contact': fields.integer('Contact Seq.',help='Order of\
importance of this address in the list of addresses of the linked contact'),
'sequence_partner': fields.integer('Partner Seq.',help='Order of importance\
of this job title in the list of job title of the linked partner'),
'email': fields.char('E-Mail', size=240, help="Job E-Mail"),
'phone': fields.char('Phone', size=64, help="Job Phone no."),
'fax': fields.char('Fax', size=64, help="Job FAX no."),
'extension': fields.char('Extension', size=64, help='Internal/External extension phone number'),
'other': fields.char('Other', size=64, help='Additional phone field'),
'date_start' : fields.date('Date Start',help="Start date of job(Joining Date)"),
'date_stop' : fields.date('Date Stop', help="Last date of job"),
'state' : fields.selection([('past', 'Past'),('current', 'Current')], 'State', required=True, help="Status of Address"),
'date_start': fields.date('Date Start',help="Start date of job(Joining Date)"),
'date_stop': fields.date('Date Stop', help="Last date of job"),
'state': fields.selection([('past', 'Past'),('current', 'Current')], \
'State', required=True, help="Status of Address"),
}
_defaults = {
'sequence_contact' : lambda *a: 0,
'state' : lambda *a: 'current',
'state': lambda *a: 'current',
}
res_partner_job()

View File

@ -2,7 +2,8 @@
<openerp>
<data>
<!-- Views for Contacts -->
<!-- Views for Contacts Tree View -->
<record model="ir.ui.view" id="view_partner_contact_tree">
<field name="name">res.partner.contact.tree</field>
<field name="model">res.partner.contact</field>
@ -20,6 +21,8 @@
</field>
</record>
<!-- Views for Contacts Form View -->
<record model="ir.ui.view" id="view_partner_contact_form">
<field name="name">res.partner.contact.form</field>
<field name="model">res.partner.contact</field>
@ -93,6 +96,8 @@
</field>
</record>
<!-- Views for Contacts Search View -->
<record model="ir.ui.view" id="view_partner_contact_search">
<field name="name">res.partner.contact.search</field>
<field name="model">res.partner.contact</field>
@ -106,6 +111,8 @@
</field>
</record>
<!-- Views for Contacts Action -->
<record model="ir.actions.act_window" id="action_partner_contact_form">
<field name="name">Contacts</field>
<field name="res_model">res.partner.contact</field>
@ -116,7 +123,7 @@
</record>
<menuitem name="Contacts" id="menu_partner_contact_form" action="action_partner_contact_form" parent = "base.menu_address_book" sequence="2"/>
<!-- Views for Partners -->
<!-- Views for Partners Form View -->
<record model="ir.ui.view" id="view_partner_form_inherit">
<field name="name">Partner form inherited</field>
@ -186,6 +193,7 @@
<!-- don't display the categories, since it is displayed in an other tab-->
<record model="ir.ui.view" id="view_partner_form_inherit2">
<field name="name">res.partner.form</field>
<field name="type">form</field>
@ -198,6 +206,7 @@
</record>
<!-- don't display the categories label-->
<record model="ir.ui.view" id="view_partner_form_inherit2bis">
<field name="name">res.partner.form</field>
<field name="type">form</field>
@ -225,6 +234,7 @@
<!-- Views for Addresses -->
<record model="ir.ui.view" id="view_partner_address_tree_inherited1">
<field name="name">Partner addresses inherited</field>
<field name="model">res.partner.address</field>
@ -306,39 +316,6 @@
</field>
</record>
<!--
<record id='view_partner_address_form_inherited5' model='ir.ui.view'>
<field name='name'>res.partner.address.form.inherited5</field>
<field name='model'>res.partner.address</field>
<field name="inherit_id" ref="base.view_partner_address_form1"/>
<field name='type'>form</field>
<field name='arch' type='xml'>
<group string="Communication" colspan="2" col="2" position="after">
<newline/>
<field name="job_ids" mode="tree,form" colspan="4">
<tree string="Contacts" editable="top">
<field name="contact_id"/>
<field name="function_id"/>
<field name="phone"/>
<field name="fax"/>
<field name="extension"/>
<field name="email"/>
</tree>
<form string="Contacts">
<field name="contact_id"/>
<field name="function_id"/>
<field name="phone"/>
<field name="fax"/>
<field name="extension"/>
<field name="email" widget="email"/>
</form>
</field>
</group>
</field>
</record>
-->
<record id="view_res_partner_address_filter" model="ir.ui.view">
<field name="name">res.partner.address.select</field>
<field name="model">res.partner.address</field>
@ -351,8 +328,8 @@
</field>
</record>
<!-- Views for partner job Tree view -->
<!-- Views for res.partner.job -->
<record model="ir.ui.view" id="view_partner_job_tree">
<field name="name">res.partner.job.tree</field>
<field name="model">res.partner.job</field>
@ -372,6 +349,8 @@
</field>
</record>
<!-- Views for partner job Form view -->
<record model="ir.ui.view" id="view_partner_job_form">
<field name="name">res.partner.job.form</field>
<field name="model">res.partner.job</field>
@ -400,19 +379,14 @@
</record>
<!-- Menuitem for res.partner.job -->
<record model="ir.actions.act_window" id="action_res_partner_job">
<field name="name">Contact's Jobs</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">res.partner.job</field>
<field name="view_type">form</field>
</record>
<!-- <menuitem-->
<!-- name="Partners/Contact's Jobs"-->
<!-- action="action_res_partner_job"-->
<!-- id="menu_action_res_partner_job"-->
<!-- groups="base.group_extended"/>-->
<!-- Act window defining a shorcut on partners to open all his contacts -->
<act_window domain="[('name', '=', active_id)]"
id="act_res_partner_jobs" name="Partner Contacts"
res_model="res.partner.job"
@ -420,6 +394,7 @@
/>
<!-- Act window defining a shorcut on partner address to open all his jobs -->
<act_window
id="act_res_partner_jobs" name="Open Jobs"
domain="[('address_id', '=', active_id)]"
@ -427,9 +402,13 @@
src_model="res.partner.address"
/>
<menuitem icon="terp-purchase" id="base.menu_purchase_root" name="Procurement Management" sequence="7"/>
<menuitem id="base.menu_procurement_management_supplier" name="Suppliers"
parent="base.menu_purchase_root" sequence="3"/>
<menuitem name="Contacts" id="menu_partner_contact_supplier_form" action="action_partner_contact_form" parent = "base.menu_procurement_management_supplier" sequence="2"/>
<menuitem icon="terp-purchase" id="base.menu_purchase_root"
name="Procurement Management" sequence="7" />
<menuitem id="base.menu_procurement_management_supplier"
name="Suppliers" parent="base.menu_purchase_root" sequence="3" />
<menuitem name="Contacts" id="menu_partner_contact_supplier_form"
action="action_partner_contact_form"
parent="base.menu_procurement_management_supplier" sequence="2" />
</data>
</openerp>

View File

@ -37,6 +37,8 @@ the "Dashboard" menu.
'init_xml': [],
'update_xml': [
'security/ir.model.access.csv',
'wizard/report_menu_create_view.xml',
'wizard/report_open_view.xml',
'base_report_creator_wizard.xml',
'base_report_creator_view.xml'
],

View File

@ -19,35 +19,13 @@
#
##############################################################################
import string
import time
import tools
from osv import fields,osv,orm
from osv import fields, osv
from tools.translate import _
#class ir_model_fields(osv.osv):
# _inherit = 'ir.model.fields'
# def _get_models(self, cr, uid, model_name, level=1):
# if not level:
# return []
# result = [model_name]
# print model_name
# for field,data in self.pool.get(model_name).fields_get(cr, uid).items():
# if data.get('relation', False):
# result += self._get_models(cr, uid, data['relation'], level-1)
# return result
# def search(self, cr, uid, args, offset=0, limit=None, order=None, context=None):
# if context and ('model_id' in context):
# model_name = self.pool.get("ir.model").browse(cr, uid, context['model_id'], context).model
# models = self._get_models(cr, uid, model_name, context.get('model_level',2))
# models = map(lambda x: self.pool.get('ir.model').search(cr, uid, [('model','=',x)])[0], models)
# args.append(('model_id','in',models))
# print args
# return super(ir_model_fields, self).search(cr, uid, args, offset, limit, order, context)
#ir_model_fields()
class report_creator(osv.osv):
"""
Report Creator
"""
_name = "base_report_creator.report"
_description = "Report"
model_set_id = False
@ -55,45 +33,74 @@ class report_creator(osv.osv):
# Should request only used fields
#
def export_data(self, cr, uid, ids, fields_to_export, context=None):
if not context:
context = {}
data_l = self.read(cr, uid, ids, ['sql_query'], context)
final_datas =[]
final_datas = []
#start Loop
for i in data_l:
datas = []
for key,value in i.items():
for key, value in i.items():
if key not in fields_to_export:
continue
if isinstance(value,tuple):
if isinstance(value, tuple):
datas.append(str(value[1]))
else:
datas.append(str(value))
final_datas += [datas]
return {'datas':final_datas}
#End Loop
return {'datas': final_datas}
def fields_get(self, cr, user, fields=None, context=None):
"""
Get Fields.
@param cr: the current row, from the database cursor,
@param user: the current users ID for security checks,
@param Fields: List of field of customer reports form.
@return: Dictionary of Fields
"""
if not context:
context = {}
data = context and context.get('report_id', False) or False
if (not context) or 'report_id' not in context:
return super(report_creator, self).fields_get(cr, user, fields, context)
report = self.browse(cr, user, context['report_id'])
models = {}
for model in report.model_ids:
models[model.model] = self.pool.get(model.model).fields_get(cr, user, context=context)
fields = {}
i = 0
for f in report.field_ids:
if f.field_id.model:
fields['field'+str(i)] = models[f.field_id.model][f.field_id.name]
i+=1
else:
fields['column_count'] = {'readonly': True, 'type': 'integer', 'string': 'Count', 'size': 64, 'name': 'column_count'}
return fields
if data:
report = self.browse(cr, user, data)
models = {}
#Start Loop
for model in report.model_ids:
models[model.model] = self.pool.get(model.model).fields_get(cr, user, context=context)
#End Loop
fields = {}
i = 0
for f in report.field_ids:
if f.field_id.model:
fields['field'+str(i)] = models[f.field_id.model][f.field_id.name]
i += 1
else:
fields['column_count'] = {'readonly': True, 'type': 'integer', 'string': 'Count', 'size': 64, 'name': 'column_count'}
return fields
#
# Should Call self.fields_get !
#
def fields_view_get(self, cr, user, view_id=None, view_type='form', context=None, toolbar=False, submenu=False):
"""
Overrides orm field_view_get.
@param cr: the current row, from the database cursor,
@param user: the current users ID for security checks,
@return: Dictionary of Fields, arch and toolbar.
"""
if not context:
context = {}
data = context and context.get('report_id', False) or False
if (not context) or 'report_id' not in context:
return super(report_creator, self).fields_view_get(cr, user, view_id, view_type, context, toolbar=toolbar, submenu=submenu)
report = self.browse(cr, user, context['report_id'])
report = self.browse(cr, user, data)
models = {}
for model in report.model_ids:
models[model.model] = self.pool.get(model.model).fields_get(cr, user, context=context)
@ -102,33 +109,33 @@ class report_creator(osv.osv):
for f in report.field_ids:
if f.field_id.model:
fields['field'+str(i)] = models[f.field_id.model][f.field_id.name]
i+=1
i += 1
else:
fields['column_count'] = {'readonly': True, 'type': 'integer', 'string': 'Count', 'size': 64, 'name': 'column_count'}
arch = '<?xml version="1.0" encoding="utf-8"?>\n'
if view_type=='graph':
arch +='<graph string="%s" type="%s" orientation="%s">' % (report.name, report.view_graph_type,report.view_graph_orientation)
if view_type == 'graph':
arch += '<graph string="%s" type="%s" orientation="%s">' % (report.name, report.view_graph_type, report.view_graph_orientation)
for val in ('x','y'):
i = 0
for f in report.field_ids:
if f.graph_mode==val:
if f.graph_mode == val:
if f.field_id.model:
arch += '<field name="%s" select="1"/>' % ('field'+str(i),)
i+=1
i += 1
else:
arch += '<field name="%s" select="1"/>' % ('column_count',)
elif view_type=='calendar':
required_types = ['date_start','date_delay','color']
set_dict = {'view_type':view_type,'string':report.name}
elif view_type == 'calendar':
required_types = ['date_start', 'date_delay', 'color']
set_dict = {'view_type':view_type, 'string':report.name}
temp_list = []
i=0
i = 0
for f in report.field_ids:
if f.calendar_mode and f.calendar_mode in required_types:
if f.field_id.model:
field_cal = 'field'+str(i)
i+=1
i += 1
else:
field_cal = 'column_count'
set_dict[f.calendar_mode] = field_cal
@ -136,20 +143,20 @@ class report_creator(osv.osv):
else:
if f.field_id.model:
temp_list.append('''<field name="%(name)s" select="1"/>''' % {'name':'field'+str(i)})
i+=1
temp_list.append('''<field name = "%(name)s" select = "1"/>''' % {'name': 'field' + str(i)})
i += 1
else:
temp_list.append('''<field name="%(name)s" select="1"/>''' % {'name':'column_count'})
arch += '''<%(view_type)s string="%(string)s" date_start="%(date_start)s" ''' %set_dict
if set_dict.get('date_delay',False):
arch +=''' date_delay="%(date_delay)s" '''%set_dict
arch += '''<% (view_type)s string = "%(string)s" date_start = "%(date_start)s" ''' % set_dict
if set_dict.get('date_delay', False):
arch += ''' date_delay = "%(date_delay)s" ''' % set_dict
if set_dict.get('date_stop',False):
arch +=''' date_stop="%(date_stop)s" '''%set_dict
if set_dict.get('date_stop', False):
arch += ''' date_stop="%(date_stop)s" '''%set_dict
if set_dict.get('color',False):
arch +=''' color="%(color)s"'''%set_dict
if set_dict.get('color', False):
arch += ''' color="%(color)s"'''%set_dict
arch += '''>'''
arch += ''.join(temp_list)
else:
@ -157,8 +164,8 @@ class report_creator(osv.osv):
i = 0
for f in report.field_ids:
if f.field_id.model:
arch += '<field name="%s" select="1"/>' % ('field'+str(i),)
i+=1
arch += '<field name="%s" select="1"/>' % ('field' + str(i),)
i += 1
else:
arch += '<field name="%s" select="1"/>' % ('column_count',)
arch += '</%s>' % (view_type,)
@ -171,70 +178,90 @@ class report_creator(osv.osv):
'action': [],
'relate': []
}
return result
def read(self, cr, user, ids, fields=None, context=None, load='_classic_read'):
def read(self, cr, user, ids, fields = None, context = None, load = '_classic_read'):
"""
overrides orm Read method.Read List of fields for report creator.
@param cr: the current row, from the database cursor,
@param user: the current users ID for security checks,
@param ids: List of report creator's id.
@param fields: List of fields.
@return: List of Dictionary of form [{name_of_the_field: value, ...}, ...]
"""
data = context and context.get('report_id', False) or False
if (not context) or 'report_id' not in context:
return super(report_creator, self).read(cr, user, ids, fields, context, load)
ctx = context or {}
wp = ''
if self.model_set_id:
wp = [self._id_get(cr, user, context['report_id'], context)+(' in (%s)' % (','.join(map(lambda x: "'"+str(x)+"'",ids))))]
report = self._sql_query_get(cr, user, [context['report_id']], 'sql_query', None, ctx, where_plus = wp)
sql_query = report[context['report_id']]
cr.execute(sql_query)
res = cr.dictfetchall()
fields_get = self.fields_get(cr,user,None,context)
for r in res:
for k in r:
r[k] = r[k] or False
field_dict = fields_get.get(k)
field_type = field_dict and field_dict.get('type',False) or False
if field_type and field_type == 'many2one':
if r[k]==False:
continue
related_name = self.pool.get(field_dict.get('relation')).name_get(cr,user,[r[k]],context)[0]
r[k] = related_name
return res
if data:
if self.model_set_id:
wp = [self._id_get(cr, user, data, context) + (' in (%s)' % (','.join(map(lambda x: "'" + str(x) + "'", ids))))]
report = self._sql_query_get(cr, user, [data], 'sql_query', None, ctx, where_plus = wp)
sql_query = report[data]
cr.execute(sql_query)
res = cr.dictfetchall()
fields_get = self.fields_get(cr, user, None, context)
for r in res:
for k in r:
r[k] = r[k] or False
field_dict = fields_get.get(k)
field_type = field_dict and field_dict.get('type', False) or False
if field_type and field_type == 'many2one':
if r[k] == False:
continue
related_name = self.pool.get(field_dict.get('relation')).name_get(cr, user, [r[k]], context)[0]
r[k] = related_name
return res
def search(self, cr, user, args, offset=0, limit=None, order=None, context=None, count=False):
"""
overrides orm search method.
@param cr: the current row, from the database cursor,
@param user: the current users ID for security checks,
@param args: list of tuples of form [(name_of_the_field, operator, value), ...].
@return: List of id
"""
context_id = context and context.get('report_id', False) or False
if (not context) or 'report_id' not in context:
return super(report_creator, self).search(cr, user, args, offset, limit, order, context, count)
report = self.browse(cr, user, context['report_id'])
i = 0
fields = {}
for f in report.field_ids:
if f.field_id.model:
fields['field'+str(i)] = (f.field_id.model, f.field_id.name)
i+=1
else:
fields['column_count'] = (False, 'Count')
newargs = []
newargs2 = []
for a in args:
if fields[a[0]][0]:
res = self.pool.get(fields[a[0]][0])._where_calc(cr, user, [[fields[a[0]][1],a[1],a[2]]], active_test=False, context=context)
newargs+=res[0]
newargs2+=res[1]
else:
newargs += [("count(*) " + a[1] +" " + str(a[2]))]
ctx = context or {}
ctx['getid'] = True
report = self._sql_query_get(cr, user, [context['report_id']], 'sql_query', None, ctx, where_plus=newargs, limit=limit, offset=offset)
query = report[context['report_id']]
cr.execute(query, newargs2)
result = cr.fetchall()
return map(lambda x: x[0], result)
if context_id:
report = self.browse(cr, user, context_id)
i = 0
fields = {}
for f in report.field_ids:
if f.field_id.model:
fields['field'+str(i)] = (f.field_id.model, f.field_id.name)
i += 1
else:
fields['column_count'] = (False, 'Count')
newargs = []
newargs2 = []
for a in args:
if fields[a[0]][0]:
res = self.pool.get(fields[a[0]][0])._where_calc(cr, user, [[fields[a[0]][1], a[1], a[2]]], active_test = False, context = context)
newargs += res[0]
newargs2 += res[1]
else:
newargs += [("count(*) " + a[1] +" " + str(a[2]))]
ctx = context or {}
ctx['getid'] = True
report = self._sql_query_get(cr, user, [context_id], 'sql_query', None, ctx, where_plus = newargs, limit=limit, offset=offset)
query = report[context_id]
cr.execute(query, newargs2)
result = cr.fetchall()
return map(lambda x: x[0], result)
def _path_get(self,cr, uid, models, filter_ids=[]):
# ret_str = """ sale_order_line
# left join sale_order on (sale_order_line.order_id=sale_order.id)
# left join res_partner on (res_partner.id=sale_order.partner_id)"""
# where_list = []
# for filter_id in filter_ids:
# where_list.append(filter_id.expression)
# if where_list:
# ret_str+="\nwhere\n\t"+" and\n\t".join(where_list)
def _path_get(self, cr, uid, models, filter_ids=[]):
"""
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param models: List of object.
"""
self.model_set_id = False
model_dict = {}
from_list = []
@ -242,7 +269,7 @@ class report_creator(osv.osv):
filter_list = []
for model in models:
model_dict[model.model] = self.pool.get(model.model)._table
model_list = model_dict.keys()
reference_model_dict = {}
for model in model_dict:
@ -250,7 +277,7 @@ class report_creator(osv.osv):
rest_list = model_dict.keys()
rest_list.remove(model)
model_pool = self.pool.get(model)
fields_get = model_pool.fields_get(cr,uid)
fields_get = model_pool.fields_get(cr, uid)
model_columns = {}
def _get_inherit_fields(obj):
@ -259,24 +286,24 @@ class report_creator(osv.osv):
model_columns.update(pool_model._columns)
#Adding the columns of its _inherits
for record in pool_model._inherits.keys():
_get_inherit_fields(record)
_get_inherit_fields(record)
_get_inherit_fields(model)
fields_filter = dict(filter(lambda x:x[1].get('relation',False)
fields_filter = dict(filter(lambda x:x[1].get('relation', False)
and x[1].get('relation') in rest_list
and x[1].get('type')=='many2one'
and not (isinstance(model_columns[x[0]],fields.function) or isinstance(model_columns[x[0]],fields.related)), fields_get.items()))
and x[1].get('type') == 'many2one'
and not (isinstance(model_columns[x[0]], fields.function) or isinstance(model_columns[x[0]], fields.related)), fields_get.items()))
if fields_filter:
model in model_list and model_list.remove(model)
model_count = reference_model_dict.get(model,False)
model_count = reference_model_dict.get(model, False)
if model_count:
reference_model_dict[model] = model_count +1
else:
reference_model_dict[model] = 1
for k,v in fields_filter.items():
for k, v in fields_filter.items():
v.get('relation') in model_list and model_list.remove(v.get('relation'))
relation_count = reference_model_dict.get(v.get('relation'),False)
relation_count = reference_model_dict.get(v.get('relation'), False)
if relation_count:
reference_model_dict[v.get('relation')] = relation_count+1
else:
@ -288,9 +315,9 @@ class report_creator(osv.osv):
if reference_model_dict:
self.model_set_id = model_dict.get(reference_model_dict.keys()[reference_model_dict.values().index(min(reference_model_dict.values()))])
if model_list and not len(model_dict.keys()) == 1:
raise osv.except_osv(_('No Related Models!!'),_('These is/are model(s) (%s) in selection which is/are not related to any other model') % ','.join(model_list))
raise osv.except_osv(_('No Related Models!!'), _('These is/are model(s) (%s) in selection which is/are not related to any other model') % ','.join(model_list))
if filter_ids and where_list<>[]:
if filter_ids and where_list <> []:
filter_list.append(' and ')
filter_list.append(' ')
@ -305,25 +332,32 @@ class report_creator(osv.osv):
else:
ret_str = ",\n".join(from_list)
if where_list:
where_list = list(set(where_list))
ret_str+="\n where \n"+" and\n".join(where_list)
ret_str += "\n where \n"+" and\n".join(where_list)
ret_str = ret_str.strip()
if filter_list:
ret_str +="\n".join(filter_list)
ret_str += "\n".join(filter_list)
if ret_str.endswith('and'):
ret_str = ret_str[0:len(ret_str)-3]
if ret_str.endswith('or'):
ret_str = ret_str[0:len(ret_str)-2]
ret_str = ret_str.strip()
return ret_str % {'uid' : uid}
return ret_str % {'uid': uid}
def _id_get(self, cr, uid, id, context):
"""
Get Model id
"""
# return 'min(sale_order_line.id)'
return self.model_set_id and 'min('+self.model_set_id+'.id)'
def _sql_query_get(self, cr, uid, ids, prop, unknow_none, context, where_plus=[], limit=None, offset=None):
"""
Get sql query which return on sql_query field.
@return: Dictionary of sql query.
"""
result = {}
for obj in self.browse(cr, uid, ids):
fields = []
@ -332,7 +366,7 @@ class report_creator(osv.osv):
for f in obj.field_ids:
# Allowing to use count(*)
if not f.field_id.model and f.group_method == 'count':
fields.insert(0,('count(*) as column_count'))
fields.insert(0, ('count(*) as column_count'))
continue
t = self.pool.get(f.field_id.model_id.model)._table
if f.group_method == 'group':
@ -340,11 +374,11 @@ class report_creator(osv.osv):
else:
fields.append('\t'+f.group_method+'('+t+'.'+f.field_id.name+')'+' as field'+str(i))
groupby.append(t+'.'+f.field_id.name)
i+=1
i += 1
models = self._path_get(cr, uid, obj.model_ids, obj.filter_ids)
check = self._id_get(cr, uid, ids[0], context)
if check<>False:
fields.insert(0,(check+' as id'))
fields.insert(0, (check + ' as id'))
if models:
result[obj.id] = """select
@ -365,36 +399,58 @@ class report_creator(osv.osv):
return result
_columns = {
'name': fields.char('Report Name',size=64, required=True),
'type': fields.selection([('list','Rows And Columns Report'),], 'Report Type',required=True),#('sum','Summation Report')
'active': fields.boolean('Active', help="If the active field is set to true, it will allow you to hide the report without removing it."),
'view_type1': fields.selection([('form','Form'),('tree','Tree'),('graph','Graph'),('calendar','Calendar')], 'First View', required=True),
'view_type2': fields.selection([('','/'),('form','Form'),('tree','Tree'),('graph','Graph'),('calendar','Calendar')], 'Second View'),
'view_type3': fields.selection([('','/'),('form','Form'),('tree','Tree'),('graph','Graph'),('calendar','Calendar')], 'Third View'),
'view_graph_type': fields.selection([('pie','Pie Chart'),('bar','Bar Chart')], 'Graph Type', required=True),
'view_graph_orientation': fields.selection([('horz','Horizontal'),('vert','Vertical')], 'Graph Orientation', required=True),
'model_ids': fields.many2many('ir.model', 'base_report_creator_report_model_rel', 'report_id','model_id', 'Reported Objects'),
'field_ids': fields.one2many('base_report_creator.report.fields', 'report_id', 'Fields to Display'),
'filter_ids': fields.one2many('base_report_creator.report.filter', 'report_id', 'Filters'),
'name': fields.char('Report Name', size=64, required=True),
'type': fields.selection([('list', 'Rows And Columns Report'), ], 'Report Type', required=True), #('sum','Summation Report')
'active': fields.boolean('Active', help="If the active field is set to true, it will allow you to hide the report without removing it."),
'view_type1': fields.selection([('form', 'Form'),
('tree', 'Tree'),
('graph', 'Graph'),
('calendar', 'Calendar')], 'First View', required=True),
'view_type2': fields.selection([('', '/'),
('form', 'Form'),
('tree', 'Tree'),
('graph', 'Graph'),
('calendar', 'Calendar')], 'Second View'),
'view_type3': fields.selection([('', '/'),
('form', 'Form'),
('tree', 'Tree'),
('graph', 'Graph'),
('calendar', 'Calendar')], 'Third View'),
'view_graph_type': fields.selection([('pie', 'Pie Chart'),
('bar', 'Bar Chart')], 'Graph Type', required=True),
'view_graph_orientation': fields.selection([('horz', 'Horizontal'),
('vert', 'Vertical')], 'Graph Orientation', required=True),
'model_ids': fields.many2many('ir.model', 'base_report_creator_report_model_rel', 'report_id', 'model_id', 'Reported Objects'),
'field_ids': fields.one2many('base_report_creator.report.fields', 'report_id', 'Fields to Display'),
'filter_ids': fields.one2many('base_report_creator.report.filter', 'report_id', 'Filters'),
'state': fields.selection([
('draft','Draft'),
('valid','Valid')],
'State', required=True,
('draft', 'Draft'),
('valid', 'Valid')],
'State', required=True,
help=' * The \'Draft\' state is used when a user is encoding a new and unconfirmed custom report. \
\n* The \'Valid\' state is used when user validates the custom report.'),
'sql_query': fields.function(_sql_query_get, method=True, type="text", string='SQL Query', store=True),
'group_ids': fields.many2many('res.groups', 'base_report_creator_group_rel','report_id','group_id','Authorized Groups'),
\n* The \'Valid\' state is used when user validates the custom report.'),
'sql_query': fields.function(_sql_query_get, method=True, type="text", string='SQL Query', store=True),
'group_ids': fields.many2many('res.groups', 'base_report_creator_group_rel', 'report_id', 'group_id', 'Authorized Groups'),
}
_defaults = {
'type': lambda *args: 'list',
'state': lambda *args: 'draft',
'active': lambda *args: True,
'view_type1': lambda *args: 'tree',
'view_type2': lambda *args: 'graph',
'view_graph_type': lambda *args: 'bar',
'view_graph_orientation': lambda *args: 'horz',
'type': lambda *args: 'list',
'state': lambda *args: 'draft',
'active': lambda *args: True,
'view_type1': lambda *args: 'tree',
'view_type2': lambda *args: 'graph',
'view_graph_type': lambda *args: 'bar',
'view_graph_orientation': lambda *args: 'horz',
}
def _function_field(self, cr, uid, ids):
"""
constraints function which specify that
not display field which are not stored in Database.
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param ids: List of Report creator's id.
@return: True if display field which are stored in database.
or false if display field which are not store in dtabase.
"""
this_objs = self.browse(cr, uid, ids)
for obj in this_objs:
for fld in obj.field_ids:
@ -402,13 +458,23 @@ class report_creator(osv.osv):
if not fld.field_id.model and fld.group_method == 'count':
continue
model_column = self.pool.get(fld.field_id.model)._columns[fld.field_id.name]
if (isinstance(model_column,fields.function) or isinstance(model_column,fields.related)) and not model_column.store:
if (isinstance(model_column, fields.function) or isinstance(model_column, fields.related)) and not model_column.store:
return False
return True
def _aggregation_error(self, cr, uid, ids):
aggregate_columns = ('integer','float')
apply_functions = ('sum','min','max','avg','count')
"""
constraints function which specify that
aggregate function to the non calculated field..
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param ids: List of Report creator's id.
@return: True if model colume type is in integer or float.
or false model colume type is not in integer or float.
"""
aggregate_columns = ('integer', 'float')
apply_functions = ('sum', 'min', 'max', 'avg', 'count')
this_objs = self.browse(cr, uid, ids)
for obj in this_objs:
for fld in obj.field_ids:
@ -425,7 +491,7 @@ class report_creator(osv.osv):
required_types = []
this_objs = self.browse(cr, uid, ids)
for obj in this_objs:
if obj.view_type1=='calendar' or obj.view_type2=='calendar' or obj.view_type3=='calendar':
if obj.view_type1 == 'calendar' or obj.view_type2 == 'calendar' or obj.view_type3 == 'calendar':
for fld in obj.field_ids:
# Allowing to use count(*)
if not fld.field_id.model and fld.group_method == 'count':
@ -442,43 +508,60 @@ class report_creator(osv.osv):
return True
_constraints = [
(_function_field, 'You can not display field which are not stored in Database.', ['field_ids']),
(_aggregation_error, 'You can apply aggregate function to the non calculated field.', ['field_ids']),
(_calander_view_error, "You must have to give calendar view's color,start date and delay.", ['field_ids']),
(_function_field, 'You can not display field which are not stored in Database.', ['field_ids']),
(_aggregation_error, 'You can apply aggregate function to the non calculated field.', ['field_ids']),
(_calander_view_error, "You must have to give calendar view's color,start date and delay.", ['field_ids']),
]
report_creator()
class report_creator_field(osv.osv):
"""
Report Creator Field
"""
_name = "base_report_creator.report.fields"
_description = "Display Fields"
_rec_name = 'field_id'
_order = "sequence,id"
_columns = {
'sequence': fields.integer('Sequence', help="Gives the sequence order when displaying a list of fields."),
'field_id': fields.many2one('ir.model.fields', 'Field'),
'report_id': fields.many2one('base_report_creator.report','Report', on_delete='cascade'),
'group_method': fields.selection([('group','Grouped'),('sum','Sum'),('min','Minimum'),('count','Count'),('max','Maximum'),('avg','Average')], 'Grouping Method', required=True),
'graph_mode': fields.selection([('','/'),('x','X Axis'),('y','Y Axis')], 'Graph Mode'),
'calendar_mode': fields.selection([('','/'),('date_start','Starting Date'),('date_end','Ending Date'),('date_delay','Delay'),('date_stop','End Date'),('color','Unique Colors')], 'Calendar Mode'),
'sequence': fields.integer('Sequence', help="Gives the sequence order when displaying a list of fields."),
'field_id': fields.many2one('ir.model.fields', 'Field'),
'report_id': fields.many2one('base_report_creator.report', 'Report', on_delete='cascade'),
'group_method': fields.selection([('group', 'Grouped'),
('sum', 'Sum'),
('min', 'Minimum'),
('count', 'Count'),
('max', 'Maximum'),
('avg', 'Average')], 'Grouping Method', required=True),
'graph_mode': fields.selection([('', '/'),
('x', 'X Axis'),
('y', 'Y Axis')], 'Graph Mode'),
'calendar_mode': fields.selection([('', '/'),
('date_start', 'Starting Date'),
('date_end', 'Ending Date'), ('date_delay', 'Delay'), ('date_stop', 'End Date'), ('color', 'Unique Colors')], 'Calendar Mode'),
}
_defaults = {
'group_method': lambda *args: 'group',
'graph_mode': lambda *args: '',
'group_method': lambda *args: 'group',
'graph_mode': lambda *args: '',
}
report_creator_field()
class report_creator_filter(osv.osv):
"""
Report Creator Filter
"""
_name = "base_report_creator.report.filter"
_description = "Report Filters"
_columns = {
'name': fields.char('Filter Name',size=64, required=True),
'expression': fields.text('Value', required=True,help='Provide an expression for the field based on which you want to filter the records.\n e.g. res_partner.id=3'),
'report_id': fields.many2one('base_report_creator.report','Report', on_delete='cascade'),
'condition' : fields.selection([('and','AND'),('or','OR')], 'Condition')
'name': fields.char('Filter Name', size=64, required=True),
'expression': fields.text('Value', required=True, help='Provide an expression for the field based on which you want to filter the records.\n e.g. res_partner.id=3'),
'report_id': fields.many2one('base_report_creator.report', 'Report', on_delete='cascade'),
'condition': fields.selection([('and', 'AND'),
('or', 'OR')], 'Condition')
}
_defaults = {
'condition': lambda *args: 'and',
'condition': lambda *args: 'and',
}
report_creator_filter()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -27,6 +27,9 @@
</tree>
</field>
</record>
<!-- Report Creator form -->
<record model="ir.ui.view" id="base_report_creator_form">
<field name="name">base_report_creator.report.form</field>
<field name="model">base_report_creator.report</field>
@ -38,10 +41,13 @@
<field name="active" select="2"/>
<notebook colspan="4">
<page string="General Configuration">
<field name="model_ids" colspan="4" context="{'model_ids':model_ids}"/>
<field name="model_ids" colspan="4"
context="{'model_ids':model_ids}" />
<separator string="State" colspan="4"/>
<field name="state"/>
<button string="Create Menu" name="%(wizard_menu_create)d" type="action" colspan="2" icon="gtk-justify-fill"/>
<button string="Create Menu"
name="%(action_report_menu_create)d" type="action"
colspan="2" icon="gtk-justify-fill" />
</page><page string="View parameters">
<separator string="Used View" colspan="4"/>
<field name="view_type1"/>
@ -54,12 +60,13 @@
<field name="field_ids" nolabel="1" colspan="4">
<form string="Fields">
<field name="sequence"/>
<field name="field_id" domain="parent.model_ids and [('model_id','in',parent.model_ids[0][2]),('ttype','&lt;&gt;','many2many'),('ttype','&lt;&gt;','one2many')] or []">
<tree string="Field List">
<field name="model_id"/>
<field name="name"/>
<field name="field_description"/>
</tree>
<field name="field_id"
domain="parent.model_ids and [('model_id','in',parent.model_ids[0][2]),('ttype','&lt;&gt;','many2many'),('ttype','&lt;&gt;','one2many')] or []">
<tree string="Field List">
<field name="model_id" />
<field name="name" />
<field name="field_description" />
</tree>
</field>
<field name="group_method"/>
<field name="graph_mode"/>
@ -67,14 +74,18 @@
</form>
<tree editable="bottom" string="Fields to Display">
<field name="sequence"/>
<field name="field_id" domain="parent.model_ids and [('model_id','in',parent.model_ids[0][2]),('ttype','&lt;&gt;','many2many'),('ttype','&lt;&gt;','one2many')] or []" attrs="{'required':[('group_method','!=','count')]}"/>
<field name="field_id"
domain="parent.model_ids and [('model_id','in',parent.model_ids[0][2]),('ttype','&lt;&gt;','many2many'),('ttype','&lt;&gt;','one2many')] or []"
attrs="{'required':[('group_method','!=','count')]}" />
<field name="group_method"/>
<field name="graph_mode"/>
<field name="calendar_mode"/>
</tree>
</field>
</page><page string="Filters on Fields">
<button string="Add filter" name="%(wizard_set_filter_fields)d" type="action" icon="gtk-add"/>
<button string="Add filter"
name="%(wizard_set_filter_fields)d" type="action"
icon="gtk-add" />
<field name="filter_ids" nolabel="1" colspan="4">
<tree string="Filters">
<field name="name"/>
@ -87,8 +98,10 @@
</form>
</field>
<separator string="Legend" colspan="4"/>
<label string="Use %%(uid)s to filter by the connected user" align="0.0"/>
</page><page string="Security">
<label
string="Use %%(uid)s to filter by the connected user"
align="0.0" />
</page><page string="Security">
<separator string="Authorized Groups (empty for all)" colspan="4"/>
<field name="group_ids" colspan="4" nolabel="1"/>
</page><page string="SQL Query">
@ -98,15 +111,20 @@
</form>
</field>
</record>
<!-- Action for Report creator form -->
<record model="ir.actions.act_window" id="base_report_creator_action">
<field name="name">Custom Report</field>
<field name="res_model">base_report_creator.report</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
</record>
<menuitem
name="Custom Reports" id="base.menu_custom_reports"
action="base_report_creator_action" parent="base.reporting_menu" sequence="1"/>
<menuitem
name="Custom Reports" parent="base.next_id_50"
id="menu_base_report_creator_action_config"
@ -122,36 +140,22 @@
</tree>
</field>
</record>
<record model="ir.actions.act_window" id="base_report_creator_action_tree">
<field name="res_model">base_report_creator.report</field>
<field name="view_type">tree</field>
</record>
<menuitem
name="Custom Reports" parent="base.menu_custom_reports"
id="menu_base_report_creator_action"
action="base_report_creator_action_tree"/>
<wizard
id="wizard_report_open"
string="Open Report"
name="base_report_creator.report.open"/>
<record model="ir.values" id="ir_open_report">
<field name="key2" eval="'tree_but_open'"/>
<field name="model" eval="'base_report_creator.report'"/>
<field name="name">Browse Report</field>
<field name="value" eval="'ir.actions.wizard,%d'%wizard_report_open"/>
<field name="object" eval="True"/>
</record>
<record model="ir.values" id="ir_open_report_multi">
<field name="key2" eval="'client_action_multi'"/>
<field name="model" eval="'base_report_creator.report'"/>
<field name="name">Browse Report</field>
<field name="value" eval="'ir.actions.wizard,%d'%wizard_report_open"/>
<field name="object" eval="True"/>
</record>
<act_window id="action_report_open"
key2="client_action_multi" name="Open Report"
res_model="report.open" src_model="base_report_creator.report"
view_mode="form" target="new" view_type="form" />
</data>
</openerp>

View File

@ -1,13 +1,8 @@
<?xml version="1.0"?>
<openerp>
<data>
<wizard
id="wizard_set_filter_fields"
string="Set Filter Fields"
name="base_report_creator.report_filter.fields"/>
<wizard
id="wizard_menu_create"
string="Create Menu for Report"
name="base_report_creator.report.menu.create"/>
</data>
<data>
<wizard id="wizard_set_filter_fields" string="Set Filter Fields"
name="base_report_creator.report_filter.fields" />
</data>
</openerp>

View File

@ -1,4 +1,6 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_base_report_creator_report,base_report_creator.report,model_base_report_creator_report,base.group_system,1,1,1,1
access_base_report_creator_report_fields,base_report_creator.report.fields,model_base_report_creator_report_fields,base.group_system,1,1,1,1
access_base_report_creator_report_filter,base_report_creator.report.filter,model_base_report_creator_report_filter,base.group_system,1,1,1,1
"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink"
"access_base_report_creator_report","base_report_creator.report","model_base_report_creator_report","base.group_system",1,1,1,1
"access_base_report_creator_report_fields","base_report_creator.report.fields","model_base_report_creator_report_fields","base.group_system",1,1,1,1
"access_base_report_creator_report_filter","base_report_creator.report.filter","model_base_report_creator_report_filter","base.group_system",1,1,1,1
"access_report_menu_create","report.menu.create","model_report_menu_create","base.group_system",1,1,1,1
"access_report_open","report.open","model_report_open","base.group_system",1,1,1,1

1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_base_report_creator_report base_report_creator.report model_base_report_creator_report base.group_system 1 1 1 1
3 access_base_report_creator_report_fields base_report_creator.report.fields model_base_report_creator_report_fields base.group_system 1 1 1 1
4 access_base_report_creator_report_filter base_report_creator.report.filter model_base_report_creator_report_filter base.group_system 1 1 1 1
5 access_report_menu_create report.menu.create model_report_menu_create base.group_system 1 1 1 1
6 access_report_open report.open model_report_open base.group_system 1 1 1 1

View File

@ -20,7 +20,7 @@
##############################################################################
import wiz_set_filter_fields
import base_report_creator_open
import report_menu_create
import report_open
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -19,63 +19,56 @@
#
##############################################################################
from osv import fields, osv
import time
import wizard
import osv
import pooler
section_form = '''<?xml version="1.0"?>
<form string="Create Menu For This Report">
<separator string="Menu Information" colspan="4"/>
<field name="menu_name"/>
<field name="menu_parent_id"/>
</form>'''
section_fields = {
'menu_name': {'string':'Menu Name', 'type':'char', 'required':True, 'size':64},
'menu_parent_id': {'string':'Parent Menu', 'type':'many2one', 'relation':'ir.ui.menu', 'required':True},
}
def report_menu_create(self, cr, uid, data, context):
pool = pooler.get_pool(cr.dbname)
board = pool.get('base_report_creator.report').browse(cr, uid, data['id'])
view = board.view_type1
if board.view_type2:
view+=','+board.view_type2
if board.view_type3:
view+=','+board.view_type3
action_id = pool.get('ir.actions.act_window').create(cr, uid, {
'name': board.name,
'view_type':'form',
'view_mode':view,
'context': "{'report_id':%d}" % (board.id,),
'res_model': 'base_report_creator.report'
})
pool.get('ir.ui.menu').create(cr, uid, {
'name': data['form']['menu_name'],
'parent_id': data['form']['menu_parent_id'],
'icon': 'STOCK_SELECT_COLOR',
'action': 'ir.actions.act_window,'+str(action_id)
}, context)
return {}
class wizard_section_menu_create(wizard.interface):
states = {
'init': {
'actions': [],
'result': {'type':'form', 'arch':section_form, 'fields':section_fields, 'state':[('end','Cancel'),('create_menu','Create Menu')]}
},
'create_menu': {
'actions': [report_menu_create],
'result': {
'type':'state',
'state':'end'
}
}
}
wizard_section_menu_create('base_report_creator.report.menu.create')
class report_menu_create(osv.osv_memory):
"""
Create Menu
"""
_name = "report.menu.create"
_description = "Menu Create"
_columns = {
'menu_name':fields.char('Menu Name', size=64, required=True),
'menu_parent_id':fields.many2one('ir.ui.menu', 'Parent Menu', required=True),
}
def report_menu_create(self, cr, uid, ids, context=None):
"""
Create Menu.
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param ids: List of Report Menu Create's IDs
@return: Dictionary {}.
"""
if not context:
context = {}
context_id = context and context.get('active_id', False) or False
if context_id:
board = self.pool.get('base_report_creator.report').browse(cr, uid, context_id)
view = board.view_type1
if board.view_type2:
view += ',' + board.view_type2
if board.view_type3:
view += ',' + board.view_type3
action_id = self.pool.get('ir.actions.act_window').create(cr, uid, {
'name': board.name,
'view_type':'form',
'view_mode':view,
'context': "{'report_id':%d}" % (board.id,),
'res_model': 'base_report_creator.report'
})
obj_menu = self.pool.get('ir.ui.menu')
#start Loop
for data in self.read(cr, uid, ids):
obj_menu.create(cr, uid, {
'name': data.get('menu_name'),
'parent_id': data.get('menu_parent_id'),
'icon': 'STOCK_SELECT_COLOR',
'action': 'ir.actions.act_window, ' + str(action_id)
}, context=context)
return {}
#End Loop
report_menu_create()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -0,0 +1,39 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<!-- Create Menu For report -->
<record id="view_report_menu_create" model="ir.ui.view">
<field name="name">report.menu.create.form</field>
<field name="model">report.menu.create</field>
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Create Menu For This Report">
<group colspan="4" >
<separator string="Menu Information" colspan="4"/>
<field name="menu_name"/>
<field name="menu_parent_id"/>
</group>
<separator string="" colspan="4" />
<group colspan="4" col="6">
<button icon="gtk-cancel" special="cancel"
string="Cancel" />
<button icon="gtk-save" string="Create Menu"
name="report_menu_create" type="object" />
</group>
</form>
</field>
</record>
<record id="action_report_menu_create" model="ir.actions.act_window">
<field name="name">Create Menu for Report</field>
<field name="res_model">report.menu.create</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
<field name="view_id" ref="view_report_menu_create"/>
<field name="target">new</field>
</record>
</data>
</openerp>

View File

@ -0,0 +1,71 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from osv import fields, osv
class report_open(osv.osv_memory):
"""
Open report
"""
_name = "report.open"
_description = __doc__
def open_report(self, cr, uid, ids, context=None):
"""
This Function opens base creator report view
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param ids: List of report open's IDs
@param context: A standard dictionary for contextual values
@return : Dictionary value for base creator report form
"""
if not context:
context = {}
context_id = context and context.get('active_id', False) or False
if context.get('report_id', False):
raise osv.except_osv(_('UserError'), _('No Wizards available for this object!'))
rep = self.pool.get('base_report_creator.report').browse(cr, uid, context_id, context)
view_mode = rep.view_type1
if rep.view_type2:
view_mode += ',' + rep.view_type2
if rep.view_type3:
view_mode += ',' + rep.view_type3
value = {
'name': rep.name,
'view_type': 'form',
'view_mode': view_mode,
'res_model': 'base_report_creator.report',
'context': {'report_id': context_id},
'view_id': False,
'type': 'ir.actions.act_window'
}
return value
report_open()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<!-- Report Open wizard -->
<record id="view_report_open" model="ir.ui.view">
<field name="name">report.open.form</field>
<field name="model">report.open</field>
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Report open">
<separator string="" colspan="4" />
<group colspan="4" col="6">
<button icon="gtk-cancel" special="cancel"
string="Cancel" />
<button icon="gtk-open" string="Open Report"
name="open_report" type="object" />
</group>
</form>
</field>
</record>
<record id="action_report_open" model="ir.actions.act_window">
<field name="name">Open Report</field>
<field name="res_model">report.open</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
<field name="view_id" ref="view_report_open"/>
<field name="target">new</field>
</record>
</data>
</openerp>

View File

@ -23,4 +23,3 @@ import board
import wizard
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -19,7 +19,6 @@
#
##############################################################################
{
'name': 'Dashboard main module',
'version': '1.0',
@ -27,7 +26,7 @@
'description': """Base module for all dashboards.""",
'author': 'Tiny',
'depends': ['base'],
'update_xml': ['security/ir.model.access.csv', 'board_view.xml'],
'update_xml': ['security/ir.model.access.csv', 'wizard/board_menu_create_view.xml', 'board_view.xml'],
'demo_xml': [],
'installable': True,
'active': False,

View File

@ -19,28 +19,42 @@
#
##############################################################################
from osv import fields, osv
import time
from osv import fields,osv
class board_board(osv.osv):
"""
Board
"""
_name = 'board.board'
_description = "Board"
def create_view(self, cr, uid, ids, context):
board = self.pool.get('board.board').browse(cr, uid, ids, context)
def create_view(self, cr, uid, ids, context=None):
"""
Create view
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param ids: List of Board's IDs
@return: arch of xml view.
"""
if not context:
context = {}
board = self.pool.get('board.board').browse(cr, uid, ids, context=context)
left = []
right = []
#start Loop
for line in board.line_ids:
linestr = '<action string="%s" name="%d" colspan="4"' % (line.name, line.action_id.id)
if line.height:
linestr+=(' height="%d"' % (line.height,))
linestr += (' height="%d"' % (line.height, ))
if line.width:
linestr+=(' width="%d"' % (line.width,))
linestr += (' width="%d"' % (line.width, ))
linestr += '/>'
if line.position=='left':
if line.position == 'left':
left.append(linestr)
else:
right.append(linestr)
#End Loop
arch = """<?xml version="1.0"?>
<form string="My Board">
<hpaned>
@ -55,24 +69,45 @@ class board_board(osv.osv):
return arch
def write(self, cr, uid, ids, vals, context={}):
def write(self, cr, uid, ids, vals, context = {}):
"""
Writes values in one or several fields.
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param ids: List of Board's IDs
@param vals: dictionary with values to update.
dictionary must be with the form: {name_of_the_field: value, ...}.
@return: True
"""
result = super(board_board, self).write(cr, uid, ids, vals, context)
board = self.pool.get('board.board').browse(cr, uid, ids[0])
view = self.create_view(cr, uid, ids[0], context)
id = board.view_id.id
cr.execute("update ir_ui_view set arch=%s where id=%s" , (view, id))
cr.execute("update ir_ui_view set arch=%s where id=%s", (view, id))
return result
def create(self, cr, user, vals, context=None):
"""
create new record.
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param vals: dictionary of values for every field.
dictionary must use this form: {name_of_the_field: value, ...}
@return: id of new created record of board.board.
"""
if not context:
context = {}
if not 'name' in vals:
return False
id = super(board_board, self).create(cr, user, vals, context)
view_id = self.pool.get('ir.ui.view').create(cr, user, {
'name': vals['name'],
'model':'board.board',
'priority':16,
'model': 'board.board',
'priority': 16,
'type': 'form',
'arch': self.create_view(cr, user, id, context),
})
@ -81,17 +116,28 @@ class board_board(osv.osv):
return id
def fields_view_get(self, cr, user, view_id=None, view_type='form', context=None, toolbar=False, submenu=False):
res = {}
res = super(board_board, self).fields_view_get(cr, user, view_id, view_type, context, toolbar=toolbar, submenu=submenu)
def fields_view_get(self, cr, user, view_id=None, view_type='form', context=None,\
toolbar=False, submenu=False):
"""
Overrides orm field_view_get.
@return: Dictionary of Fields, arch and toolbar.
"""
vids = self.pool.get('ir.ui.view.custom').search(cr, user, [('user_id','=',user), ('ref_id','=',view_id)])
if not context:
context = {}
res = {}
res = super(board_board, self).fields_view_get(cr, user, view_id, view_type,\
context, toolbar=toolbar, submenu=submenu)
vids = self.pool.get('ir.ui.view.custom').search(cr, user,\
[('user_id', '=', user), ('ref_id' ,'=', view_id)])
if vids:
view_id = vids[0]
arch = self.pool.get('ir.ui.view.custom').browse(cr, user, view_id)
res['arch'] = arch.arch
res['toolbar'] = {'print':[],'action':[],'relate':[]}
res['toolbar'] = {'print': [], 'action': [], 'relate': []}
return res
_columns = {
@ -102,44 +148,67 @@ class board_board(osv.osv):
# the following lines added to let the button on dashboard work.
_defaults = {
'name': lambda *args: 'Dashboard'
'name':lambda *args: 'Dashboard'
}
board_board()
class board_line(osv.osv):
"""
Board Line
"""
_name = 'board.board.line'
_description = "Board Line"
_order = 'position,sequence'
_columns = {
'name': fields.char('Title', size=64, required=True),
'sequence': fields.integer('Sequence', help="Gives the sequence order when displaying a list of board lines."),
'sequence': fields.integer('Sequence', help="Gives the sequence order\
when displaying a list of board lines."),
'height': fields.integer('Height'),
'width': fields.integer('Width'),
'board_id': fields.many2one('board.board', 'Dashboard', required=True, ondelete='cascade'),
'action_id': fields.many2one('ir.actions.act_window', 'Action', required=True),
'position': fields.selection([('left','Left'),('right','Right')], 'Position', required=True)
'position': fields.selection([('left','Left'),
('right','Right')], 'Position', required=True)
}
_defaults = {
'position': lambda *args: 'left'
}
board_line()
class board_note_type(osv.osv):
"""
Board note Type
"""
_name = 'board.note.type'
_description = "NOte Type"
_columns = {
'name': fields.char('Note Type', size=64, required=True),
}
board_note_type()
def _type_get(self, cr, uid, context={}):
"""
Get by default Note type.
"""
obj = self.pool.get('board.note.type')
ids = obj.search(cr, uid, [])
res = obj.read(cr, uid, ids, ['name'], context)
res = [(r['name'], r['name']) for r in res]
return res
class board_note(osv.osv):
"""
Board Note
"""
_name = 'board.note'
_description = "Note"
_columns = {
'name': fields.char('Subject', size=128, required=True),
'note': fields.text('Note'),
@ -148,10 +217,10 @@ class board_note(osv.osv):
'type': fields.selection(_type_get, 'Note type', size=64),
}
_defaults = {
'user_id': lambda object,cr,uid,context: uid,
'date': lambda object,cr,uid,context: time.strftime('%Y-%m-%d'),
'user_id': lambda object, cr, uid, context: uid,
'date': lambda object, cr, uid, context: time.strftime('%Y-%m-%d'),
}
board_note()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -1,8 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<menuitem icon="terp-graph" id="base.reporting_menu" name="Reporting" sequence="8"/>
<!-- <menuitem icon="terp-graph" id="dashboard" name="Dashboards" sequence="2" parent="base.reporting_menu"/>-->
<menuitem icon="terp-graph" id="base.reporting_menu"
name="Reporting" sequence="8" />
<!--Board Note Tree View -->
<record id="view_board_note_tree" model="ir.ui.view">
<field name="name">board.note.tree</field>
<field name="model">board.note</field>
@ -13,6 +16,9 @@
</tree>
</field>
</record>
<!--Board Note Form View -->
<record id="view_board_note_form" model="ir.ui.view">
<field name="name">board.note.form</field>
<field name="model">board.note</field>
@ -27,6 +33,9 @@
</form>
</field>
</record>
<!-- Action for Publish note Form -->
<record id="action_view_board_note_form" model="ir.actions.act_window">
<field name="name">Publish a note</field>
<field name="res_model">board.note</field>
@ -34,7 +43,7 @@
<field name="view_mode">form,tree</field>
</record>
<wizard id="wizard_board_create_menu" model="board.board" multi="True" name="board.board.menu.create" string="Create Board Menu" />
<!-- Board Tree View -->
<record id="view_board_tree" model="ir.ui.view">
<field name="name">board.board.tree</field>
@ -46,6 +55,9 @@
</tree>
</field>
</record>
<!-- Dashboard Definition Form View -->
<record id="view_board_form" model="ir.ui.view">
<field name="name">board.board.form</field>
<field name="model">board.board</field>
@ -54,7 +66,10 @@
<field name="arch" type="xml">
<form string="Dashboard">
<field name="name" select="1"/>
<button colspan="2" name="%(wizard_board_create_menu)d" string="Create Menu" type="action" icon="gtk-justify-fill"/>
<button colspan="2"
name="%(action_board_menu_create)d"
string="Create Menu" type="action"
icon="gtk-justify-fill" />
<field colspan="4" name="line_ids">
<tree string="Dashboard View">
<field name="name"/>
@ -67,19 +82,24 @@
<field name="sequence"/>
<field name="width"/>
<field name="height"/>
<field name="action_id" domain="[('view_type','!=','tree')]"/>
<field name="action_id"
domain="[('view_type','!=','tree')]" />
<field name="position"/>
</form>
</field>
</form>
</field>
</record>
<!-- Action for DashBoard Definition form -->
<record id="action_view_board_list_form" model="ir.actions.act_window">
<field name="name">Dashboard Definition</field>
<field name="res_model">board.board</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
</record>
<menuitem
id="base.next_id_50"
name="Configuration"
@ -90,10 +110,14 @@
action="action_view_board_list_form"
id="menu_view_board_form"
parent="base.next_id_50" sequence="1"/>
<menuitem action="action_view_board_note_form" id="menu_view_board_note_form" parent="base.next_id_50"
sequence="3"
groups="base.group_system" />
<act_window context="{'view': active_id}" id="dashboard_open" multi="True" name="Open Dashboard" res_model="board.board" src_model="board.board"/>
<menuitem action="action_view_board_note_form"
id="menu_view_board_note_form" parent="base.next_id_50"
sequence="3" groups="base.group_system" />
<act_window context="{'view': active_id}" id="dashboard_open"
multi="True" name="Open Dashboard" res_model="board.board"
src_model="board.board" />
</data>
</openerp>

View File

@ -1,5 +1,6 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_board_board,board.board,model_board_board,base.group_user,1,1,1,1
access_board_board_line,board.board.line,model_board_board_line,base.group_user,1,1,1,1
access_board_note_type,board.note.type,model_board_note_type,base.group_user,1,1,1,1
access_board_note,board.note,model_board_note,base.group_user,1,1,1,1
"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink"
"access_board_board","board.board","model_board_board","base.group_user",1,1,1,1
"access_board_board_line","board.board.line","model_board_board_line","base.group_user",1,1,1,1
"access_board_note_type","board.note.type","model_board_note_type","base.group_user",1,1,1,1
"access_board_note","board.note","model_board_note","base.group_user",1,1,1,1
"access_board_menu_create","board.menu.create","model_board_menu_create","base.group_user",1,1,1,1

1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_board_board board.board model_board_board base.group_user 1 1 1 1
3 access_board_board_line board.board.line model_board_board_line base.group_user 1 1 1 1
4 access_board_note_type board.note.type model_board_note_type base.group_user 1 1 1 1
5 access_board_note board.note model_board_note base.group_user 1 1 1 1
6 access_board_menu_create board.menu.create model_board_menu_create base.group_user 1 1 1 1

View File

@ -19,7 +19,7 @@
#
##############################################################################
import board_wizard
import board_menu_create
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -0,0 +1,94 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from osv import fields, osv
from tools.translate import _
class board_menu_create(osv.osv_memory):
"""
Create Menu
"""
def view_init(self, cr, uid, fields, context=None):
"""
This function checks for precondition before wizard executes
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param fields: List of fields for default value
@param context: A standard dictionary for contextual values
check dashboard view on menu name field.
@return: False
"""
data = context and context.get('active_id', False) or False
if data:
board = self.pool.get('board.board').browse(cr, uid, data)
if not board.line_ids:
raise osv.except_osv(_('User Error!'),
_('Please Insert Dashboard View(s) !'))
return False
def board_menu_create(self, cr, uid, ids, context=None):
"""
Create Menu.
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param ids: List of Board Menu Create's IDs
@return: Dictionary {}.
"""
if not context:
context = {}
context_id = context and context.get('active_id', False) or False
if context_id:
board = self.pool.get('board.board').browse(cr, uid, context_id)
action_id = self.pool.get('ir.actions.act_window').create(cr, uid, {
'name': board.name,
'view_type': 'form',
'view_mode': 'form',
'res_model': 'board.board',
'view_id': board.view_id.id,
})
obj_menu = self.pool.get('ir.ui.menu')
#start Loop
for data in self.read(cr, uid, ids):
obj_menu.create(cr, uid, {
'name': data.get('menu_name'),
'parent_id': data.get('menu_parent_id'),
'icon': 'STOCK_SELECT_COLOR',
'action': 'ir.actions.act_window,' + str(action_id)
}, context=context)
#End Loop
return {}
_name = "board.menu.create"
_description = "Menu Create"
_columns = {
'menu_name': fields.char('Menu Name', size=64, required=True),
'menu_parent_id': fields.many2one('ir.ui.menu', 'Parent Menu', required=True),
}
board_menu_create()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -0,0 +1,41 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<!--Board menu create wizard -->
<record id="view_board_menu_create" model="ir.ui.view">
<field name="name">board.menu.create.form</field>
<field name="model">board.menu.create</field>
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Create Menu For Dashboard">
<group colspan="4" >
<separator string="Menu Information" colspan="4"/>
<field name="menu_name"/>
<field name="menu_parent_id"/>
</group>
<separator string="" colspan="4" />
<group colspan="4" col="6">
<button icon="gtk-cancel" special="cancel"
string="Cancel" />
<button icon="gtk-save" string="Create Menu"
name="board_menu_create" type="object" />
</group>
</form>
</field>
</record>
<!-- Action for Board Menu create wizard. -->
<record id="action_board_menu_create" model="ir.actions.act_window">
<field name="name">Create Board Menu</field>
<field name="res_model">board.menu.create</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
<field name="view_id" ref="view_board_menu_create"/>
<field name="target">new</field>
</record>
</data>
</openerp>

View File

@ -1,86 +0,0 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
import time
import wizard
import osv
import pooler
from tools.translate import _
section_form = '''<?xml version="1.0"?>
<form string="Create Menu For Dashboard">
<separator string="Menu Information" colspan="4"/>
<field name="menu_name"/>
<field name="menu_parent_id"/>
</form>'''
section_fields = {
'menu_name': {'string':'Menu Name', 'type':'char', 'required':True, 'size':64},
'menu_parent_id': {'string':'Parent Menu', 'type':'many2one', 'relation':'ir.ui.menu', 'required':True},
}
def check_views(self, cr, uid, data, context):
pool = pooler.get_pool(cr.dbname)
board = pool.get('board.board').browse(cr, uid, data['id'])
if not board.line_ids:
raise wizard.except_wizard(_('User Error!'),_('Please Insert Dashboard View(s) !'))
return data['form']
def board_menu_create(self, cr, uid, data, context):
pool = pooler.get_pool(cr.dbname)
board = pool.get('board.board').browse(cr, uid, data['id'])
action_id = pool.get('ir.actions.act_window').create(cr, uid, {
'name': board.name,
'view_type':'form',
'view_mode':'form',
'res_model': 'board.board',
'view_id': board.view_id.id,
})
pool.get('ir.ui.menu').create(cr, uid, {
'name': data['form']['menu_name'],
'parent_id': data['form']['menu_parent_id'],
'icon': 'STOCK_SELECT_COLOR',
'action': 'ir.actions.act_window,'+str(action_id)
}, context)
return {}
class wizard_section_menu_create(wizard.interface):
states = {
'init': {
'actions': [check_views],
'result': {'type':'form', 'arch':section_form, 'fields':section_fields, 'state':[('end','Cancel'),('create_menu','Create Menu')]}
},
'create_menu': {
'actions': [board_menu_create],
'result': {
'type':'state',
'state':'end'
}
}
}
wizard_section_menu_create('board.board.menu.create')
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -27,6 +27,7 @@ __author__ = AUTHOR
from BaseHTTPServer import BaseHTTPRequestHandler
import os
class BufferedHTTPRequestHandler(BaseHTTPRequestHandler):
"""
Buffering HTTP Request Handler
@ -47,12 +48,12 @@ class BufferedHTTPRequestHandler(BaseHTTPRequestHandler):
If you override the handle() method remember to call
this (see below)
"""
self.__buffer=""
self.__outfp=os.tmpfile()
self.__buffer = ""
self.__outfp = os.tmpfile()
def _append(self,s):
""" append a string to the buffer """
self.__buffer=self.__buffer+s
self.__buffer = self.__buffer+s
def _flush(self):
""" flush the buffer to wfile """
@ -60,7 +61,7 @@ class BufferedHTTPRequestHandler(BaseHTTPRequestHandler):
self.__outfp.write(self.__buffer)
self.__outfp.flush()
self.wfile.flush()
self.__buffer=""
self.__buffer = ""
def handle(self):
""" Handle a HTTP request """
@ -97,3 +98,4 @@ class BufferedHTTPRequestHandler(BaseHTTPRequestHandler):
protocol_version="HTTP/1.1"
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -1,3 +1,5 @@
#!/usr/bin/env python
"""
Python WebDAV Server.
Copyright (C) 1999 Christian Scholz (ruebe@aachen.heimat.de)
@ -23,12 +25,20 @@ Subclass this class and specify an IFACE_CLASS. See example.
"""
DEBUG=None
DEBUG = None
from utils import VERSION, AUTHOR
__version__ = VERSION
__author__ = AUTHOR
from propfind import PROPFIND
from delete import DELETE
from davcopy import COPY
from davmove import MOVE
from string import atoi, split
from status import STATUS_CODES
from errors import *
import os
import sys
@ -39,16 +49,6 @@ import posixpath
import base64
import urlparse
import urllib
from propfind import PROPFIND
from delete import DELETE
from davcopy import COPY
from davmove import MOVE
from string import atoi,split
from status import STATUS_CODES
from errors import *
import BaseHTTPServer
class DAVRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
@ -74,21 +74,21 @@ class DAVRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
def _log(self, message):
pass
def _append(self,s):
def _append(self, s):
""" write the string to wfile """
self.wfile.write(s)
def send_body(self,DATA,code,msg,desc,ctype='application/octet-stream',headers=None):
def send_body(self, DATA, code, msg, desc, ctype='application/octet-stream', headers=None):
""" send a body in one part """
if not headers:
headers = {}
self.send_response(code,message=msg)
self.send_response(code, message=msg)
self.send_header("Connection", "keep-alive")
self.send_header("Accept-Ranges", "bytes")
for a,v in headers.items():
self.send_header(a,v)
for a, v in headers.items():
self.send_header(a, v)
if DATA:
self.send_header("Content-Length", str(len(DATA)))
@ -100,11 +100,11 @@ class DAVRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
if DATA:
self._append(DATA)
def send_body_chunks(self,DATA,code,msg,desc,ctype='text/xml; encoding="utf-8"'):
def send_body_chunks(self, DATA, code, msg, desc, ctype='text/xml; encoding="utf-8"'):
""" send a body in chunks """
self.responses[207]=(msg,desc)
self.send_response(code,message=msg)
self.responses[207]=(msg, desc)
self.send_response(code, message=msg)
self.send_header("Content-type", ctype)
self.send_header("Connection", "keep-alive")
self.send_header("Transfer-Encoding", "chunked")
@ -128,12 +128,12 @@ class DAVRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
def do_PROPFIND(self):
dc=self.IFACE_CLASS
dc = self.IFACE_CLASS
# read the body
body=None
body = None
if self.headers.has_key("Content-Length"):
l=self.headers['Content-Length']
body=self.rfile.read(atoi(l))
l = self.headers['Content-Length']
body = self.rfile.read(atoi(l))
alt_body = """<?xml version="1.0" encoding="utf-8"?>
<propfind xmlns="DAV:"><prop>
<getcontentlength xmlns="DAV:"/>
@ -149,26 +149,26 @@ class DAVRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
# which Depth?
if self.headers.has_key('Depth'):
d=self.headers['Depth']
d = self.headers['Depth']
else:
d="infinity"
d = "infinity"
uri=self.geturi()
pf=PROPFIND(uri,dc,d)
uri = self.geturi()
pf = PROPFIND(uri, dc, d)
if body:
pf.read_propfind(body)
try:
DATA=pf.createResponse()
DATA=DATA+"\n"
DATA = pf.createResponse()
DATA = DATA+"\n"
# print "Data:", DATA
except DAV_NotFound,(ec,dd):
except DAV_NotFound, (ec, dd):
return self.send_notFound(dd, uri)
except DAV_Error, (ec,dd):
return self.send_error(ec,dd)
except DAV_Error, (ec, dd):
return self.send_error(ec, dd)
self.send_body_chunks(DATA,207,"Multi-Status","Multiple responses")
self.send_body_chunks(DATA, 207, "Multi-Status", "Multiple responses")
def geturi(self):
buri = self.IFACE_CLASS.baseuri
@ -179,110 +179,110 @@ class DAVRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
def do_GET(self):
"""Serve a GET request."""
dc=self.IFACE_CLASS
uri=self.geturi()
dc = self.IFACE_CLASS
uri = self.geturi()
# get the last modified date
try:
lm=dc.get_prop(uri,"DAV:","getlastmodified")
lm = dc.get_prop(uri, "DAV:", "getlastmodified")
except:
lm="Sun, 01 Dec 2014 00:00:00 GMT" # dummy!
headers={"Last-Modified":lm , "Connection": "keep-alive"}
lm = "Sun, 01 Dec 2014 00:00:00 GMT" # dummy!
headers = {"Last-Modified":lm , "Connection": "keep-alive"}
# get the content type
try:
ct=dc.get_prop(uri,"DAV:","getcontenttype")
ct = dc.get_prop(uri, "DAV:", "getcontenttype")
except:
ct="application/octet-stream"
ct = "application/octet-stream"
# get the data
try:
data=dc.get_data(uri)
except DAV_Error, (ec,dd):
data = dc.get_data(uri)
except DAV_Error, (ec, dd):
self.send_status(ec)
return
# send the data
self.send_body(data,200,"OK","OK",ct,headers)
self.send_body(data, 200, "OK", "OK", ct, headers)
def do_HEAD(self):
""" Send a HEAD response """
dc=self.IFACE_CLASS
uri=self.geturi()
dc = self.IFACE_CLASS
uri = self.geturi()
# get the last modified date
try:
lm=dc.get_prop(uri,"DAV:","getlastmodified")
lm = dc.get_prop(uri, "DAV:", "getlastmodified")
except:
lm="Sun, 01 Dec 2014 00:00:00 GMT" # dummy!
lm = "Sun, 01 Dec 2014 00:00:00 GMT" # dummy!
headers={"Last-Modified":lm, "Connection": "keep-alive"}
headers = {"Last-Modified":lm, "Connection": "keep-alive"}
# get the content type
try:
ct=dc.get_prop(uri,"DAV:","getcontenttype")
ct = dc.get_prop(uri, "DAV:", "getcontenttype")
except:
ct="application/octet-stream"
ct = "application/octet-stream"
try:
data=dc.get_data(uri)
headers["Content-Length"]=str(len(data))
data = dc.get_data(uri)
headers["Content-Length"] = str(len(data))
except DAV_NotFound:
self.send_body(None,404,"Not Found","")
self.send_body(None, 404, "Not Found", "")
return
self.send_body(None,200,"OK","OK",ct,headers)
self.send_body(None, 200, "OK", "OK", ct, headers)
def do_POST(self):
self.send_error(404,"File not found")
self.send_error(404, "File not found")
def do_MKCOL(self):
""" create a new collection """
dc=self.IFACE_CLASS
uri=self.geturi()
dc = self.IFACE_CLASS
uri = self.geturi()
try:
res = dc.mkcol(uri)
if res:
self.send_body(None,201,"Created",'')
self.send_body(None, 201, "Created", '')
else:
self.send_body(None,415,"Cannot create",'')
self.send_body(None, 415, "Cannot create", '')
#self.send_header("Connection", "keep-alive")
# Todo: some content, too
except DAV_Error, (ec,dd):
self.send_body(None,int(ec),dd,dd)
except DAV_Error, (ec, dd):
self.send_body(None, int(ec), dd, dd)
def do_DELETE(self):
""" delete an resource """
dc=self.IFACE_CLASS
uri=self.geturi()
dl=DELETE(uri,dc)
dc = self.IFACE_CLASS
uri = self.geturi()
dl = DELETE(uri, dc)
if dc.is_collection(uri):
res=dl.delcol()
res = dl.delcol()
else:
res=dl.delone()
res = dl.delone()
if res:
self.send_status(207,body=res)
self.send_status(207, body=res)
else:
self.send_status(204)
def do_PUT(self):
dc=self.IFACE_CLASS
dc = self.IFACE_CLASS
# read the body
body=None
body = None
if self.headers.has_key("Content-Length"):
l=self.headers['Content-Length']
body=self.rfile.read(atoi(l))
uri=self.geturi()
l = self.headers['Content-Length']
body = self.rfile.read(atoi(l))
uri = self.geturi()
ct=None
ct = None
if self.headers.has_key("Content-Type"):
ct=self.headers['Content-Type']
ct = self.headers['Content-Type']
try:
dc.put(uri,body,ct)
except DAV_Error, (ec,dd):
dc.put(uri, body, ct)
except DAV_Error, (ec, dd):
self.send_status(ec)
return
self.send_status(201)
@ -291,84 +291,84 @@ class DAVRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
""" copy one resource to another """
try:
self.copymove(COPY)
except DAV_Error, (ec,dd):
except DAV_Error, (ec, dd):
self.send_status(ec)
def do_MOVE(self):
""" move one resource to another """
try:
self.copymove(MOVE)
except DAV_Error, (ec,dd):
except DAV_Error, (ec, dd):
self.send_status(ec)
def copymove(self,CLASS):
def copymove(self, CLASS):
""" common method for copying or moving objects """
dc=self.IFACE_CLASS
dc = self.IFACE_CLASS
# get the source URI
source_uri=self.geturi()
source_uri = self.geturi()
# get the destination URI
dest_uri=self.headers['Destination']
dest_uri=urllib.unquote(dest_uri)
dest_uri = self.headers['Destination']
dest_uri = urllib.unquote(dest_uri)
# Overwrite?
overwrite=1
result_code=204
overwrite = 1
result_code = 204
if self.headers.has_key("Overwrite"):
if self.headers['Overwrite']=="F":
overwrite=None
result_code=201
# instanciate ACTION class
cp=CLASS(dc,source_uri,dest_uri,overwrite)
cp = CLASS(dc, source_uri, dest_uri, overwrite)
# Depth?
d="infinity"
d = "infinity"
if self.headers.has_key("Depth"):
d=self.headers['Depth']
d = self.headers['Depth']
if d!="0" and d!="infinity":
self.send_status(400)
return
if d=="0":
res=cp.single_action()
res = cp.single_action()
self.send_status(res)
return
# now it only can be "infinity" but we nevertheless check for a collection
if dc.is_collection(source_uri):
try:
res=cp.tree_action()
except DAV_Error, (ec,dd):
res = cp.tree_action()
except DAV_Error, (ec, dd):
self.send_status(ec)
return
else:
try:
res=cp.single_action()
except DAV_Error, (ec,dd):
res = cp.single_action()
except DAV_Error, (ec, dd):
self.send_status(ec)
return
if res:
self.send_body_chunks(res,207,STATUS_CODES[207],STATUS_CODES[207],
self.send_body_chunks(res, 207, STATUS_CODES[207], STATUS_CODES[207],
ctype='text/xml; charset="utf-8"')
else:
self.send_status(result_code)
def get_userinfo(self,user,pw):
def get_userinfo(self, user, pw):
""" Dummy method which lets all users in """
return 1
def send_status(self,code=200,mediatype='text/xml; charset="utf-8"', \
msg=None,body=None):
def send_status(self, code=200, mediatype='text/xml; charset="utf-8"', \
msg=None, body=None):
if not msg: msg=STATUS_CODES[code]
self.send_body(body,code,STATUS_CODES[code],msg,mediatype)
if not msg: msg = STATUS_CODES[code]
self.send_body(body, code, STATUS_CODES[code], msg, mediatype)
def send_notFound(self,descr,uri):
def send_notFound(self, descr, uri):
body = """<?xml version="1.0" encoding="utf-8" ?>
<D:response xmlns:D="DAV:">
<D:href>%s</D:href>
@ -376,4 +376,6 @@ class DAVRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
<D:responsedescription>%s</D:responsedescription>
</D:response>
"""
return self.send_status(404,descr, body=body % (uri,descr))
return self.send_status(404, descr, body=body % (uri, descr))
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -14,8 +14,8 @@ import urlparse
from utils import create_treelist, is_prefix
from errors import *
def deltree(dc,uri,exclude={}):
""" delete a tree of resources
def deltree(dc, uri, exclude={}):
""" delete a tree of resources
dc -- dataclass to use
uri -- root uri to delete
@ -30,43 +30,43 @@ def deltree(dc,uri,exclude={}):
"""
tlist=create_treelist(dc,uri)
result={}
tlist = create_treelist(dc,uri)
result = {}
for i in range(len(tlist),0,-1):
problem_uris=result.keys()
element=tlist[i-1]
problem_uris = result.keys()
element = tlist[i-1]
# test here, if an element is a prefix of an uri which
# generated an error before.
# note that we walk here from childs to parents, thus
# we cannot delete a parent if a child made a problem.
# (see example in 8.6.2.1)
ok=1
ok = 1
for p in problem_uris:
if is_prefix(element,p):
ok=None
ok = None
break
if not ok: continue
if not ok: continue
# here we test for the exclude list which is the other way round!
for p in exclude.keys():
if is_prefix(p,element):
ok=None
ok = None
break
if not ok: continue
if not ok: continue
# now delete stuff
try:
delone(dc,element)
except DAV_Error, (ec,dd):
result[element]=ec
except DAV_Error, (ec,dd):
result[element] = ec
return result
def delone(dc,uri):
def delone(dc, uri):
""" delete a single object """
if dc.is_collection(uri):
dc.rmcol(uri) # should be empty
@ -79,12 +79,12 @@ def delone(dc,uri):
# helper function
def copy(dc,src,dst):
""" only copy the element
def copy(dc, src, dst):
""" only copy the element
This is just a helper method factored out from copy and
copytree. It will not handle the overwrite or depth header.
"""
# destination should have been deleted before
@ -101,28 +101,28 @@ def copy(dc,src,dst):
# the main functions
def copyone(dc,src,dst,overwrite=None):
def copyone(dc, src, dst, overwrite=None):
""" copy one resource to a new destination """
if overwrite and dc.exists(dst):
delres=deltree(dc,dst)
delres = deltree(dc,dst)
else:
delres={}
delres = {}
# if we cannot delete everything, then do not copy!
if delres: return delres
try:
copy(dc,src,dst) # pass thru exceptions
except DAV_Error, (ec,dd):
return ec
def copytree(dc,src,dst,overwrite=None):
def copytree(dc, src, dst, overwrite=None):
""" copy a tree of resources to another location
dc -- dataclass to use
src -- src uri from where to copy
dst -- dst uri
dst -- dst uri
overwrite -- if 1 then delete dst uri before
returns dict of uri:error_code tuples from which
@ -133,59 +133,59 @@ def copytree(dc,src,dst,overwrite=None):
# first delete the destination resource
if overwrite and dc.exists(dst):
delres=deltree(dc,dst)
delres = deltree(dc,dst)
else:
delres={}
delres = {}
# if we cannot delete everything, then do not copy!
if delres: return delres
# get the tree we have to copy
tlist=create_treelist(dc,src)
result={}
tlist = create_treelist(dc,src)
result = {}
# prepare destination URIs (get the prefix)
dpath=urlparse.urlparse(dst)[2]
dpath = urlparse.urlparse(dst)[2]
for element in tlist:
problem_uris=result.keys()
problem_uris = result.keys()
# now URIs get longer and longer thus we have
# to test if we had a parent URI which we were not
# able to copy in problem_uris which is the prefix
# of the actual element. If it is, then we cannot
# copy this as well but do not generate another error.
ok=1
ok = 1
for p in problem_uris:
if is_prefix(p,element):
ok=None
ok = None
break
if not ok: continue
if not ok: continue
# now create the destination URI which corresponds to
# the actual source URI. -> actual_dst
# ("subtract" the base src from the URI and prepend the
# dst prefix to it.)
esrc=replace(element,src,"")
actual_dst=dpath+esrc
esrc = replace(element,src,"")
actual_dst = dpath+esrc
# now copy stuff
try:
copy(dc,element,actual_dst)
except DAV_Error, (ec,dd):
result[element]=ec
except DAV_Error, (ec,dd):
result[element] = ec
return result
###
### MOVE
###
def moveone(dc,src,dst,overwrite=None):
def moveone(dc, src, dst, overwrite=None):
""" move a single resource
This is done by first copying it and then deleting
@ -197,8 +197,8 @@ def moveone(dc,src,dst,overwrite=None):
# then delete it
dc.rm(src)
def movetree(dc,src,dst,overwrite=None):
def movetree(dc, src, dst, overwrite=None):
""" move a collection
This is done by first copying it and then deleting
@ -209,10 +209,11 @@ def movetree(dc,src,dst,overwrite=None):
"""
# first copy it
res=copytree(dc,src,dst,overwrite)
res = copytree(dc,src,dst,overwrite)
# then delete it
res=deltree(dc,src,exclude=res)
res = deltree(dc,src,exclude=res)
return res
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -47,11 +47,11 @@ class COPY:
"""
def __init__(self,dataclass,src_uri,dst_uri,overwrite):
self.__dataclass=dataclass
self.__src=src_uri
self.__dst=dst_uri
self.__overwrite=overwrite
def __init__(self, dataclass, src_uri, dst_uri, overwrite):
self.__dataclass = dataclass
self.__src = src_uri
self.__dst = dst_uri
self.__overwrite = overwrite
def single_action(self):
@ -62,16 +62,16 @@ class COPY:
"""
dc=self.__dataclass
base=self.__src
dc = self.__dataclass
base = self.__src
### some basic tests
# test if dest exists and overwrite is false
if dc.exists(self.__dst) and not self.__overwrite: raise DAV_Error, 412
# test if src and dst are the same
# (we assume that both uris are on the same server!)
ps=urlparse.urlparse(self.__src)[2]
pd=urlparse.urlparse(self.__dst)[2]
ps = urlparse.urlparse(self.__src)[2]
pd = urlparse.urlparse(self.__dst)[2]
if ps==pd: raise DAV_Error, 403
return dc.copyone(self.__src,self.__dst,self.__overwrite)
@ -84,20 +84,20 @@ class COPY:
Here we return a multistatus xml element.
"""
dc=self.__dataclass
base=self.__src
dc = self.__dataclass
base = self.__src
### some basic tests
# test if dest exists and overwrite is false
if dc.exists(self.__dst) and not self.__overwrite: raise DAV_Error, 412
# test if src and dst are the same
# (we assume that both uris are on the same server!)
ps=urlparse.urlparse(self.__src)[2]
pd=urlparse.urlparse(self.__dst)[2]
ps = urlparse.urlparse(self.__src)[2]
pd = urlparse.urlparse(self.__dst)[2]
if ps==pd: raise DAV_Error, 403
result=dc.copytree(self.__src,self.__dst,self.__overwrite)
result = dc.copytree(self.__src,self.__dst,self.__overwrite)
#result=copytree(dc,self.__src,self.__dst,self.__overwrite)
if not result: return None
@ -109,25 +109,26 @@ class COPY:
###
doc = Document(None)
ms=doc.createElement("D:multistatus")
ms = doc.createElement("D:multistatus")
ms.setAttribute("xmlns:D","DAV:")
doc.appendChild(ms)
for el,ec in result.items():
re=doc.createElement("D:response")
hr=doc.createElement("D:href")
st=doc.createElement("D:status")
huri=doc.createTextNode(quote_uri(el))
t=doc.createTextNode(gen_estring(ec))
re = doc.createElement("D:response")
hr = doc.createElement("D:href")
st = doc.createElement("D:status")
huri = doc.createTextNode(quote_uri(el))
t = doc.createTextNode(gen_estring(ec))
st.appendChild(t)
hr.appendChild(huri)
re.appendChild(hr)
re.appendChild(st)
ms.appendChild(re)
sfile=StringIO()
ext.PrettyPrint(doc,stream=sfile)
s=sfile.getvalue()
sfile = StringIO()
ext.PrettyPrint(doc,stream = sfile)
s = sfile.getvalue()
sfile.close()
return s
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -39,19 +39,19 @@ class MOVE:
""" move resources and eventually create multistatus responses
This module implements the MOVE class which is responsible for
moving resources.
moving resources.
MOVE is implemented by a COPY followed by a DELETE of the old
resource.
resource.
"""
def __init__(self,dataclass,src_uri,dst_uri,overwrite):
self.__dataclass=dataclass
self.__src=src_uri
self.__dst=dst_uri
self.__overwrite=overwrite
def __init__(self, dataclass, src_uri, dst_uri, overwrite):
self.__dataclass = dataclass
self.__src = src_uri
self.__dst = dst_uri
self.__overwrite = overwrite
def single_action(self):
@ -62,16 +62,16 @@ class MOVE:
"""
dc=self.__dataclass
base=self.__src
dc = self.__dataclass
base = self.__src
### some basic tests
# test if dest exists and overwrite is false
if dc.exists(self.__dst) and not self.__overwrite: raise DAV_Error, 412
# test if src and dst are the same
# (we assume that both uris are on the same server!)
ps=urlparse.urlparse(self.__src)[2]
pd=urlparse.urlparse(self.__dst)[2]
ps = urlparse.urlparse(self.__src)[2]
pd = urlparse.urlparse(self.__dst)[2]
if ps==pd: raise DAV_Error, 403
return dc.moveone(self.__src,self.__dst,self.__overwrite)
@ -82,21 +82,22 @@ class MOVE:
Here we return a multistatus xml element.
"""
dc=self.__dataclass
base=self.__src
dc = self.__dataclass
base = self.__src
### some basic tests
# test if dest exists and overwrite is false
if dc.exists(self.__dst) and not self.__overwrite: raise DAV_Error, 412
# test if src and dst are the same
# (we assume that both uris are on the same server!)
ps=urlparse.urlparse(self.__src)[2]
pd=urlparse.urlparse(self.__dst)[2]
ps = urlparse.urlparse(self.__src)[2]
pd = urlparse.urlparse(self.__dst)[2]
if ps==pd: raise DAV_Error, 403
result=dc.movetree(self.__src,self.__dst,self.__overwrite)
result = dc.movetree(self.__src,self.__dst,self.__overwrite)
if not result: return None
# create the multistatus XML element
return make_xmlresponse(result)
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -32,15 +32,15 @@ from davcmd import deltree
class DELETE:
def __init__(self,uri,dataclass):
self.__dataclass=dataclass
self.__uri=uri
def __init__(self, uri, dataclass):
self.__dataclass = dataclass
self.__uri = uri
def delcol(self):
""" delete a collection """
dc=self.__dataclass
result=dc.deltree(self.__uri)
dc = self.__dataclass
result = dc.deltree(self.__uri)
if not len(result.items()):
return None # everything ok
@ -51,9 +51,9 @@ class DELETE:
def delone(self):
""" delete a resource """
dc=self.__dataclass
result=dc.delone(self.__uri)
dc = self.__dataclass
result = dc.delone(self.__uri)
if not result: return None
if not len(result.items()):
return None # everything ok
@ -61,3 +61,4 @@ class DELETE:
# create the result element
return make_xmlresponse(result)
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -12,15 +12,15 @@ class DAV_Error(Exception):
2. the error result element, e.g. a <multistatus> element
"""
def __init__(self,*args):
def __init__(self, *args):
if len(args)==1:
self.args=(args[0],"")
self.args = (args[0],"")
else:
self.args=args
self.args = args
class DAV_Secret(DAV_Error):
""" the user is not allowed to know anything about it
returning this for a property value means to exclude it
from the response xml element.
"""
@ -31,10 +31,10 @@ class DAV_Secret(DAV_Error):
class DAV_NotFound(DAV_Error):
""" a requested property was not found for a resource """
def __init__(self,*args):
def __init__(self, *args):
if len(args):
if isinstance(args[0],list):
if isinstance(args[0], list):
stre = "Path %s not found!"%('/'.join(args[0]))
else:
stre = args[0]
@ -46,11 +46,12 @@ class DAV_NotFound(DAV_Error):
class DAV_Forbidden(DAV_Error):
""" a method on a resource is not allowed """
def __init__(self,*args):
def __init__(self, *args):
if len(args):
DAV_Error.__init__(self,403,args[0])
else:
DAV_Error.__init__(self,403)
pass
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -18,16 +18,16 @@ class dav_interface:
### defined properties (modify this but let the DAV stuff there!)
### the format is namespace: [list of properties]
PROPS={"DAV:" : ('creationdate',
'displayname',
'getcontentlanguage',
'getcontentlength',
'getcontenttype',
'getetag',
'getlastmodified',
'lockdiscovery',
'resourcetype',
'source',
PROPS={"DAV:" : ('creationdate',
'displayname',
'getcontentlanguage',
'getcontentlength',
'getcontenttype',
'getetag',
'getlastmodified',
'lockdiscovery',
'resourcetype',
'source',
'supportedlock'),
"NS2" : ("p1","p2")
}
@ -36,28 +36,28 @@ class dav_interface:
# the first item is the namespace URI and the second one
# the method prefix
# e.g. for DAV:getcontenttype we call dav_getcontenttype()
M_NS={"DAV:" : "_get_dav",
M_NS = {"DAV:" : "_get_dav",
"NS2" : "ns2" }
def get_propnames(self,uri):
""" return the property names allowed for the given URI
def get_propnames(self, uri):
""" return the property names allowed for the given URI
In this method we simply return the above defined properties
assuming that they are valid for any resource.
assuming that they are valid for any resource.
You can override this in order to return a different set
of property names for each resource.
"""
return self.PROPS
def get_prop2(self,uri,ns,pname):
""" return the value of a property
def get_prop2(self, uri, ns, pname):
""" return the value of a property
"""
if lower(ns)=="dav:": return self.get_dav(uri,pname)
raise DAV_NotFound
def get_prop(self,uri,ns,propname):
def get_prop(self, uri, ns, propname):
""" return the value of a given property
uri -- uri of the object to get the property of
@ -65,17 +65,17 @@ class dav_interface:
pname -- name of the property
"""
if self.M_NS.has_key(ns):
prefix=self.M_NS[ns]
prefix = self.M_NS[ns]
else:
print "No namespace:",ns, "( for prop:", propname,")"
raise DAV_NotFound
mname=prefix+"_"+propname
mname = prefix+"_"+propname
if not hasattr(self,mname):
raise DAV_NotFound
try:
m=getattr(self,mname)
r=m(uri)
m = getattr(self,mname)
r = m(uri)
return r
except AttributeError, e:
print 'Property %s not supported' % propname
@ -86,16 +86,16 @@ class dav_interface:
### DATA methods (for GET and PUT)
###
def get_data(self,uri):
""" return the content of an object
def get_data(self, uri):
""" return the content of an object
return data or raise an exception
"""
raise DAV_NotFound
def put(self,uri,data):
""" write an object to the repository
def put(self, uri, data):
""" write an object to the repository
return a result code or raise an exception
"""
@ -106,17 +106,17 @@ class dav_interface:
### Methods for DAV properties
###
def _get_dav_creationdate(self,uri):
def _get_dav_creationdate(self, uri):
""" return the creationdate of a resource """
d=self.get_creationdate(uri)
d = self.get_creationdate(uri)
# format it
if isinstance(d, int) or isinstance(d, float):
d = time.localtimetime(d)
return time.strftime("%Y-%m-%dT%H:%M:%S%Z",d)
def _get_dav_getlastmodified(self,uri):
def _get_dav_getlastmodified(self, uri):
""" return the last modified date of a resource """
d=self.get_lastmodified(uri)
d = self.get_lastmodified(uri)
if isinstance(d, int) or isinstance(d, float):
d = time.localtime(d)
# format it
@ -127,37 +127,37 @@ class dav_interface:
### OVERRIDE THESE!
###
def get_creationdate(self,uri):
def get_creationdate(self, uri):
""" return the creationdate of the resource """
return time.time()
def get_lastmodified(self,uri):
def get_lastmodified(self, uri):
""" return the last modification date of the resource """
return time.time()
###
### COPY MOVE DELETE
###
### methods for deleting a resource
def rmcol(self,uri):
""" delete a collection
def rmcol(self, uri):
""" delete a collection
This should not delete any children! This is automatically done
before by the DELETE class in DAV/delete.py
return a success code or raise an exception
"""
raise DAV_NotFound
def rm(self,uri):
""" delete a single resource
def rm(self, uri):
""" delete a single resource
return a success code or raise an exception
"""
raise DAV_NotFound
@ -182,7 +182,7 @@ class dav_interface:
1. to handle the action directly (e.g. cp or mv on filesystems)
2. to let it handle via the copy/move methods in davcmd.
ad 1) The first approach can be used when we know that no error can
ad 1) The first approach can be used when we know that no error can
happen inside a tree or when the action can exactly tell which
element made which error. We have to collect these and return
it in a dict of the form {uri: error_code, ...}
@ -210,28 +210,28 @@ class dav_interface:
### MOVE handlers
def moveone(self,src,dst,overwrite):
def moveone(self, src, dst, overwrite):
""" move one resource with Depth=0 """
return moveone(self,src,dst,overwrite)
return moveone(self, src, dst, overwrite)
def movetree(self,src,dst,overwrite):
def movetree(self, src, dst, overwrite):
""" move a collection with Depth=infinity """
return movetree(self,src,dst,overwrite)
return movetree(self, src, dst, overwrite)
### COPY handlers
def copyone(self,src,dst,overwrite):
def copyone(self, src, dst, overwrite):
""" copy one resource with Depth=0 """
return copyone(self,src,dst,overwrite)
return copyone(self, src, dst, overwrite)
def copytree(self,src,dst,overwrite):
def copytree(self, src, dst, overwrite):
""" copy a collection with Depth=infinity """
return copytree(self,src,dst,overwrite)
return copytree(self, src, dst, overwrite)
### low level copy methods (you only need these for method 2)
def copy(self,src,dst):
""" copy a resource with depth==0
def copy(self, src, dst):
""" copy a resource with depth==0
You don't need to bother about overwrite or not.
This has been done already.
@ -241,8 +241,8 @@ class dav_interface:
return 201
def copycol(self,src,dst):
""" copy a resource with depth==infinity
def copycol(self, src, dst):
""" copy a resource with depth==infinity
You don't need to bother about overwrite or not.
This has been done already.
@ -253,11 +253,12 @@ class dav_interface:
### some utility functions you need to implement
def exists(self,uri):
def exists(self, uri):
""" return 1 or None depending on if a resource exists """
return None # no
def is_collection(self,uri):
def is_collection(self, uri):
""" return 1 or None depending on if a resource is a collection """
return None # no
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -18,8 +18,8 @@
License along with this library; if not, write to the Free
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
"""
"""
from xml.dom import ext
from xml.dom.Document import Document
@ -45,9 +45,9 @@ class PROPFIND:
It will set the following instance vars:
request_class : ALLPROP | PROPNAME | PROP
proplist : list of properties
nsmap : map of namespaces
request_class: ALLPROP | PROPNAME | PROP
proplist: list of properties
nsmap: map of namespaces
The list of properties will contain tuples of the form
(element name, ns_prefix, ns_uri)
@ -55,21 +55,20 @@ class PROPFIND:
"""
def __init__(self, uri, dataclass, depth):
self.request_type = None
self.nsmap = {}
self.proplist = {}
self.default_ns = None
self.__dataclass = dataclass
self.__depth = str(depth)
self.__uri = uri
self.use_full_urls = True
self.__has_body = None # did we parse a body?
def __init__(self,uri,dataclass,depth):
self.request_type=None
self.nsmap={}
self.proplist={}
self.default_ns=None
self.__dataclass=dataclass
self.__depth=str(depth)
self.__uri=uri
self.use_full_urls=True
self.__has_body=None # did we parse a body?
def read_propfind(self, xml_doc):
self.request_type,self.proplist,self.namespaces = utils.parse_propfind(xml_doc)
def read_propfind(self,xml_doc):
self.request_type,self.proplist,self.namespaces=utils.parse_propfind(xml_doc)
# a violation of the expected logic: client (korganizer) will ask for DAV:resourcetype
# but we also have to return the http://groupdav.org/:resourcetype property!
if self.proplist.has_key('DAV:') and 'resourcetype' in self.proplist['DAV:']:
@ -91,7 +90,7 @@ class PROPFIND:
If we get an ALLPROP we first get the list of properties and then
we do the same as with a PROP method.
If the uri doesn't exist, return an xml response with a 404 status
"""
@ -114,41 +113,41 @@ class PROPFIND:
def create_propname(self):
""" create a multistatus response for the prop names """
dc=self.__dataclass
dc = self.__dataclass
# create the document generator
doc = Document(None)
ms=doc.createElement("D:multistatus")
ms = doc.createElement("D:multistatus")
ms.setAttribute("xmlns:D","DAV:")
doc.appendChild(ms)
if self.__depth=="0":
pnames=dc.get_propnames(self.__uri)
re=self.mk_propname_response(self.__uri,pnames,doc)
pnames = dc.get_propnames(self.__uri)
re = self.mk_propname_response(self.__uri,pnames,doc)
ms.appendChild(re)
elif self.__depth=="1":
pnames=dc.get_propnames(self.__uri)
re=self.mk_propname_response(self.__uri,pnames,doc)
pnames = dc.get_propnames(self.__uri)
re = self.mk_propname_response(self.__uri,pnames,doc)
ms.appendChild(re)
for newuri in dc.get_childs(self.__uri):
pnames=dc.get_propnames(newuri)
re=self.mk_propname_response(newuri,pnames,doc)
pnames = dc.get_propnames(newuri)
re = self.mk_propname_response(newuri,pnames,doc)
ms.appendChild(re)
# *** depth=="infinity"
sfile=StringIO()
ext.PrettyPrint(doc,stream=sfile)
s=sfile.getvalue()
sfile = StringIO()
ext.PrettyPrint(doc,stream = sfile)
s = sfile.getvalue()
sfile.close()
return s
def create_allprop(self):
""" return a list of all properties """
self.proplist={}
self.namespaces=[]
self.proplist = {}
self.namespaces = []
for ns,plist in self.__dataclass.get_propnames(self.__uri).items():
self.proplist[ns]=plist
self.proplist[ns] = plist
self.namespaces.append(ns)
return self.create_prop()
@ -178,32 +177,32 @@ class PROPFIND:
# create the document generator
doc = Document(None)
ms=doc.createElement("D:multistatus")
ms = doc.createElement("D:multistatus")
ms.setAttribute("xmlns:D","DAV:")
doc.appendChild(ms)
if self.__depth=="0":
gp,bp=self.get_propvalues(self.__uri)
res=self.mk_prop_response(self.__uri,gp,bp,doc)
gp,bp = self.get_propvalues(self.__uri)
res = self.mk_prop_response(self.__uri,gp,bp,doc)
ms.appendChild(res)
elif self.__depth=="1":
gp,bp=self.get_propvalues(self.__uri)
res=self.mk_prop_response(self.__uri,gp,bp,doc)
gp,bp = self.get_propvalues(self.__uri)
res = self.mk_prop_response(self.__uri,gp,bp,doc)
ms.appendChild(res)
try:
for newuri in self.__dataclass.get_childs(self.__uri):
gp,bp=self.get_propvalues(newuri)
res=self.mk_prop_response(newuri,gp,bp,doc)
gp,bp = self.get_propvalues(newuri)
res = self.mk_prop_response(newuri,gp,bp,doc)
ms.appendChild(res)
except DAV_NotFound:
# If no children, never mind.
pass
sfile=StringIO()
ext.PrettyPrint(doc,stream=sfile)
s=sfile.getvalue()
sfile = StringIO()
ext.PrettyPrint(doc,stream = sfile)
s = sfile.getvalue()
sfile.close()
return s
@ -215,32 +214,32 @@ class PROPFIND:
propnames should have the format {NS1 : [prop1, prop2, ...], NS2: ...}
"""
re=doc.createElement("D:response")
re = doc.createElement("D:response")
# write href information
href=doc.createElement("D:href")
href = doc.createElement("D:href")
if self.use_full_urls:
huri=doc.createTextNode(uri)
huri = doc.createTextNode(uri)
else:
uparts=urlparse.urlparse(uri)
fileloc=uparts[2]
huri=doc.createTextNode(urllib.quote(fileloc.encode('utf8')))
uparts = urlparse.urlparse(uri)
fileloc = uparts[2]
huri = doc.createTextNode(urllib.quote(fileloc.encode('utf8')))
href.appendChild(huri)
re.appendChild(href)
ps=doc.createElement("D:propstat")
nsnum=0
ps = doc.createElement("D:propstat")
nsnum = 0
for ns,plist in propnames.items():
# write prop element
pr=doc.createElement("D:prop")
nsp="ns"+str(nsnum)
pr = doc.createElement("D:prop")
nsp = "ns"+str(nsnum)
pr.setAttribute("xmlns:"+nsp,ns)
nsnum=nsnum+1
nsnum = nsnum+1
# write propertynames
for p in plist:
pe=doc.createElement(nsp+":"+p)
pe = doc.createElement(nsp+":"+p)
pr.appendChild(pe)
ps.appendChild(pr)
@ -257,33 +256,33 @@ class PROPFIND:
one, that means).
"""
re=doc.createElement("D:response")
re = doc.createElement("D:response")
# append namespaces to response
nsnum=0
nsnum = 0
for nsname in self.namespaces:
re.setAttribute("xmlns:ns"+str(nsnum),nsname)
nsnum=nsnum+1
nsnum = nsnum+1
# write href information
href=doc.createElement("D:href")
href = doc.createElement("D:href")
if self.use_full_urls:
huri=doc.createTextNode(uri)
huri = doc.createTextNode(uri)
else:
uparts=urlparse.urlparse(uri)
fileloc=uparts[2]
huri=doc.createTextNode(urllib.quote(fileloc.encode('utf8')))
uparts = urlparse.urlparse(uri)
fileloc = uparts[2]
huri = doc.createTextNode(urllib.quote(fileloc.encode('utf8')))
href.appendChild(huri)
re.appendChild(href)
# write good properties
if good_props and len(good_props.items()):
ps=doc.createElement("D:propstat")
ps = doc.createElement("D:propstat")
gp=doc.createElement("D:prop")
gp = doc.createElement("D:prop")
for ns in good_props.keys():
ns_prefix="ns"+str(self.namespaces.index(ns))+":"
for p,v in good_props[ns].items():
pe=doc.createElement(ns_prefix+str(p))
pe = doc.createElement(ns_prefix+str(p))
if v == None:
pass
elif ns=='DAV:' and p=="resourcetype":
@ -294,15 +293,15 @@ class PROPFIND:
ve=doc.createElement(ns_prefix+v[0])
pe.appendChild(ve)
else:
ve=doc.createTextNode(utf8str(v))
ve = doc.createTextNode(utf8str(v))
pe.appendChild(ve)
gp.appendChild(pe)
if gp.hasChildNodes():
re.appendChild(ps)
ps.appendChild(gp)
s=doc.createElement("D:status")
t=doc.createTextNode("HTTP/1.1 200 OK")
s = doc.createElement("D:status")
t = doc.createTextNode("HTTP/1.1 200 OK")
s.appendChild(t)
ps.appendChild(s)
re.appendChild(ps)
@ -312,20 +311,20 @@ class PROPFIND:
# write a propstat for each error code
for ecode in bad_props.keys():
ps=doc.createElement("D:propstat")
ps = doc.createElement("D:propstat")
re.appendChild(ps)
bp=doc.createElement("D:prop")
bp = doc.createElement("D:prop")
ps.appendChild(bp)
for ns in bad_props[ecode].keys():
ns_prefix="ns"+str(self.namespaces.index(ns))+":"
ns_prefix = "ns"+str(self.namespaces.index(ns))+":"
for p in bad_props[ecode][ns]:
pe=doc.createElement(ns_prefix+str(p))
pe = doc.createElement(ns_prefix+str(p))
bp.appendChild(pe)
s=doc.createElement("D:status")
t=doc.createTextNode(utils.gen_estring(ecode))
s = doc.createElement("D:status")
t = doc.createTextNode(utils.gen_estring(ecode))
s.appendChild(t)
ps.appendChild(s)
re.appendChild(ps)
@ -342,20 +341,20 @@ class PROPFIND:
found or the user is not allowed to read them.
"""
good_props={}
bad_props={}
good_props = {}
bad_props = {}
for (ns,plist) in self.proplist.items():
good_props[ns]={}
bad_props={}
good_props[ns] = {}
bad_props = {}
ec = 0
for prop in plist:
try:
ec = 0
r=self.__dataclass.get_prop(uri,ns,prop)
good_props[ns][prop]=r
r = self.__dataclass.get_prop(uri,ns,prop)
good_props[ns][prop] = r
except DAV_Error, error_code:
ec=error_code[0]
ec = error_code[0]
# ignore props with error_code if 0 (invisible)
if ec==0: continue
@ -364,9 +363,11 @@ class PROPFIND:
if bad_props[ec].has_key(ns):
bad_props[ec][ns].append(prop)
else:
bad_props[ec][ns]=[prop]
bad_props[ec][ns] = [prop]
else:
bad_props[ec]={ns:[prop]}
bad_props[ec] = {ns:[prop]}
return good_props, bad_props
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -27,7 +27,7 @@ AUTHOR = 'Simon Pamies <s.pamies@banality.de>'
def gen_estring(ecode):
""" generate an error string from the given code """
ec=atoi(str(ecode))
ec = atoi(str(ecode))
if STATUS_CODES.has_key(ec):
return "HTTP/1.1 %s %s" %(ec,STATUS_CODES[ec])
else:
@ -46,20 +46,20 @@ def parse_propfind(xml_doc):
doc = PyExpat.Reader().fromString(xml_doc)
snit = doc.createNodeIterator(doc, NodeFilter.NodeFilter.SHOW_ELEMENT, None, None)
request_type=None
props={}
namespaces=[]
request_type = None
props = {}
namespaces = []
while 1:
curr_elem = snit.nextNode()
if not curr_elem: break
ename=fname=lower(curr_elem.nodeName)
ename=fname = lower(curr_elem.nodeName)
if ":" in fname:
ename=split(fname,":")[1]
if ename=="prop": request_type=RT_PROP; continue
ename = split(fname,":")[1]
if ename=="prop": request_type = RT_PROP; continue
if ename=="propfind": continue
if ename=="allprop": request_type=RT_ALLPROP; continue
if ename=="propname": request_type=RT_PROPNAME; continue
if ename=="allprop": request_type = RT_ALLPROP; continue
if ename=="propname": request_type = RT_PROPNAME; continue
# rest should be names of attributes
@ -67,13 +67,13 @@ def parse_propfind(xml_doc):
if props.has_key(ns):
props[ns].append(ename)
else:
props[ns]=[ename]
props[ns] = [ename]
namespaces.append(ns)
return request_type,props,namespaces
def create_treelist(dataclass,uri):
def create_treelist(dataclass, uri):
""" create a list of resources out of a tree
This function is used for the COPY, MOVE and DELETE methods
@ -83,23 +83,23 @@ def create_treelist(dataclass,uri):
It will return the flattened tree as list
"""
queue=[uri]
list=[uri]
queue = [uri]
list = [uri]
while len(queue):
element=queue[-1]
element = queue[-1]
if dataclass.is_collection(element):
childs=dataclass.get_childs(element)
childs = dataclass.get_childs(element)
else:
childs=[]
childs = []
if len(childs):
list=list+childs
list = list+childs
# update queue
del queue[-1]
if len(childs):
queue=queue+childs
queue = queue+childs
return list
def is_prefix(uri1,uri2):
def is_prefix(uri1, uri2):
""" returns 1 of uri1 is a prefix of uri2 """
if uri2[:len(uri1)]==uri1:
return 1
@ -111,50 +111,51 @@ def quote_uri(uri):
import urlparse
import urllib
up=urlparse.urlparse(uri)
np=urllib.quote(up[2])
return urlparse.urlunparse((up[0],up[1],np,up[3],up[4],up[5]))
up = urlparse.urlparse(uri)
np = urllib.quote(up[2])
return urlparse.urlunparse((up[0], up[1], np, up[3], up[4], up[5]))
def get_uriparentpath(uri):
""" extract the uri path and remove the last element """
up=urlparse.urlparse(uri)
return joinfields(split(up[2],"/")[:-1],"/")
up = urlparse.urlparse(uri)
return joinfields(split(up[2], "/")[:-1], "/")
def get_urifilename(uri):
""" extract the uri path and return the last element """
up=urlparse.urlparse(uri)
return split(up[2],"/")[-1]
up = urlparse.urlparse(uri)
return split(up[2], "/")[-1]
def get_parenturi(uri):
""" return the parent of the given resource"""
up=urlparse.urlparse(uri)
np=joinfields(split(up[2],"/")[:-1],"/")
return urlparse.urlunparse((up[0],up[1],np,up[3],up[4],up[5]))
up = urlparse.urlparse(uri)
np = joinfields(split(up[2], "/")[:-1], "/")
return urlparse.urlunparse((up[0], up[1], np, up[3], up[4], up[5]))
### XML utilities
def make_xmlresponse(result):
""" construct a response from a dict of uri:error_code elements """
doc = Document(None)
ms=doc.createElement("D:multistatus")
ms.setAttribute("xmlns:D","DAV:")
ms = doc.createElement("D:multistatus")
ms.setAttribute("xmlns:D", "DAV:")
doc.appendChild(ms)
for el,ec in result.items():
re=doc.createElement("D:response")
hr=doc.createElement("D:href")
st=doc.createElement("D:status")
huri=doc.createTextNode(quote_uri(el))
t=doc.createTextNode(gen_estring(ec))
for el, ec in result.items():
re = doc.createElement("D:response")
hr = doc.createElement("D:href")
st = doc.createElement("D:status")
huri = doc.createTextNode(quote_uri(el))
t = doc.createTextNode(gen_estring(ec))
st.appendChild(t)
hr.appendChild(huri)
re.appendChild(hr)
re.appendChild(st)
ms.appendChild(re)
sfile=StringIO()
ext.PrettyPrint(doc,stream=sfile)
s=sfile.getvalue()
sfile = StringIO()
ext.PrettyPrint(doc, stream = sfile)
s = sfile.getvalue()
sfile.close()
return s
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
##############################################################################
#
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
#
@ -15,7 +15,7 @@
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################

View File

@ -49,7 +49,9 @@
"demo_xml" : [],
"update_xml" : [
'security/ir.model.access.csv',
'caldav_wizard.xml',
'wizard/calendar_event_export_view.xml',
'wizard/calendar_event_import_view.xml',
'wizard/calendar_event_subscribe_view.xml',
'caldav_view.xml'
],
"installable" : True,

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
##############################################################################
#
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
#
@ -15,7 +15,7 @@
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
@ -36,14 +36,21 @@ import time
try:
import vobject
except ImportError:
raise osv.except_osv('vobject Import Error!','Please install python-vobject from http://vobject.skyhouseconsulting.com/')
raise osv.except_osv('vobject Import Error!','Please install python-vobject \
from http://vobject.skyhouseconsulting.com/')
# O-1 Optional and can come only once
# O-n Optional and can come more than once
# R-1 Required and can come only once
# R-n Required and can come more than once
def uid2openobjectid(cr, uidval, oomodel, rdate):
""" UID To Open Object Id
@param cr: the current row, from the database cursor,
@param uidval: Get USerId vale
@oomodel: Open Object ModelName
@param rdate: Get Recurrent Date
"""
__rege = re.compile(r'OpenObject-([\w|\.]+)_([0-9]+)@(\w+)$')
wematch = __rege.match(uidval.encode('utf8'))
if not wematch:
@ -60,17 +67,28 @@ def uid2openobjectid(cr, uidval, oomodel, rdate):
r_id = cr.fetchone()
if r_id:
return (id, r_id[0])
cr.execute(qry)
cr.execute(qry)
ids = map(lambda x: str(x[0]), cr.fetchall())
if id in ids:
return (id, None)
return (False, None)
def openobjectid2uid(cr, uidval, oomodel):
""" Open Object Id To UId
@param cr: the current row, from the database cursor,
@param uidval: Get USerId vale
@oomodel: Open Object ModelName """
value = 'OpenObject-%s_%s@%s' % (oomodel, uidval, cr.dbname)
return value
def get_attribute_mapping(cr, uid, calname, context={}):
""" Attribute Mapping with Basic calendar fields and lines
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param calname: Get Calendar name
@param context: A standard dictionary for contextual values """
if not context:
context = {}
pool = pooler.get_pool(cr.dbname)
@ -100,6 +118,10 @@ def get_attribute_mapping(cr, uid, calname, context={}):
return res
def map_data(cr, uid, obj):
""" Map Data
@param self: The object pointer
@param cr: the current row, from the database cursor,"""
vals = {}
for map_dict in obj.__attribute__:
map_val = obj.ical_get(map_dict, 'value')
@ -146,14 +168,26 @@ def map_data(cr, uid, obj):
class CalDAV(object):
__attribute__ = {}
def ical_set(self, name, value, type):
""" set calendar Attribute
@param self: The object pointer,
@param name: Get Attribute Name
@param value: Get Attribute Value
@param type: Get Attribute Type
"""
if name in self.__attribute__ and self.__attribute__[name]:
self.__attribute__[name][type] = value
return True
def ical_get(self, name, type):
""" Get calendar Attribute
@param self: The object pointer,
@param name: Get Attribute Name
@param type: Get Attribute Type
"""
if self.__attribute__.get(name):
val = self.__attribute__.get(name).get(type, None)
valtype = self.__attribute__.get(name).get('type', None)
@ -168,12 +202,23 @@ class CalDAV(object):
return self.__attribute__.get(name, None)
def ical_reset(self, type):
""" Reset Calendar Attribute
@param self: The object pointer,
@param type: Get Attribute Type
"""
for name in self.__attribute__:
if self.__attribute__[name]:
self.__attribute__[name][type] = None
return True
def parse_ics(self, cr, uid, child, cal_children=None, context=None):
""" parse calendaring and scheduling information
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param context: A standard dictionary for contextual values """
att_data = []
for cal_data in child.getChildren():
if cal_data.name.lower() == 'attendee':
@ -197,7 +242,7 @@ class CalDAV(object):
self.ical_set(cal_data.name.lower(), ','.join(exval), 'value')
continue
if cal_data.name.lower() in self.__attribute__:
if cal_data.params.get('X-VOBJ-ORIGINAL-TZID'):
self.ical_set('vtimezone', cal_data.params.get('X-VOBJ-ORIGINAL-TZID'), 'value')
self.ical_set(cal_data.name.lower(), cal_data.value, 'value')
@ -205,8 +250,15 @@ class CalDAV(object):
return vals
def create_ics(self, cr, uid, datas, name, ical, context=None):
""" create calendaring and scheduling information
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param context: A standard dictionary for contextual values """
if not datas:
return
timezones = []
for data in datas:
tzval = None
vevent = ical.add(name)
@ -222,10 +274,10 @@ class CalDAV(object):
model_obj = self.pool.get(model)
r_ids = []
if model_obj._columns.get('recurrent_uid', None):
cr.execute('select id from %s where recurrent_uid=%s'
cr.execute('select id from %s where recurrent_uid=%s'
% (model_obj._table, data[map_field]))
r_ids = map(lambda x: x[0], cr.fetchall())
if r_ids:
if r_ids:
rdata = self.pool.get(model).read(cr, uid, r_ids)
event_obj = self.pool.get('basic.calendar.event')
rcal = event_obj.export_cal(cr, uid, rdata, context=context)
@ -246,17 +298,18 @@ class CalDAV(object):
alarm_obj = self.pool.get('basic.calendar.alarm')
vevent = alarm_obj.export_cal(cr, uid, model, \
data[map_field][0], vevent, context=ctx)
elif field == 'vtimezone' and data[map_field]:
elif field == 'vtimezone' and data[map_field] and data[map_field] not in timezones:
tzval = data[map_field]
tz_obj = self.pool.get('basic.calendar.timezone')
ical = tz_obj.export_cal(cr, uid, None, \
data[map_field], ical, context=context)
timezones.append(data[map_field])
elif data[map_field]:
if map_type in ("char", "text"):
if field in ('exdate'):
vevent.add(field).value = map(parser.parse, (data[map_field]).split(','))
else:
vevent.add(field).value = tools.ustr(data[map_field])
vevent.add(field).value = tools.ustr(data[map_field])
elif map_type in ('datetime', 'date') and data[map_field]:
dtfield = vevent.add(field)
dtfield.value = parser.parse(data[map_field])
@ -276,8 +329,16 @@ class CalDAV(object):
if val1 == data[map_field]:
vevent.add(field).value = key1
return vevent
def check_import(self, cr, uid, vals, context={}):
"""
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param vals: Get Values
@param context: A standard dictionary for contextual values
"""
ids = []
model_obj = self.pool.get(context.get('model'))
recur_pool = {}
@ -309,6 +370,14 @@ class CalDAV(object):
return ids
def export_cal(self, cr, uid, datas, vobj=None, context={}):
""" Export Calendar
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param datas: Get Data's for caldav
@param context: A standard dictionary for contextual values
"""
try:
self.__attribute__ = get_attribute_mapping(cr, uid, self._calname, context)
ical = vobject.iCalendar()
@ -318,6 +387,14 @@ class CalDAV(object):
raise osv.except_osv(('Error !'), (str(e)))
def import_cal(self, cr, uid, content, data_id=None, context=None):
""" Import Calendar
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param data_id: Get Datas ID or False
@param context: A standard dictionary for contextual values
"""
ical_data = base64.decodestring(content)
self.__attribute__ = get_attribute_mapping(cr, uid, self._calname, context)
parsedCal = vobject.readOne(ical_data)
@ -333,6 +410,7 @@ class CalDAV(object):
self.ical_reset('value')
return res
class Calendar(CalDAV, osv.osv):
_name = 'basic.calendar'
_description = 'Calendar'
@ -352,27 +430,35 @@ class Calendar(CalDAV, osv.osv):
'vtimezone': None, # Use: O-n, Type: Collection of Timezone class
}
_columns = {
'name': fields.char("Name", size=64),
'line_ids': fields.one2many('basic.calendar.lines', 'calendar_id', 'Calendar Lines'),
'active': fields.boolean('Active'),
'create_date': fields.datetime('Created Date'),
'write_date': fields.datetime('Modifided Date'),
'name': fields.char("Name", size=64),
'line_ids': fields.one2many('basic.calendar.lines', 'calendar_id', 'Calendar Lines'),
'active': fields.boolean('Active'),
'create_date': fields.datetime('Created Date'),
'write_date': fields.datetime('Modifided Date'),
}
_defaults = {
'active': lambda *a: True,
'active': lambda *a: True,
}
def export_cal(self, cr, uid, ids, vobj='vevent', context={}):
""" Export Calendar
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param ids: List of calendars IDs
@param context: A standard dictionary for contextual values
"""
cal = self.browse(cr, uid, ids[0])
ical = vobject.iCalendar()
ical = vobject.iCalendar()
for line in cal.line_ids:
if line.name in ('valarm', 'attendee'):
continue
mod_obj = self.pool.get(line.object_id.model)
data_ids = mod_obj.search(cr, uid, eval(line.domain), context=context)
datas = mod_obj.read(cr, uid, data_ids, context=context)
context.update({'model': line.object_id.model,
context.update({'model': line.object_id.model,
'calendar_id': cal.id
})
self.__attribute__ = get_attribute_mapping(cr, uid, line.name, context)
@ -380,6 +466,14 @@ class Calendar(CalDAV, osv.osv):
return ical.serialize()
def import_cal(self, cr, uid, content, data_id=None, context=None):
""" Import Calendar
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param data_id: Get Datas ID or False
@param context: A standard dictionary for contextual values
"""
if not context:
context = {}
vals = []
@ -394,7 +488,7 @@ class Calendar(CalDAV, osv.osv):
cal_children[line.name] = line.object_id.model
for child in parsedCal.getChildren():
if child.name.lower() in cal_children:
context.update({'model': cal_children[child.name.lower()],
context.update({'model': cal_children[child.name.lower()],
'calendar_id': cal.id
})
self.__attribute__ = get_attribute_mapping(cr, uid, child.name.lower(), context=context)
@ -407,27 +501,39 @@ class Calendar(CalDAV, osv.osv):
self.check_import(cr, uid, vals, context=context)
return {}
Calendar()
class basic_calendar_line(osv.osv):
""" Calendar Lines """
_name = 'basic.calendar.lines'
_description = 'Calendar Lines'
_columns = {
'name': fields.selection([('vevent', 'Event'), ('vtodo', 'TODO'), \
('valarm', 'Alarm'), \
('attendee', 'Attendee')], \
string="Type", size=64),
'object_id': fields.many2one('ir.model', 'Object'),
string="Type", size=64),
'object_id': fields.many2one('ir.model', 'Object'),
'calendar_id': fields.many2one('basic.calendar', 'Calendar', \
required=True, ondelete='cascade'),
'domain': fields.char('Domain', size=124),
required=True, ondelete='cascade'),
'domain': fields.char('Domain', size=124),
'mapping_ids': fields.one2many('basic.calendar.fields', 'type_id', 'Fields Mapping')
}
}
_defaults = {
'domain': lambda *a: '[]',
'domain': lambda *a: '[]',
}
def create(self, cr, uid, vals, context={}):
""" create calendar's line
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param vals: Get the Values
@param context: A standard dictionary for contextual values
"""
cr.execute("Select count(id) from basic_calendar_lines \
where name='%s' and calendar_id=%s" % (vals.get('name'), vals.get('calendar_id')))
res = cr.fetchone()
@ -439,41 +545,52 @@ line "%s" more than once' % (vals.get('name'))))
basic_calendar_line()
class basic_calendar_attribute(osv.osv):
_name = 'basic.calendar.attributes'
_description = 'Calendar attributes'
_columns = {
'name': fields.char("Name", size=64, required=True),
_columns = {
'name': fields.char("Name", size=64, required=True),
'type': fields.selection([('vevent', 'Event'), ('vtodo', 'TODO'), \
('alarm', 'Alarm'), \
('attendee', 'Attendee')], \
string="Type", size=64, required=True),
string="Type", size=64, required=True),
}
basic_calendar_attribute()
class basic_calendar_fields(osv.osv):
""" Calendar fields """
_name = 'basic.calendar.fields'
_description = 'Calendar fields'
_columns = {
'field_id': fields.many2one('ir.model.fields', 'OpenObject Field'),
'name': fields.many2one('basic.calendar.attributes', 'Name', required=True),
'field_id': fields.many2one('ir.model.fields', 'OpenObject Field'),
'name': fields.many2one('basic.calendar.attributes', 'Name', required=True),
'type_id': fields.many2one('basic.calendar.lines', 'Type', \
required=True, ondelete='cascade'),
'expr': fields.char("Expression", size=64),
'fn': fields.selection([('field', 'Use the field'),
('const', 'Expression as constant'),
('hours', 'Interval in hours'),
], 'Function'),
'mapping': fields.text('Mapping'),
required=True, ondelete='cascade'),
'expr': fields.char("Expression", size=64),
'fn': fields.selection([('field', 'Use the field'),
('const', 'Expression as constant'),
('hours', 'Interval in hours'),
], 'Function'),
'mapping': fields.text('Mapping'),
}
_defaults = {
'fn': lambda *a: 'field',
'fn': lambda *a: 'field',
}
def check_line(self, cr, uid, vals, name, context=None):
""" check calendar's line
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param vals: Get Values
@param context: A standard dictionary for contextual values
"""
f_obj = self.pool.get('ir.model.fields')
field = f_obj.browse(cr, uid, vals['field_id'], context=context)
relation = field.relation
@ -485,8 +602,16 @@ class basic_calendar_fields(osv.osv):
if (relation != 'NULL') and (not relation == line_rel):
raise osv.except_osv(_('Warning !'), _('Please provide proper configuration of "%s" in Calendar Lines' % (name)))
return True
def create(self, cr, uid, vals, context={}):
""" Create Calendar's fields
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param vals: Get Values
@param context: A standard dictionary for contextual values
"""
cr.execute('select name from basic_calendar_attributes \
where id=%s' % (vals.get('name')))
name = cr.fetchone()
@ -500,8 +625,16 @@ class basic_calendar_fields(osv.osv):
if res[0] > 0:
raise osv.except_osv(_('Warning !'), _('Can not map the field more than once'))
return super(basic_calendar_fields, self).create(cr, uid, vals, context=context)
def write(self, cr, uid, ids, vals, context=None):
""" write Calendar's fields
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param vals: Get Values
@param context: A standard dictionary for contextual values
"""
if not vals:
return
for id in ids:
@ -520,6 +653,7 @@ class basic_calendar_fields(osv.osv):
basic_calendar_fields()
class Event(CalDAV, osv.osv_memory):
_name = 'basic.calendar.event'
_calname = 'vevent'
@ -540,7 +674,7 @@ class Event(CalDAV, osv.osv_memory):
'transp': None, # Use: O-1, Type: TEXT, Defines whether an event is transparent or not to busy time searches.
'uid': None, # Use: O-1, Type: TEXT, Defines the persistent, globally unique identifier for the calendar component.
'url': None, # Use: O-1, Type: URL, Defines a Uniform Resource Locator (URL) associated with the iCalendar object.
'recurid': None,
'recurid': None,
'attach': None, # Use: O-n, Type: BINARY, Provides the capability to associate a document object with a calendar component.
'attendee': None, # Use: O-n, Type: CAL-ADDRESS, Defines an "Attendee" within a calendar component.
'categories': None, # Use: O-n, Type: TEXT, Defines the categories for a calendar component.
@ -548,69 +682,89 @@ class Event(CalDAV, osv.osv_memory):
'contact': None, # Use: O-n, Type: TEXT, Used to represent contact information or alternately a reference to contact information associated with the calendar component.
'exdate': None, # Use: O-n, Type: DATE-TIME, Defines the list of date/time exceptions for a recurring calendar component.
'exrule': None, # Use: O-n, Type: RECUR, Defines a rule or repeating pattern for an exception to a recurrence set.
'rstatus': None,
'rstatus': None,
'related': None, # Use: O-n, Specify the relationship of the alarm trigger with respect to the start or end of the calendar component.
# like A trigger set 5 minutes after the end of the event or to-do.---> TRIGGER;related=END:PT5M
'resources': None, # Use: O-n, Type: TEXT, Defines the equipment or resources anticipated for an activity specified by a calendar entity like RESOURCES:EASEL,PROJECTOR,VCR, LANGUAGE=fr:1 raton-laveur
'rdate': None, # Use: O-n, Type: DATE-TIME, Defines the list of date/times for a recurrence set.
'rrule': None, # Use: O-n, Type: RECUR, Defines a rule or repeating pattern for recurring events, to-dos, or time zone definitions.
'x-prop': None,
'x-prop': None,
'duration': None, # Use: O-1, Type: DURATION, Specifies a positive duration of time.
'dtend': None, # Use: O-1, Type: DATE-TIME, Specifies the date and time that a calendar component ends.
}
def export_cal(self, cr, uid, datas, vobj='vevent', context={}):
""" Export calendar
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param datas: Get datas
@param context: A standard dictionary for contextual values
"""
return super(Event, self).export_cal(cr, uid, datas, 'vevent', context=context)
Event()
class ToDo(CalDAV, osv.osv_memory):
_name = 'basic.calendar.todo'
_calname = 'vtodo'
__attribute__ = {
'class': None,
'completed': None,
'created': None,
'description': None,
'dtstamp': None,
'dtstart': None,
'duration': None,
'due': None,
'geo': None,
'last-mod ': None,
'location': None,
'organizer': None,
'percent': None,
'priority': None,
'recurid': None,
'seq': None,
'status': None,
'summary': None,
'uid': None,
'url': None,
'attach': None,
'attendee': None,
'categories': None,
'comment': None,
'contact': None,
'exdate': None,
'exrule': None,
'rstatus': None,
'related': None,
'resources': None,
'rdate': None,
'rrule': None,
'class': None,
'completed': None,
'created': None,
'description': None,
'dtstamp': None,
'dtstart': None,
'duration': None,
'due': None,
'geo': None,
'last-mod ': None,
'location': None,
'organizer': None,
'percent': None,
'priority': None,
'recurid': None,
'seq': None,
'status': None,
'summary': None,
'uid': None,
'url': None,
'attach': None,
'attendee': None,
'categories': None,
'comment': None,
'contact': None,
'exdate': None,
'exrule': None,
'rstatus': None,
'related': None,
'resources': None,
'rdate': None,
'rrule': None,
}
def export_cal(self, cr, uid, datas, vobj='vevent', context={}):
""" Export Calendar
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param datas: Get datas
@param context: A standard dictionary for contextual values
"""
return super(ToDo, self).export_cal(cr, uid, datas, 'vtodo', context=context)
ToDo()
class Journal(CalDAV):
__attribute__ = {
}
class FreeBusy(CalDAV):
__attribute__ = {
'contact': None, # Use: O-1, Type: Text, Represent contact information or alternately a reference to contact information associated with the calendar component.
@ -624,15 +778,15 @@ class FreeBusy(CalDAV):
'attendee': None, # Use: O-n, Type: CAL-ADDRESS, Defines an "Attendee" within a calendar component.
'comment': None, # Use: O-n, Type: TEXT, Specifies non-processing information intended to provide a comment to the calendar user.
'freebusy': None, # Use: O-n, Type: PERIOD, Defines one or more free or busy time intervals.
'rstatus': None,
'X-prop': None,
'rstatus': None,
'X-prop': None,
}
class Timezone(CalDAV, osv.osv_memory):
_name = 'basic.calendar.timezone'
_calname = 'vtimezone'
__attribute__ = {
'tzid': {'field': 'tzid'}, # Use: R-1, Type: Text, Specifies the text value that uniquely identifies the "VTIMEZONE" calendar component.
'last-mod': None, # Use: O-1, Type: DATE-TIME, Specifies the date and time that the information associated with the calendar component was last revised in the calendar store.
@ -641,8 +795,15 @@ class Timezone(CalDAV, osv.osv_memory):
'daylightc': {'tzprop': None}, # Use: R-1,
'x-prop': None, # Use: O-n, Type: Text,
}
def get_name_offset(self, cr, uid, tzid, context={}):
""" Get Name Offset value
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param context: A standard dictionary for contextual values
"""
mytz = pytz.timezone(tzid.title())
mydt = datetime.now(tz=mytz)
offset = mydt.utcoffset()
@ -653,6 +814,14 @@ class Timezone(CalDAV, osv.osv_memory):
return (mydt.tzname(), realoffset)
def export_cal(self, cr, uid, model, tzid, ical, context={}):
""" Export Calendar
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param model: Get Model's name
@param context: A standard dictionary for contextual values
"""
ctx = context.copy()
ctx.update({'model': model})
cal_tz = ical.add('vtimezone')
@ -664,8 +833,16 @@ class Timezone(CalDAV, osv.osv_memory):
tz_std.add("DTSTART").value = datetime.now() # TODO
tz_std.add("TZNAME").value = tzname
return ical
def import_cal(self, cr, uid, ical_data, context=None):
""" Import Calendar
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param ical_data: Get calendar's data
@param context: A standard dictionary for contextual values
"""
for child in ical_data.getChildren():
if child.name.lower() == 'tzid':
tzname = child.value
@ -673,7 +850,7 @@ class Timezone(CalDAV, osv.osv_memory):
vals = map_data(cr, uid, self)
return vals
Timezone()
Timezone()
class Alarm(CalDAV, osv.osv_memory):
@ -689,10 +866,19 @@ class Alarm(CalDAV, osv.osv_memory):
'duration': None, # Type: DURATION, Duration' and 'repeat' are both optional, and MUST NOT occur more than once each, but if one occurs, so MUST the other. Use:- 0-1 for AUDIO, EMAIL and PROCEDURE, Use:- 0-n for DISPLAY
'repeat': None, # Type: INTEGER, Duration' and 'repeat' are both optional, and MUST NOT occur more than once each, but if one occurs, so MUST the other. Use:- 0-1 for AUDIO, EMAIL and PROCEDURE, Use:- 0-n for DISPLAY
'attach': None, # Use:- O-n: which MUST point to a sound resource, which is rendered when the alarm is triggered for AUDIO, Use:- O-n: which are intended to be sent as message attachments for EMAIL, Use:- R-1:which MUST point to a procedure resource, which is invoked when the alarm is triggered for PROCEDURE.
'x-prop': None,
'x-prop': None,
}
def export_cal(self, cr, uid, model, alarm_id, vevent, context={}):
""" Export Calendar
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param model: Get Model's name
@param alarm_id: Get Alarm's Id
@param context: A standard dictionary for contextual values
"""
valarm = vevent.add('valarm')
alarm_object = self.pool.get(model)
alarm_data = alarm_object.read(cr, uid, alarm_id, [])
@ -719,6 +905,14 @@ class Alarm(CalDAV, osv.osv_memory):
return vevent
def import_cal(self, cr, uid, ical_data, context=None):
""" Import Calendar
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param ical_data: Get calendar's Data
@param context: A standard dictionary for contextual values
"""
ctx = context.copy()
ctx.update({'model': context.get('model', None)})
self.__attribute__ = get_attribute_mapping(cr, uid, self._calname, ctx)
@ -753,6 +947,7 @@ class Alarm(CalDAV, osv.osv_memory):
Alarm()
class Attendee(CalDAV, osv.osv_memory):
_name = 'basic.calendar.attendee'
_calname = 'attendee'
@ -772,6 +967,14 @@ class Attendee(CalDAV, osv.osv_memory):
}
def import_cal(self, cr, uid, ical_data, context=None):
""" Import Calendar
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param ical_data: Get calendar's Data
@param context: A standard dictionary for contextual values
"""
ctx = context.copy()
ctx.update({'model': context.get('model', None)})
self.__attribute__ = get_attribute_mapping(cr, uid, self._calname, ctx)
@ -787,6 +990,15 @@ class Attendee(CalDAV, osv.osv_memory):
return vals
def export_cal(self, cr, uid, model, attendee_ids, vevent, context={}):
""" Export Calendar
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param model: Get model's name
@param attendee_ids: Get Attendee's Id
@param context: A standard dictionary for contextual values
"""
attendee_object = self.pool.get(model)
ctx = context.copy()
ctx.update({'model': model})
@ -812,5 +1024,4 @@ class Attendee(CalDAV, osv.osv_memory):
Attendee()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -1,3 +1,24 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
import time
import heapq
@ -10,8 +31,8 @@ def memoize(maxsize):
def wrapper(*args):
key = repr(args)
# performance crap
_cache=cache
_heap=heap
_cache = cache
_heap = heap
_heappop = heapq.heappop
_heappush = heapq.heappush
_time = time.time
@ -20,13 +41,13 @@ def memoize(maxsize):
if not _cache.has_key(key):
if _cursize == _maxsize:
# pop oldest element
(_,oldkey) = _heappop(_heap)
(_, oldkey) = _heappop(_heap)
_cache.pop(oldkey)
else:
_cursize += 1
# insert this element
_cache[key] = f(*args)
_heappush(_heap,(_time(),key))
_heappush(_heap, (_time(), key))
wrapper.misses += 1
else:
wrapper.hits += 1
@ -34,6 +55,8 @@ def memoize(maxsize):
wrapper.__doc__ = f.__doc__
wrapper.__name__ = f.__name__
wrapper.hits = wrapper.misses = 0
return wrapper
return wrapper
return decorating_function
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
##############################################################################
#
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
#
@ -15,7 +15,7 @@
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
@ -49,10 +49,10 @@ class tinydav_handler(dav_interface):
"""
PROPS={'DAV:': dav_interface.PROPS['DAV:'], }
M_NS={ "DAV:" : dav_interface.M_NS['DAV:'], }
M_NS={ "DAV:": dav_interface.M_NS['DAV:'], }
def __init__(self, parent, verbose=False):
self.db_name = False
self.db_name = False
self.parent = parent
self.baseuri = parent.baseuri
@ -64,7 +64,7 @@ class tinydav_handler(dav_interface):
if not dbname:
cr.close()
return props
node = self.uri2object(cr,uid,pool, uri2)
node = self.uri2object(cr, uid, pool, uri2)
if node:
props.update(node.get_dav_props(cr))
cr.close()
@ -78,18 +78,18 @@ class tinydav_handler(dav_interface):
pname -- name of the property
"""
if self.M_NS.has_key(ns):
return dav_interface.get_prop(self,uri,ns,propname)
return dav_interface.get_prop(self, uri, ns, propname)
if uri[-1]=='/':uri=uri[:-1]
if uri[-1]=='/':uri = uri[:-1]
cr, uid, pool, dbname, uri2 = self.get_cr(uri)
if not dbname:
cr.close()
raise DAV_NotFound
node = self.uri2object(cr,uid,pool, uri2)
node = self.uri2object(cr, uid, pool, uri2)
if not node:
cr.close()
raise DAV_NotFound
res = node.get_dav_eprop(cr,ns,propname)
res = node.get_dav_eprop(cr, ns, propname)
cr.close()
return res
@ -97,21 +97,21 @@ class tinydav_handler(dav_interface):
""" Return the base URI of this request, or even join it with the
ajoin path elements
"""
return self.baseuri+ '/'.join(ajoin)
return self.baseuri+ '/'.join(ajoin)
def uri2local(self, uri):
uparts=urlparse.urlparse(uri)
reluri=uparts[2]
uparts = urlparse.urlparse(uri)
reluri = uparts[2]
if reluri and reluri[-1]=="/":
reluri=reluri[:-1]
reluri = reluri[:-1]
return reluri
#
# pos: -1 to get the parent of the uri
#
def get_cr(self, uri):
def get_cr(self, uri):
pdb = self.parent.auth_proxy.last_auth
reluri = self.uri2local(uri)
reluri = self.uri2local(uri)
try:
dbname = reluri.split('/')[2]
except:
@ -121,7 +121,7 @@ class tinydav_handler(dav_interface):
if not pdb and dbname:
# if dbname was in our uri, we should have authenticated
# against that.
raise Exception("Programming error")
raise Exception("Programming error")
assert pdb == dbname, " %s != %s" %(pdb, dbname)
user, passwd, dbn2, uid = self.parent.auth_proxy.auth_creds[pdb]
db,pool = pooler.get_db_and_pool(dbname)
@ -134,29 +134,29 @@ class tinydav_handler(dav_interface):
return None
return pool.get('basic.calendar').get_calendar_object(cr, uid, uri)
def get_data(self,uri):
def get_data(self, uri):
self.parent.log_message('GET: %s' % uri)
if uri[-1]=='/':uri=uri[:-1]
cr, uid, pool, dbname, uri2 = self.get_cr(uri)
try:
if not dbname:
raise DAV_Error, 409
node = self.uri2object(cr,uid,pool, uri2)
node = self.uri2object(cr, uid, pool, uri2)
if not node:
raise DAV_NotFound(uri2)
try:
try:
datas = node.get_data(cr, uid)
except TypeError,e:
except TypeError, e:
import traceback
self.parent.log_error("GET typeError: %s", str(e))
self.parent.log_message("Exc: %s",traceback.format_exc())
self.parent.log_message("Exc: %s", traceback.format_exc())
raise DAV_Forbidden
except IndexError,e :
except IndexError, e :
self.parent.log_error("GET IndexError: %s", str(e))
raise DAV_NotFound(uri2)
except Exception,e:
except Exception, e:
import traceback
self.parent.log_error("GET exception: %s",str(e))
self.parent.log_error("GET exception: %s", str(e))
self.parent.log_message("Exc: %s", traceback.format_exc())
raise DAV_Error, 409
return datas
@ -164,7 +164,7 @@ class tinydav_handler(dav_interface):
cr.close()
@memoize(CACHE_SIZE)
def _get_dav_resourcetype(self,uri):
def _get_dav_resourcetype(self, uri):
""" return type of object """
self.parent.log_message('get RT: %s' % uri)
if uri[-1]=='/':uri=uri[:-1]
@ -172,21 +172,21 @@ class tinydav_handler(dav_interface):
try:
if not dbname:
return COLLECTION
node = self.uri2object(cr,uid,pool, uri2)
node = self.uri2object(cr, uid, pool, uri2)
if not node:
raise DAV_NotFound(uri2)
raise DAV_NotFound(uri2)
return OBJECT
finally:
cr.close()
def _get_dav_displayname(self,uri):
def _get_dav_displayname(self, uri):
self.parent.log_message('get DN: %s' % uri)
if uri[-1]=='/':uri=uri[:-1]
cr, uid, pool, dbname, uri2 = self.get_cr(uri)
if not dbname:
cr.close()
return COLLECTION
node = self.uri2object(cr,uid,pool, uri2)
node = self.uri2object(cr, uid, pool, uri2)
if not node:
cr.close()
raise DAV_NotFound(uri2)
@ -194,7 +194,7 @@ class tinydav_handler(dav_interface):
return node.displayname
@memoize(CACHE_SIZE)
def _get_dav_getcontentlength(self,uri):
def _get_dav_getcontentlength(self, uri):
""" return the content length of an object """
self.parent.log_message('get length: %s' % uri)
if uri[-1]=='/':uri=uri[:-1]
@ -212,7 +212,7 @@ class tinydav_handler(dav_interface):
return str(result)
@memoize(CACHE_SIZE)
def _get_dav_getetag(self,uri):
def _get_dav_getetag(self, uri):
""" return the ETag of an object """
self.parent.log_message('get etag: %s' % uri)
if uri[-1]=='/':uri=uri[:-1]
@ -230,7 +230,7 @@ class tinydav_handler(dav_interface):
return str(result)
@memoize(CACHE_SIZE)
def get_lastmodified(self,uri):
def get_lastmodified(self, uri):
""" return the last modified date of the object """
if uri[-1]=='/':uri=uri[:-1]
today = time.time()
@ -238,18 +238,18 @@ class tinydav_handler(dav_interface):
try:
if not dbname:
return today
node = self.uri2object(cr,uid,pool, uri2)
node = self.uri2object(cr, uid, pool, uri2)
if not node:
raise DAV_NotFound(uri2)
if node.write_date:
return time.mktime(time.strptime(node.write_date,'%Y-%m-%d %H:%M:%S'))
return time.mktime(time.strptime(node.write_date, '%Y-%m-%d %H:%M:%S'))
else:
return today
finally:
cr.close()
@memoize(CACHE_SIZE)
def get_creationdate(self,uri):
def get_creationdate(self, uri):
""" return the last modified date of the object """
if uri[-1]=='/':uri=uri[:-1]
@ -257,11 +257,11 @@ class tinydav_handler(dav_interface):
try:
if not dbname:
raise DAV_Error, 409
node = self.uri2object(cr,uid,pool, uri2)
node = self.uri2object(cr, uid, pool, uri2)
if not node:
raise DAV_NotFound(uri2)
if node.create_date:
result = time.strptime(node.create_date,'%Y-%m-%d %H:%M:%S')
result = time.strptime(node.create_date, '%Y-%m-%d %H:%M:%S')
else:
result = time.gmtime()
return result
@ -269,14 +269,14 @@ class tinydav_handler(dav_interface):
cr.close()
@memoize(CACHE_SIZE)
def _get_dav_getcontenttype(self,uri):
def _get_dav_getcontenttype(self, uri):
self.parent.log_message('get contenttype: %s' % uri)
if uri[-1]=='/':uri=uri[:-1]
cr, uid, pool, dbname, uri2 = self.get_cr(uri)
try:
if not dbname:
return 'httpd/unix-directory'
node = self.uri2object(cr,uid,pool, uri2)
node = self.uri2object(cr, uid, pool, uri2)
if not node:
raise DAV_NotFound(uri2)
result = node.mimetype
@ -285,50 +285,50 @@ class tinydav_handler(dav_interface):
finally:
cr.close()
def put(self, uri, data, content_type=None):
""" put the object into the filesystem """
self.parent.log_message('Putting %s (%d), %s'%( misc.ustr(uri), len(data), content_type))
self.parent.log_message('Putting %s (%d), %s'%(misc.ustr(uri), len(data), content_type))
parent='/'.join(uri.split('/')[:-1])
cr, uid, pool,dbname, uri2 = self.get_cr(uri)
cr, uid, pool, dbname, uri2 = self.get_cr(uri)
if not dbname:
raise DAV_Forbidden
try:
node = self.uri2object(cr,uid,pool, uri2[:])
node = self.uri2object(cr, uid, pool, uri2[:])
except:
node = False
node = False
if not node:
raise DAV_Forbidden
else:
try:
node.set_data(cr, uid, data)
except Exception,e:
import traceback
node.set_data(cr, uid, data)
except Exception, e:
import traceback
self.parent.log_error("Cannot save :%s", str(e))
self.parent.log_message("Exc: %s",traceback.format_exc())
self.parent.log_message("Exc: %s", traceback.format_exc())
raise DAV_Forbidden
cr.commit()
cr.close()
return 201
def exists(self,uri):
def exists(self, uri):
""" test if a resource exists """
result = False
cr, uid, pool,dbname, uri2 = self.get_cr(uri)
cr, uid, pool, dbname, uri2 = self.get_cr(uri)
if not dbname:
cr.close()
return True
try:
node = self.uri2object(cr,uid,pool, uri2)
node = self.uri2object(cr, uid, pool, uri2)
if node:
result = True
except:
pass
cr.close()
return result
return result
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
##############################################################################
#
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
#
@ -15,7 +15,7 @@
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
@ -26,47 +26,46 @@ import tools
import time
import base64
class node_calendar(object):
class node_calendar(object):
def __init__(self, path, context, calendar):
self.path = path
self.context = context
self.calendar_id = calendar.id
self.mimetype = 'ics'
self.calendar_id = calendar.id
self.mimetype = 'ics'
self.create_date = calendar.create_date
self.write_date = calendar.write_date or calendar.create_date
self.content_length = 0
self.displayname = calendar.name
def get_data(self, cr, uid):
def get_data(self, cr, uid):
calendar_obj = pooler.get_pool(cr.dbname).get('basic.calendar')
return calendar_obj.export_cal(cr, uid, [self.calendar_id])
def get_data_len(self, cr):
return calendar_obj.export_cal(cr, uid, [self.calendar_id])
def get_data_len(self, cr):
return self.content_length
def set_data(self, cr, uid, data):
calendar_obj = pooler.get_pool(cr.dbname).get('basic.calendar')
def set_data(self, cr, uid, data):
calendar_obj = pooler.get_pool(cr.dbname).get('basic.calendar')
return calendar_obj.import_cal(cr, uid, base64.encodestring(data), self.calendar_id)
def get_etag(self,cr):
def get_etag(self, cr):
""" Get a tag, unique per object + modification.
see. http://tools.ietf.org/html/rfc2616#section-13.3.3 """
return self._get_ttag(cr) + ':' + self._get_wtag(cr)
def _get_wtag(self,cr):
def _get_wtag(self, cr):
""" Return the modification time as a unique, compact string """
if self.write_date:
wtime = time.mktime(time.strptime(self.write_date,'%Y-%m-%d %H:%M:%S'))
wtime = time.mktime(time.strptime(self.write_date, '%Y-%m-%d %H:%M:%S'))
else: wtime = time.time()
return str(wtime)
def _get_ttag(self,cr):
def _get_ttag(self, cr):
return 'calendar-%d' % self.calendar_id
def get_dav_eprop(self,cr,ns,prop):
return None
class Calendar(osv.osv):
_inherit = 'basic.calendar'
@ -82,5 +81,7 @@ class Calendar(osv.osv):
return None
calendar_id, calendar_name = res[0]
calendar = self.browse(cr, uid, calendar_id)
return node_calendar(uri, context, calendar)
return node_calendar(uri, context, calendar)
Calendar()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4

View File

@ -31,7 +31,7 @@
<separator string="Value Mapping" colspan="4" />
<field name="mapping" select="1" colspan="4" nolabel="1" />
</form>
</field>
</field>
</form>
<tree string="Attributes Mapping" editable="bottom">
<field name="name" select="1" />

View File

@ -1,19 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<wizard string="Import .ics File" model="basic.calendar"
name="calendar.event.import" id="wizard_cal_event_import" />
<wizard string="Subscribe"
model="basic.calendar" name="calendar.event.subscribe"
id="wizard_cal_event_subscribe" multi="True" />
<wizard string="Export .ics File" model="basic.calendar"
name="calendar.event.export" id="wizard_cal_event_export" />
<wizard string="Edit this event" model="basic.calendar.event"
name="calendar.event.edit.this" id="wizard_edit_this_event"
menu="False" />
</data>
</openerp>

View File

@ -1,6 +1,13 @@
"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink"
"access_basic_calendar_all","basic.calendar","model_basic_calendar",,1,1,1,1
"access_basic_calendar_event_all","basic.calendar.event","model_basic_calendar_event",,1,1,1,1
"access_basic_calendar_attendee_all","basic.calendar.attendee","model_basic_calendar_attendee",,1,1,1,1
"access_calendar_todo_all","basic.calendar.todo","model_basic_calendar_todo",,1,1,1,1
"access_basic_calendar_alarm_all","basic.calendar.alarm","model_basic_calendar_alarm",,1,1,1,1
"access_basic_calendar_all","basic.calendar","model_basic_calendar","base.group_user",1,1,1,1
"access_basic_calendar_event_all","basic.calendar.event","model_basic_calendar_event","base.group_user",1,1,1,0
"access_basic_calendar_attendee_all","basic.calendar.attendee","model_basic_calendar_attendee","base.group_user",1,1,1,0
"access_calendar_todo_all","basic.calendar.todo","model_basic_calendar_todo","base.group_user",1,1,1,0
"access_basic_calendar_alarm_all","basic.calendar.alarm","model_basic_calendar_alarm","base.group_user",1,1,1,1
"access_basic_calendar_lines","basic.calendar.lines","model_basic_calendar_lines","base.group_user",1,1,1,1
"access_basic_calendar_attributes","basic.calendar.attributes","model_basic_calendar_attributes","base.group_user",1,1,1,1
"access_basic_calendar_fields","basic.calendar.fields","model_basic_calendar_fields","base.group_user",1,1,1,1
"access_basic_calendar_timezone","basic.calendar.timezone","model_basic_calendar_timezone","base.group_user",1,1,1,1
"access_calendar_event_export","calendar.event.export","model_calendar_event_export","base.group_user",1,1,1,1
"access_calendar_event_import","calendar.event.import","model_calendar_event_import","base.group_user",1,1,1,1
"access_calendar_event_subscribe","calendar.event.subscribe","model_calendar_event_subscribe","base.group_user",1,1,1,1

1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_basic_calendar_all basic.calendar model_basic_calendar base.group_user 1 1 1 1
3 access_basic_calendar_event_all basic.calendar.event model_basic_calendar_event base.group_user 1 1 1 1 0
4 access_basic_calendar_attendee_all basic.calendar.attendee model_basic_calendar_attendee base.group_user 1 1 1 1 0
5 access_calendar_todo_all basic.calendar.todo model_basic_calendar_todo base.group_user 1 1 1 1 0
6 access_basic_calendar_alarm_all basic.calendar.alarm model_basic_calendar_alarm base.group_user 1 1 1 1
7 access_basic_calendar_lines basic.calendar.lines model_basic_calendar_lines base.group_user 1 1 1 1
8 access_basic_calendar_attributes basic.calendar.attributes model_basic_calendar_attributes base.group_user 1 1 1 1
9 access_basic_calendar_fields basic.calendar.fields model_basic_calendar_fields base.group_user 1 1 1 1
10 access_basic_calendar_timezone basic.calendar.timezone model_basic_calendar_timezone base.group_user 1 1 1 1
11 access_calendar_event_export calendar.event.export model_calendar_event_export base.group_user 1 1 1 1
12 access_calendar_event_import calendar.event.import model_calendar_event_import base.group_user 1 1 1 1
13 access_calendar_event_subscribe calendar.event.subscribe model_calendar_event_subscribe base.group_user 1 1 1 1

View File

@ -1,9 +1,7 @@
# -*- encoding: utf-8 -*-
#
# Copyright P. Christeas <p_christ@hol.gr> 2008,2009
#
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsability of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
@ -33,15 +31,16 @@ from tools.config import config
from DAV.WebDAVServer import DAVRequestHandler
from service.websrv_lib import HTTPDir,FixSendError
class DAVHandler(FixSendError,DAVRequestHandler):
class DAVHandler(FixSendError, DAVRequestHandler):
verbose = False
def get_userinfo(self,user,pw):
def get_userinfo(self, user, pw):
return False
def _log(self, message):
netsvc.Logger().notifyChannel("webdav",netsvc.LOG_DEBUG,message)
netsvc.Logger().notifyChannel("webdav", netsvc.LOG_DEBUG, message)
def handle(self):
pass
@ -50,30 +49,30 @@ class DAVHandler(FixSendError,DAVRequestHandler):
def setup(self):
davpath = '/calendar/'
self.baseuri = "http://%s:%d%s"% (self.server.server_name,self.server.server_port,davpath)
self.baseuri = "http://%s:%d%s"% (self.server.server_name, self.server.server_port, davpath)
self.IFACE_CLASS = tinydav_handler(self)
pass
def log_message(self, format, *args):
netsvc.Logger().notifyChannel('webdav',netsvc.LOG_DEBUG_RPC,format % args)
netsvc.Logger().notifyChannel('webdav', netsvc.LOG_DEBUG_RPC, format % args)
def log_error(self, format, *args):
netsvc.Logger().notifyChannel('webdav',netsvc.LOG_WARNING,format % args)
netsvc.Logger().notifyChannel('webdav', netsvc.LOG_WARNING, format % args)
try:
from service.http_server import reg_http_service,OpenERPAuthProvider
from service.http_server import reg_http_service, OpenERPAuthProvider
davpath = '/calendar/'
handler = DAVHandler
handler.verbose = config.get_misc('webdav','verbose',True)
handler.debug = config.get_misc('webdav','debug',True)
reg_http_service(HTTPDir(davpath,DAVHandler,OpenERPAuthProvider()))
netsvc.Logger().notifyChannel('webdav',netsvc.LOG_INFO,"WebDAV service registered at path: %s/ "% davpath)
handler.verbose = config.get_misc('webdav', 'verbose', True)
handler.debug = config.get_misc('webdav', 'debug', True)
reg_http_service(HTTPDir(davpath, DAVHandler, OpenERPAuthProvider()))
netsvc.Logger().notifyChannel('webdav', netsvc.LOG_INFO, "WebDAV service registered at path: %s/ "% davpath)
except Exception, e:
logger = netsvc.Logger()
logger.notifyChannel('webdav', netsvc.LOG_ERROR, 'Cannot launch webdav: %s' % e)
#eof
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4

Some files were not shown because too many files have changed in this diff Show More