sbh (Open ERP)
2010-02-01 10:38:27 +05:30
54 changed files with 7092 additions and 466 deletions
+37 -37
View File
@@ -7,13 +7,13 @@ msgstr ""
"Project-Id-Version: OpenERP Server 5.0.0\n"
"Report-Msgid-Bugs-To: support@openerp.com\n"
"POT-Creation-Date: 2009-08-28 16:01+0000\n"
"PO-Revision-Date: 2010-01-22 23:52+0000\n"
"Last-Translator: Balint (eSolve) <Unknown>\n"
"PO-Revision-Date: 2010-01-30 16:38+0000\n"
"Last-Translator: Dukai Gábor <Unknown>\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2010-01-24 04:50+0000\n"
"X-Launchpad-Export-Date: 2010-01-31 04:51+0000\n"
"X-Generator: Launchpad (build Unknown)\n"
#. module: account
@@ -910,7 +910,7 @@ msgstr ""
#. module: account
#: wizard_field:account.open_closed_fiscalyear,init,fyear_id:0
msgid "Fiscal Year to Open"
msgstr ""
msgstr "Visszanyitandó üzleti év"
#. module: account
#: view:account.config.wizard:0
@@ -981,7 +981,7 @@ msgstr ""
#: wizard_field:account.print.journal.report,init,period_id:0
#: field:account.subscription,period_nbr:0
msgid "Period"
msgstr "Időszak"
msgstr "Periódus"
#. module: account
#: rml:account.partner.balance:0
@@ -1172,7 +1172,7 @@ msgstr ""
#. module: account
#: wizard_view:account.fiscalyear.close.state,init:0
msgid "Are you sure you want to close the fiscal year ?"
msgstr ""
msgstr "Biztos benne, hogy le akarja zárni az üzleti évet?"
#. module: account
#: selection:account.move,type:0
@@ -1212,7 +1212,7 @@ msgstr ""
#. module: account
#: field:account.account,company_currency_id:0
msgid "Company Currency"
msgstr ""
msgstr "Cég pénzneme"
#. module: account
#: model:ir.model,name:account.model_account_fiscal_position_account_template
@@ -1332,7 +1332,7 @@ msgstr ""
#: selection:account.analytic.account,type:0
#: field:account.journal,view_id:0
msgid "View"
msgstr ""
msgstr "nézet"
#. module: account
#: selection:account.account.balance.report,checktype,display_account:0
@@ -1471,7 +1471,7 @@ msgstr "Kötelezettség összesen"
#. module: account
#: wizard_button:account.fiscalyear.close.state,init,close:0
msgid "Close states"
msgstr ""
msgstr "Állapotok zárása"
#. module: account
#: model:ir.model,name:account.model_wizard_company_setup
@@ -1707,7 +1707,7 @@ msgstr "Visszanyitás"
#. module: account
#: wizard_view:account.fiscalyear.close,init:0
msgid "Are you sure you want to create entries?"
msgstr ""
msgstr "Biztos hogy létre akarja hozni ezeket a könyvelési tételeket?"
#. module: account
#: field:account.tax,include_base_amount:0
@@ -2071,7 +2071,7 @@ msgstr ""
#. module: account
#: wizard_field:account.fiscalyear.close,init,period_id:0
msgid "Opening Entries Period"
msgstr ""
msgstr "Nyitóegyenleg periódusa"
#. module: account
#: model:ir.actions.wizard,name:account.wizard_validate_account_moves
@@ -2088,7 +2088,7 @@ msgstr ""
#. module: account
#: selection:account.aged.trial.balance,init,direction_selection:0
msgid "Past"
msgstr ""
msgstr "múlt"
#. module: account
#: field:account.analytic.account,company_currency_id:0
@@ -2193,7 +2193,7 @@ msgstr "Tételes"
#: selection:account.account,type:0
#: selection:account.account.template,type:0
msgid "Consolidation"
msgstr "Konszolidáció"
msgstr "konszolidáció"
#. module: account
#: field:account.chart.template,account_root_id:0
@@ -2236,7 +2236,7 @@ msgstr ""
#. module: account
#: wizard_field:account.fiscalyear.close,init,journal_id:0
msgid "Opening Entries Journal"
msgstr ""
msgstr "Nyitóegyenleg naplója"
#. module: account
#: view:account.config.wizard:0
@@ -2711,7 +2711,7 @@ msgstr "Számla azonosító"
#. module: account
#: field:account.period,date_stop:0
msgid "End of Period"
msgstr "Időszak vége"
msgstr "Periódus vége"
#. module: account
#: wizard_button:populate_statement_from_inv,go,finish:0
@@ -2765,7 +2765,7 @@ msgstr ""
#. module: account
#: wizard_field:account.aged.trial.balance,init,direction_selection:0
msgid "Analysis Direction"
msgstr ""
msgstr "Elemzés iránya"
#. module: account
#: wizard_button:populate_statement_from_inv,init,go:0
@@ -3093,7 +3093,7 @@ msgstr ""
#: selection:account.account.template,type:0
#: selection:account.aged.trial.balance,init,result_selection:0
msgid "Payable"
msgstr ""
msgstr "kötelezettségek"
#. module: account
#: rml:account.invoice:0
@@ -3110,7 +3110,7 @@ msgstr ""
#: selection:account.account,type:0
#: selection:account.account.template,type:0
msgid "Others"
msgstr ""
msgstr "egyéb"
#. module: account
#: selection:account.automatic.reconcile,init,power:0
@@ -3250,7 +3250,7 @@ msgstr ""
#: model:ir.actions.wizard,name:account.wizard_fiscalyear_close
#: model:ir.ui.menu,name:account.menu_wizard_fy_close
msgid "Generate Fiscal Year Opening Entries"
msgstr ""
msgstr "Nyitóegyenlegek készítése az üzleti évhez"
#. module: account
#: model:ir.actions.wizard,name:account.wizard_reconcile
@@ -3277,7 +3277,7 @@ msgstr "Adónév"
#. module: account
#: wizard_view:account.fiscalyear.close.state,init:0
msgid " Close states of Fiscal year and periods"
msgstr ""
msgstr " Az üzleti év és a periódusok állapotainak zárása"
#. module: account
#: model:account.payment.term,name:account.account_payment_term
@@ -3565,7 +3565,7 @@ msgstr "Naplókód"
#: wizard_button:account.fiscalyear.close,init,close:0
#: view:account.model:0
msgid "Create entries"
msgstr ""
msgstr "Tételek létrehozása"
#. module: account
#: view:account.analytic.line:0
@@ -3780,7 +3780,7 @@ msgstr ""
#: selection:account.account,type:0
#: selection:account.account.template,type:0
msgid "Closed"
msgstr ""
msgstr "lezárt"
#. module: account
#: model:process.node,name:account.process_node_paymententries0
@@ -3878,7 +3878,7 @@ msgstr "Maximális mennyiség"
#. module: account
#: field:account.period,name:0
msgid "Period Name"
msgstr "Időszak"
msgstr "Periódus"
#. module: account
#: help:account.analytic.journal,type:0
@@ -4294,7 +4294,7 @@ msgstr ""
#: field:account.account,type:0
#: field:account.account.template,type:0
msgid "Internal Type"
msgstr ""
msgstr "Belső típus"
#. module: account
#: selection:account.automatic.reconcile,init,power:0
@@ -4333,7 +4333,7 @@ msgstr "Záró dátum"
#: model:ir.actions.wizard,name:account.wizard_open_closed_fiscalyear
#: model:ir.ui.menu,name:account.menu_wizard_open_closed_fy
msgid "Cancel Opening Entries"
msgstr ""
msgstr "Nyitóegyenleg visszavonása"
#. module: account
#: model:process.transition,name:account.process_transition_invoicemanually0
@@ -4435,7 +4435,7 @@ msgstr ""
#: view:account.fiscalyear:0
#: view:account.period:0
msgid "States"
msgstr ""
msgstr "Állapotok"
#. module: account
#: view:account.move:0
@@ -4560,7 +4560,7 @@ msgstr ""
#. module: account
#: wizard_field:account.aged.trial.balance,init,result_selection:0
msgid "Filter on Partners"
msgstr ""
msgstr "Partnerek szűrése"
#. module: account
#: field:account.tax,price_include:0
@@ -4772,7 +4772,7 @@ msgstr ""
#: selection:account.account.template,type:0
#: selection:account.aged.trial.balance,init,result_selection:0
msgid "Receivable"
msgstr ""
msgstr "követelések"
#. module: account
#: model:ir.actions.report.xml,name:account.account_account_balance
@@ -4909,12 +4909,12 @@ msgstr "Tételek"
#. module: account
#: field:account.period,date_start:0
msgid "Start of Period"
msgstr ""
msgstr "Periódus kezdete"
#. module: account
#: wizard_field:account.fiscalyear.close,init,report_name:0
msgid "Name of new entries"
msgstr ""
msgstr "Új tételek neve"
#. module: account
#: wizard_button:account_use_models,init_form,create:0
@@ -5252,7 +5252,7 @@ msgstr ""
#. module: account
#: wizard_view:account.period.close,init:0
msgid "Are you sure ?"
msgstr ""
msgstr "Biztos benne?"
#. module: account
#: rml:account.invoice:0
@@ -5320,7 +5320,7 @@ msgstr ""
#. module: account
#: selection:account.aged.trial.balance,init,result_selection:0
msgid "Receivable and Payable"
msgstr ""
msgstr "követelések és kötelezettségek"
#. module: account
#: rml:account.account.balance:0
@@ -5468,7 +5468,7 @@ msgstr ""
#: view:account.move:0
#: view:account.move.line:0
msgid "General Information"
msgstr ""
msgstr "Általános adatok"
#. module: account
#: help:populate_statement_from_inv,init,journal_id:0
@@ -5566,7 +5566,7 @@ msgstr ""
#: wizard_field:account.fiscalyear.close,init,fy_id:0
#: wizard_field:account.fiscalyear.close.state,init,fy_id:0
msgid "Fiscal Year to close"
msgstr ""
msgstr "Lezárandó üzleti év"
#. module: account
#: wizard_field:account.aged.trial.balance,init,date1:0
@@ -5670,7 +5670,7 @@ msgstr "Árbevétel számla"
#. module: account
#: field:account.period,special:0
msgid "Opening/Closing Period"
msgstr ""
msgstr "Nyitó/záró periódus"
#. module: account
#: rml:account.analytic.account.balance:0
@@ -5763,7 +5763,7 @@ msgstr "Pénzügyi év"
#. module: account
#: selection:account.aged.trial.balance,init,direction_selection:0
msgid "Future"
msgstr ""
msgstr "jövő"
#. module: account
#: help:account.account.balance.report,checktype,fiscalyear:0
@@ -5893,7 +5893,7 @@ msgstr ""
#: wizard_field:account.fiscalyear.close.state,init,sure:0
#: wizard_field:account.period.close,init,sure:0
msgid "Check this box"
msgstr ""
msgstr "Jelölje meg ezt a négyzetet"
#. module: account
#: help:account.tax,price_include:0
+20 -15
View File
@@ -7,13 +7,13 @@ msgstr ""
"Project-Id-Version: OpenERP Server 5.0.0\n"
"Report-Msgid-Bugs-To: support@openerp.com\n"
"POT-Creation-Date: 2009-08-28 16:01+0000\n"
"PO-Revision-Date: 2010-01-17 16:07+0000\n"
"Last-Translator: Davide Corio <davide.corio@domsense.com>\n"
"PO-Revision-Date: 2010-01-29 14:02+0000\n"
"Last-Translator: Carlo <Unknown>\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2010-01-18 04:44+0000\n"
"X-Launchpad-Export-Date: 2010-01-30 04:39+0000\n"
"X-Generator: Launchpad (build Unknown)\n"
#. module: account
@@ -299,7 +299,7 @@ msgstr "Fisso"
#: model:ir.actions.report.xml,name:account.account_overdue
#: view:res.company:0
msgid "Overdue Payments"
msgstr ""
msgstr "Ritardo Pagamenti"
#. module: account
#: wizard_view:account.account.balance.report,checktype:0
@@ -336,7 +336,7 @@ msgstr "Computazione del canone"
#. module: account
#: rml:account.central.journal:0
msgid "Account Num."
msgstr ""
msgstr "Conto n."
#. module: account
#: rml:account.analytic.account.analytic.check:0
@@ -475,7 +475,7 @@ msgstr "Riconciliazione bancaria"
#. module: account
#: rml:account.invoice:0
msgid "Disc.(%)"
msgstr ""
msgstr "Sconto (%)"
#. module: account
#: rml:account.general.ledger:0
@@ -497,6 +497,8 @@ msgid ""
"Set if the amount of tax must be included in the base amount before "
"computing the next taxes."
msgstr ""
"Selezionare se l'importo della tassa debba essere incluso nell'importo base "
"prima di calcolare le prossime tasse"
#. module: account
#: model:ir.ui.menu,name:account.menu_finance_periodical_processing
@@ -506,7 +508,7 @@ msgstr "Elaborazioni periodiche"
#. module: account
#: view:report.hr.timesheet.invoice.journal:0
msgid "Analytic Entries Stats"
msgstr ""
msgstr "Statistiche delle registrazioni analitiche"
#. module: account
#: model:ir.actions.act_window,name:account.action_account_tax_code_template_form
@@ -528,7 +530,7 @@ msgstr "Pagamento Riconciliato"
#. module: account
#: wizard_field:account.chart,init,target_move:0
msgid "Target Moves"
msgstr ""
msgstr "Registrazioni:"
#. module: account
#: model:ir.actions.act_window,name:account.action_account_tax_template_form
@@ -549,7 +551,7 @@ msgstr "Metodo riapertura conti"
#. module: account
#: field:account.tax.template,include_base_amount:0
msgid "Include in Base Amount"
msgstr ""
msgstr "Incluso nell'imponibile"
#. module: account
#: field:account.tax,ref_base_code_id:0
@@ -579,6 +581,9 @@ msgid ""
"Number of days to add before computation of the day of month.If Date=15/01, "
"Number of Days=22, Day of Month=-1, then the due date is 28/02."
msgstr ""
"Numero di giorni da aggiungere prima della computazione del giorno del mese. "
"Se Data=15/01, Numero di giorni=22, Giorno del mese=-1, allora la scadenza "
"sarà 28/02."
#. module: account
#: model:ir.model,name:account.model_account_tax
@@ -588,12 +593,12 @@ msgstr "account.tax"
#. module: account
#: rml:account.central.journal:0
msgid "Printing Date"
msgstr ""
msgstr "Stampa Data"
#. module: account
#: rml:account.general.ledger:0
msgid "Mvt"
msgstr ""
msgstr "Reg. n."
#. module: account
#: model:ir.actions.wizard,name:account.wizard_aged_trial_balance
@@ -659,7 +664,7 @@ msgstr ""
#. module: account
#: help:account.fiscalyear,company_id:0
msgid "Keep empty if the fiscal year belongs to several companies."
msgstr ""
msgstr "Lasciare vuoto se l'anno fiscale appartiene a più aziende"
#. module: account
#: model:ir.ui.menu,name:account.menu_analytic_accounting
@@ -669,7 +674,7 @@ msgstr "Contabilità Analitica"
#. module: account
#: rml:account.overdue:0
msgid "Sub-Total :"
msgstr ""
msgstr "Sub-totale"
#. module: account
#: field:account.analytic.account,line_ids:0
@@ -687,12 +692,12 @@ msgstr "Mese"
#. module: account
#: field:account.analytic.account,partner_id:0
msgid "Associated Partner"
msgstr ""
msgstr "Partner associato"
#. module: account
#: field:account.invoice,comment:0
msgid "Additional Information"
msgstr ""
msgstr "Informazioni Aggiuntive"
#. module: account
#: selection:account.invoice,type:0
+3 -3
View File
@@ -37,13 +37,13 @@ class ir_sequence(osv.osv):
_columns = {
'fiscal_ids' : fields.one2many('account.sequence.fiscalyear', 'sequence_main_id', 'Sequences')
}
def get_id(self, cr, uid, sequence_id, test='id=%s', context={}):
cr.execute('select id from ir_sequence where '+test+' and active=%s', (sequence_id, True,))
def get_id(self, cr, uid, sequence_id, test='id', context={}):
cr.execute('select id from ir_sequence where '+test+'=%s and active=%s', (sequence_id, True,))
res = cr.dictfetchone()
if res:
for line in self.browse(cr, uid, res['id'], context=context).fiscal_ids:
if line.fiscalyear_id.id==context.get('fiscalyear_id', False):
return super(ir_sequence, self).get_id(cr, uid, line.sequence_id.id, test="id=%s", context=context)
return super(ir_sequence, self).get_id(cr, uid, line.sequence_id.id, test="id", context=context)
return super(ir_sequence, self).get_id(cr, uid, sequence_id, test, context)
ir_sequence()
+4 -4
View File
@@ -7,16 +7,16 @@ msgstr ""
"Project-Id-Version: OpenERP Server 5.0.0\n"
"Report-Msgid-Bugs-To: support@openerp.com\n"
"POT-Creation-Date: 2009-08-28 16:01+0000\n"
"PO-Revision-Date: 2009-09-08 13:46+0000\n"
"Last-Translator: Fabien (Open ERP) <fp@tinyerp.com>\n"
"PO-Revision-Date: 2010-01-29 07:09+0000\n"
"Last-Translator: Stefano Prenna <Unknown>\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2009-12-16 05:27+0000\n"
"X-Launchpad-Export-Date: 2010-01-30 04:40+0000\n"
"X-Generator: Launchpad (build Unknown)\n"
#. module: account_chart
#: model:ir.module.module,description:account_chart.module_meta_information
msgid "Remove minimal account chart"
msgstr ""
msgstr "Rimuovi il grafico del conto minimo"
+71 -23
View File
@@ -184,7 +184,10 @@ class CalDAV(object):
continue
if cal_data.name.lower() == 'valarm':
alarm = self.pool.get('basic.calendar.alarm')
vals = alarm.import_cal(cr, uid, cal_data)
ctx = context.copy()
if cal_children:
ctx.update({'model': cal_children[cal_data.name.lower()]})
vals = alarm.import_cal(cr, uid, cal_data, context=ctx)
self.ical_set(cal_data.name.lower(), vals, 'value')
continue
if cal_data.name.lower() in self.__attribute__:
@@ -194,9 +197,7 @@ class CalDAV(object):
def create_ics(self, cr, uid, datas, name, ical, context=None):
if not datas:
model = context.get('model', None)
war_str = "No data available" + (model and " for " + model) or ""
raise osv.except_osv(_('Warning !'), _(war_str))
return
for data in datas:
vevent = ical.add(name)
for field in self.__attribute__.keys():
@@ -229,9 +230,11 @@ class CalDAV(object):
data[map_field], vevent, context=context)
elif field == 'valarm' and data[map_field]:
model = self.__attribute__[field].get('object', False)
ctx = context.copy()
ctx.update({'model': model})
alarm_obj = self.pool.get('basic.calendar.alarm')
vevent = alarm_obj.export_cal(cr, uid, model, \
data[map_field][0], vevent, context=context)
data[map_field][0], vevent, context=ctx)
elif data[map_field]:
if map_type in ("char", "text"):
vevent.add(field).value = tools.ustr(data[map_field])
@@ -333,21 +336,18 @@ class Calendar(CalDAV, osv.osv):
}
def export_cal(self, cr, uid, datas, vobj='vevent', context={}):
try:
cal = self.browse(cr, uid, datas[0])
ical = vobject.iCalendar()
for line in cal.line_ids:
if line.name in ('alarm', '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})
self.__attribute__ = get_attribute_mapping(cr, uid, line.name, context)
self.create_ics(cr, uid, datas, line.name, ical, context=context)
return ical.serialize()
except Exception, e:
raise osv.except_osv(('Error !'), (str(e)))
cal = self.browse(cr, uid, datas[0])
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})
self.__attribute__ = get_attribute_mapping(cr, uid, line.name, context)
self.create_ics(cr, uid, datas, line.name, ical, context=context)
return ical.serialize()
def import_cal(self, cr, uid, content, data_id=None, context=None):
ical_data = base64.decodestring(content)
@@ -378,7 +378,7 @@ class basic_calendar_line(osv.osv):
_description = 'Calendar Lines'
_columns = {
'name': fields.selection([('vevent', 'Event'), ('vtodo', 'TODO'), \
('alarm', 'Alarm'), \
('valarm', 'Alarm'), \
('attendee', 'Attendee')], \
string="Type", size=64),
'object_id': fields.many2one('ir.model', 'Object'),
@@ -427,6 +427,51 @@ class basic_calendar_fields(osv.osv):
_defaults = {
'fn': lambda *a: 'field',
}
def check_line(self, cr, uid, vals, name, context=None):
f_obj = self.pool.get('ir.model.fields')
field = f_obj.browse(cr, uid, vals['field_id'], context=context)
relation = field.relation
line_obj = self.pool.get('basic.calendar.lines')
l_id = line_obj.search(cr, uid, [('name', '=', name)])
if l_id:
line = line_obj.browse(cr, uid, l_id, context=context)[0]
line_rel = line.object_id.model
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={}):
cr.execute('select name from basic_calendar_attributes \
where id=%s' % (vals.get('name')))
name = cr.fetchone()
name = name[0]
if name in ('valarm', 'attendee'):
self.check_line(cr, uid, vals, name, context=context)
cr.execute("Select count(id) from basic_calendar_fields \
where name=%s and type_id=%s" % (vals.get('name'), vals.get('type_id')))
res = cr.fetchone()
if res:
if res[0] > 0:
raise osv.except_osv(_('Warning !'), _('Can not map same 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):
if not vals:
return
for id in ids:
field = self.browse(cr, uid, id, context=context)
name = field.name.name
if name in ('valarm', 'attendee'):
self.check_line(cr, uid, vals, name, context=context)
qry = "Select count(id) from basic_calendar_fields \
where name=%s and type_id=%s" % (field.name.id, field.type_id.id)
cr.execute(qry)
res = cr.fetchone()
if res:
if res[0] > 1:
raise osv.except_osv(_('Warning !'), _('Can not map same field more than once'))
return super(basic_calendar_fields, self).write(cr, uid, ids, vals, context)
basic_calendar_fields()
@@ -588,11 +633,14 @@ class Alarm(CalDAV, osv.osv_memory):
trigger.value = delta
# Compute other details
valarm.add('DESCRIPTION').value = alarm_data['name']
valarm.add('DESCRIPTION').value = alarm_data['name'] or 'OpenERP'
valarm.add('ACTION').value = alarm_data['action']
return vevent
def import_cal(self, cr, uid, ical_data):
def import_cal(self, cr, uid, ical_data, context=None):
ctx = context.copy()
ctx.update({'model': context.get('model', None)})
self.__attribute__ = get_attribute_mapping(cr, uid, self._calname, ctx)
for child in ical_data.getChildren():
if child.name.lower() == 'trigger':
seconds = child.value.seconds
+1 -1
View File
@@ -19,7 +19,7 @@
<tree string="Attributes Mapping" editable="bottom">
<field name="name" required="1" domain="[('type', '=', parent.name)]"/>
<field name="fn" select="1" />
<field name="field_id" select="1" domain="[('model_id', '=', parent.object_id)]" />
<field name="field_id" select="1" required="1" domain="[('model_id', '=', parent.object_id)]" />
<field name="expr" />
</tree>
<form string="Attributes Mapping">
@@ -66,13 +66,14 @@ class cal_event_subscribe_wizard(wizard.interface):
model_obj = pooler.get_pool(cr.dbname).get(model)
context.update({'url': data['form']['url_path'],
'model': data.get('model')})
vals = model_obj.import_cal(cr, uid, base64.encodestring(caldata), context)
vals = model_obj.import_cal(cr, uid, base64.encodestring(caldata), \
data['id'], context)
if vals:
cnt = vals['count']
return {}
def _result_set(self, cr, uid, data, context=None):
return {'msg': 'Subscribed %s Event(s)' % cnt}
return {'msg': 'Import Sucessful.'}
states = {
'init': {
+4 -4
View File
@@ -8,13 +8,13 @@ msgstr ""
"Project-Id-Version: openobject-addons\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2009-08-28 16:01+0000\n"
"PO-Revision-Date: 2009-10-08 08:06+0000\n"
"Last-Translator: Fabien (Open ERP) <fp@tinyerp.com>\n"
"PO-Revision-Date: 2010-01-31 20:05+0000\n"
"Last-Translator: Rinalds <rranny@gmail.com>\n"
"Language-Team: Latvian <lv@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2009-12-16 05:08+0000\n"
"X-Launchpad-Export-Date: 2010-02-01 04:45+0000\n"
"X-Generator: Launchpad (build Unknown)\n"
#. module: base_contact
@@ -195,7 +195,7 @@ msgstr "Kategorijas"
#. module: base_contact
#: field:res.partner.contact,function_id:0
msgid "Main Function"
msgstr ""
msgstr "Pamatfunkcija"
#. module: base_contact
#: model:process.transition,note:base_contact.process_transition_partnertoaddress0
+7 -4
View File
@@ -7,13 +7,13 @@ msgstr ""
"Project-Id-Version: OpenERP Server 5.0.0\n"
"Report-Msgid-Bugs-To: support@openerp.com\n"
"POT-Creation-Date: 2009-08-28 16:01+0000\n"
"PO-Revision-Date: 2009-09-08 14:11+0000\n"
"Last-Translator: Fabien (Open ERP) <fp@tinyerp.com>\n"
"PO-Revision-Date: 2010-01-29 14:07+0000\n"
"Last-Translator: Carlo <Unknown>\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2009-12-16 05:10+0000\n"
"X-Launchpad-Export-Date: 2010-01-30 04:39+0000\n"
"X-Generator: Launchpad (build Unknown)\n"
#. module: base_setup
@@ -165,6 +165,9 @@ msgid ""
"We suggest you to put bank information here:\n"
"IBAN: BE74 1262 0121 6907 - SWIFT: CPDF BE71 - VAT: BE0477.472.701"
msgstr ""
"Questa frase apparirà al fondo dei report.\n"
"Si possono inserire le coordinate bancarie qui:\n"
"IBAN: BE74 1262 0121 6907 - SWIFT: CPDF BE71 - VAT: BE0477.472.701"
#. module: base_setup
#: wizard_field:base_setup.base_setup,company,country_id:0
@@ -270,7 +273,7 @@ msgstr "Logo"
#. module: base_setup
#: model:ir.module.module,shortdesc:base_setup.module_meta_information
msgid "Base Setup"
msgstr ""
msgstr "Configurazione di base"
#. module: base_setup
#: wizard_button:base_setup.base_setup,company,init:0
+6 -6
View File
@@ -7,13 +7,13 @@ msgstr ""
"Project-Id-Version: OpenERP Server 5.0.0\n"
"Report-Msgid-Bugs-To: support@openerp.com\n"
"POT-Creation-Date: 2009-08-28 16:01+0000\n"
"PO-Revision-Date: 2009-09-08 12:29+0000\n"
"Last-Translator: Fabien (Open ERP) <fp@tinyerp.com>\n"
"PO-Revision-Date: 2010-01-29 14:08+0000\n"
"Last-Translator: Carlo <Unknown>\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2009-12-16 05:20+0000\n"
"X-Launchpad-Export-Date: 2010-01-30 04:40+0000\n"
"X-Generator: Launchpad (build Unknown)\n"
#. module: board
@@ -37,7 +37,7 @@ msgstr "Autore"
#. module: board
#: model:ir.module.module,shortdesc:board.module_meta_information
msgid "Dashboard main module"
msgstr ""
msgstr "Modulo principale cruscotti"
#. module: board
#: view:board.note:0
@@ -53,7 +53,7 @@ msgstr "Larghezza"
#. module: board
#: constraint:ir.actions.act_window:0
msgid "Invalid model name in the action definition."
msgstr ""
msgstr "Nome del modulo non valido nella definizione dell'azione."
#. module: board
#: field:board.board.line,name:0
@@ -137,7 +137,7 @@ msgstr "Dashboard"
#. module: board
#: model:ir.module.module,description:board.module_meta_information
msgid "Base module for all dashboards."
msgstr ""
msgstr "Modulo base per tutti i cruscotti."
#. module: board
#: field:board.board.line,position:0
+3 -59
View File
@@ -143,7 +143,7 @@
</record>
<record model="basic.calendar.lines" id="calendar_lines_alarm">
<field name="name">alarm</field>
<field name="name">valarm</field>
<field name="calendar_id" ref="base_calendar.basic_calendar1" />
<field name="object_id" search="[('model','=','calendar.alarm')]" />
</record>
@@ -170,13 +170,6 @@
<field name="fn">field</field>
</record>
<record model="basic.calendar.fields" id="map_event_4">
<field name="name" ref="base_calendar.field_event_attendee"/>
<field name="type_id" ref="caldav.calendar_lines_event" />
<field name="field_id" search="[('name','=','attendee_ids'),('model_id.model','=','calendar.event')]" />
<field name="fn">field</field>
</record>
<record model="basic.calendar.fields" id="map_event_5">
<field name="name" ref="base_calendar.field_event_rrule"/>
<field name="type_id" ref="caldav.calendar_lines_event" />
@@ -198,13 +191,6 @@
<field name="fn">field</field>
</record>
<record model="basic.calendar.fields" id="map_event_8">
<field name="name" ref="base_calendar.field_event_priority"/>
<field name="type_id" ref="caldav.calendar_lines_event" />
<field name="field_id" search="[('name','=','priority'),('model_id.model','=','calendar.event')]" />
<field name="fn">field</field>
</record>
<record model="basic.calendar.fields" id="map_event_9">
<field name="name" ref="base_calendar.field_event_location"/>
<field name="type_id" ref="caldav.calendar_lines_event" />
@@ -219,13 +205,6 @@
<field name="fn">field</field>
</record>
<record model="basic.calendar.fields" id="map_event_11">
<field name="name" ref="base_calendar.field_event_status"/>
<field name="type_id" ref="caldav.calendar_lines_event" />
<field name="field_id" search="[('name','=','state'),('model_id.model','=','calendar.event')]" />
<field name="fn">field</field>
</record>
<record model="basic.calendar.fields" id="map_event_12">
<field name="name" ref="base_calendar.field_event_exdate"/>
<field name="type_id" ref="caldav.calendar_lines_event" />
@@ -284,13 +263,6 @@
<record model="basic.calendar.fields" id="map_todo_1">
<field name="name" ref="base_calendar.field_todo_status"/>
<field name="type_id" ref="caldav.calendar_lines_todo" />
<field name="field_id" search="[('name','=','state'),('model_id.model','=','calendar.todo')]" />
<field name="fn">field</field>
</record>
<record model="basic.calendar.fields" id="map_todo_2">
<field name="name" ref="base_calendar.field_todo_exdate"/>
<field name="type_id" ref="caldav.calendar_lines_todo" />
@@ -298,13 +270,6 @@
<field name="fn">field</field>
</record>
<record model="basic.calendar.fields" id="map_todo_3">
<field name="name" ref="base_calendar.field_todo_attendee"/>
<field name="type_id" ref="caldav.calendar_lines_todo" />
<field name="field_id" search="[('name','=','attendee_ids'),('model_id.model','=','calendar.todo')]" />
<field name="fn">field</field>
</record>
<record model="basic.calendar.fields" id="map_todo_4">
<field name="name" ref="base_calendar.field_todo_valarm"/>
<field name="type_id" ref="caldav.calendar_lines_todo" />
@@ -333,13 +298,6 @@
<field name="fn">field</field>
</record>
<record model="basic.calendar.fields" id="map_todo_9">
<field name="name" ref="base_calendar.field_todo_percent"/>
<field name="type_id" ref="caldav.calendar_lines_todo" />
<field name="field_id" search="[('name','=','progress_rate'),('model_id.model','=','calendar.todo')]" />
<field name="fn">field</field>
</record>
<record model="basic.calendar.fields" id="map_todo_11">
<field name="name" ref="base_calendar.field_todo_summary"/>
<field name="type_id" ref="caldav.calendar_lines_todo" />
@@ -347,13 +305,6 @@
<field name="fn">field</field>
</record>
<record model="basic.calendar.fields" id="map_todo_12">
<field name="name" ref="base_calendar.field_todo_priority"/>
<field name="type_id" ref="caldav.calendar_lines_todo" />
<field name="field_id" search="[('name','=','priority'),('model_id.model','=','calendar.todo')]" />
<field name="fn">field</field>
</record>
<record model="basic.calendar.fields" id="map_todo_13">
<field name="name" ref="base_calendar.field_todo_location"/>
<field name="type_id" ref="caldav.calendar_lines_todo" />
@@ -368,13 +319,6 @@
<field name="fn">field</field>
</record>
<record model="basic.calendar.fields" id="map_todo_15">
<field name="name" ref="base_calendar.field_todo_duration"/>
<field name="type_id" ref="caldav.calendar_lines_todo" />
<field name="field_id" search="[('name','=','planned_hours'),('model_id.model','=','calendar.todo')]" />
<field name="fn">hours</field>
</record>
<record model="basic.calendar.fields" id="map_todo_16">
<field name="name" ref="base_calendar.field_todo_dtstart"/>
<field name="type_id" ref="caldav.calendar_lines_todo" />
@@ -422,7 +366,7 @@
<record model="basic.calendar.fields" id="map_alarm_3">
<field name="name" ref="base_calendar.field_alarm_description"/>
<field name="type_id" ref="caldav.calendar_lines_alarm" />
<field name="field_id" search="[('name','=','name'),('model_id.model','=','calendar.alarm')]" />
<field name="field_id" search="[('name','=','description'),('model_id.model','=','calendar.alarm')]" />
<field name="fn">field</field>
</record>
@@ -450,7 +394,7 @@
<record model="basic.calendar.fields" id="map_alarm_7">
<field name="name" ref="base_calendar.field_alarm_summary"/>
<field name="type_id" ref="caldav.calendar_lines_alarm" />
<field name="field_id" search="[('name','=','description'),('model_id.model','=','calendar.alarm')]" />
<field name="field_id" search="[('name','=','name'),('model_id.model','=','calendar.alarm')]" />
<field name="fn">field</field>
</record>
+6 -5
View File
@@ -26,11 +26,12 @@
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Leads Form">
<group colspan="4" col="6">
<group colspan="4" col="7">
<field name="name" select="1" string="Lead Subject"/>
<field name="section_id" colspan="1" widget="selection"/>
<field name="user_id" select="2"/>
<newline/>
<field name="opportunity_id" string="Opportunity Case"/>
<button string="Convert to Opportunity" name="%(wizard_crm_lead_opportunity_set)d" icon="gtk-index" type="action" attrs="{'invisible':[('opportunity_id','!=',False)]}"/>
<label string="Stage: " align="1.0"/>
<group colspan="1" col="2">
<field name="stage_id" select="1" nolabel="1"/>
@@ -39,9 +40,9 @@
<field name="categ_id" select="1" on_change="onchange_categ_id(categ_id)" string="Lead Source"/>
<field name="type_id" string="Campaign Type" select="1"/>
<newline/>
<field name="date"/>
<field name="date_deadline"/>
<field name="opportunity_id" string="Opportunity Case"/>
<button string="Convert to Opportunity" name="%(wizard_crm_lead_opportunity_set)d" icon="gtk-index" type="action" attrs="{'invisible':[('opportunity_id','!=',False)]}" colspan="2"/>
<field name="user_id" select="2"/>
</group>
<notebook colspan="4">
<page string="Leads">
+2 -2
View File
@@ -44,14 +44,14 @@
</record>
<record model="basic.calendar.fields" id="caldav.map_event_8">
<record model="basic.calendar.fields" id="map_event_8">
<field name="name" ref="base_calendar.field_event_priority"/>
<field name="type_id" ref="caldav.calendar_lines_event" />
<field name="field_id" search="[('name','=','priority'),('model_id.model','=','crm.meeting')]" />
<field name="fn">field</field>
</record>
<record model="basic.calendar.fields" id="caldav.map_event_11">
<record model="basic.calendar.fields" id="map_event_11">
<field name="name" ref="base_calendar.field_event_status"/>
<field name="type_id" ref="caldav.calendar_lines_event" />
<field name="field_id" search="[('name','=','state'),('model_id.model','=','crm.meeting')]" />
+2 -3
View File
@@ -46,9 +46,8 @@
icon="gtk-save-as" type="action" context="{'model' : 'crm.meeting'}"
attrs="{'readonly': [('rrule_type','!=','custom')]}"
/>
</group>
<field name="phonecall_id"/>
<field name="opportunity_id"/>
</group>
</group>
<notebook colspan="4">
<page string="Description">
+2 -5
View File
@@ -51,13 +51,10 @@
<field name="stage_id" select="1" nolabel="1"/>
<button icon="gtk-go-forward" string="" name="stage_next" type="object"/>
</group>
<field name="date_deadline"/>
<field name="date" />
<field name="date_deadline"/>
<button string="Schedule a Phone Call"
name="%(wizard_crm_opportunity_reschedule_phone_call)d" icon="gtk-redo" type="action" attrs="{'invisible':[('phonecall_id','!=',False)]}" />
<group colspan="2" attrs="{'invisible' : [('phonecall_id','=',False)]}" >
<field name="phonecall_id" />
</group>
name="%(wizard_crm_opportunity_reschedule_phone_call)d" icon="gtk-redo" type="action" />
</group>
<notebook colspan="4">
<page string="Description">
+11 -11
View File
@@ -82,19 +82,19 @@
<field name="name" string="Subject"/>
<field name="section_id" colspan="1" widget="selection"/>
<field name="user_id" string="Assigned to" select="2"/>
<button string="Convert to Opportunity" name="%(wizard_crm_phonecall_opportunity_set)d" icon="gtk-index" type="action" attrs="{'invisible':[('opportunity_id','!=',False)]}" />
<newline/>
<field name="date" string="Planned Date"/>
<field name="duration"/>
<group colspan="2">
<field name="opportunity_id" on_change="onchange_case_id(case_id, name, partner_id)"
context="{'default_name':name,'default_section_id':section_id,'default_user_id':user_id,'default_duration':duration,
'default_partner_id':partner_id,'default_partner_address_id':partner_address_id,'default_partner_phone':partner_phone,
'default_partner_mobile':partner_mobile,'default_categ_id':categ_id}"/>
</group>
<button string="Schedule a Meeting"
name="%(wizard_crm_phonecall_meeting_set)d" icon="gtk-redo" type="action" />
<newline/>
<field name="date" string="Planned Date"/>
<field name="duration"/>
<field name="opportunity_id" on_change="onchange_case_id(case_id, name, partner_id)"
context="{'default_name':name,'default_section_id':section_id,'default_user_id':user_id,'default_duration':duration,
'default_partner_id':partner_id,'default_partner_address_id':partner_address_id,'default_partner_phone':partner_phone,
'default_partner_mobile':partner_mobile,'default_categ_id':categ_id}"/>
<button string="Convert to Opportunity" name="%(wizard_crm_phonecall_opportunity_set)d" icon="gtk-index" type="action" attrs="{'invisible':[('opportunity_id','!=',False)]}" />
</group>
<notebook colspan="4">
<page string="General">
+2 -4
View File
@@ -99,7 +99,6 @@ class opportunity2phonecall(wizard.interface):
}, context=context)
vals = {}
opportunity_case_obj.case_cancel(cr, uid, [opportunity.id])
phonecall_case_obj.case_open(cr, uid, [new_case])
value = {
@@ -145,8 +144,7 @@ class opportunity2meeting(wizard.interface):
})
new_meeting = meeting_case_obj.browse(cr, uid, new_meeting_id)
vals = {}
opportunity_case_obj.write(cr, uid, [opportunity.id], vals)
opportunity_case_obj.case_cancel(cr, uid, [opportunity.id])
opportunity_case_obj.write(cr, uid, [opportunity.id], vals)
meeting_case_obj.case_open(cr, uid, [new_meeting_id])
data_obj = pool.get('ir.model.data')
result = data_obj._get_id(cr, uid, 'crm', 'view_crm_case_meetings_filter')
@@ -162,7 +160,7 @@ class opportunity2meeting(wizard.interface):
id3 = data_obj.browse(cr, uid, id3, context=context).res_id
return {
'name': _('Meetings'),
'domain' : "[('opportunity_id','=',%s)]"%(data['ids']),
'domain' : "[('opportunity_id','in',%s)]"%(data['ids']),
'view_type': 'form',
'view_mode': 'calendar,form,tree',
'res_model': 'crm.meeting',
+5 -3
View File
@@ -109,8 +109,10 @@ class phonecall2opportunity(wizard.interface):
})
new_opportunity = opportunity_case_obj.browse(cr, uid, new_opportunity_id)
vals = {
'partner_id': data['form']['partner_id'],
}
'partner_id': data['form']['partner_id'],
'opportunity_id' : new_opportunity_id,
}
phonecall_case_obj.write(cr, uid, [phonecall.id], vals)
phonecall_case_obj.case_cancel(cr, uid, [phonecall.id])
opportunity_case_obj.case_open(cr, uid, [new_opportunity_id])
value = {
@@ -221,7 +223,7 @@ class phonecall2meeting(wizard.interface):
id3 = data_obj.browse(cr, uid, id3, context=context).res_id
return {
'name': _('Meetings'),
'domain' : "[('phonecall_id','=',%s)]"%(data['ids']),
'domain' : "[('phonecall_id','in',%s)]"%(data['ids']),
'view_type': 'form',
'view_mode': 'calendar,form,tree',
'res_model': 'crm.meeting',
+6 -6
View File
@@ -7,13 +7,13 @@ msgstr ""
"Project-Id-Version: OpenERP Server 5.0.0\n"
"Report-Msgid-Bugs-To: support@openerp.com\n"
"POT-Creation-Date: 2009-08-28 16:01+0000\n"
"PO-Revision-Date: 2009-11-18 06:22+0000\n"
"Last-Translator: Fabien (Open ERP) <fp@tinyerp.com>\n"
"PO-Revision-Date: 2010-01-30 21:47+0000\n"
"Last-Translator: Domas Jokubauskis <domasj@gmail.com>\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2009-12-16 05:21+0000\n"
"X-Launchpad-Export-Date: 2010-01-31 04:51+0000\n"
"X-Generator: Launchpad (build Unknown)\n"
#. module: hr
@@ -42,7 +42,7 @@ msgstr "Soc.draud.nr"
#: model:ir.module.module,shortdesc:hr.module_meta_information
#: model:ir.ui.menu,name:hr.menu_hr_root
msgid "Human Resources"
msgstr ""
msgstr "Personalas"
#. module: hr
#: selection:hr.timesheet,dayofweek:0
@@ -52,7 +52,7 @@ msgstr "Sekmadienis"
#. module: hr
#: view:res.users:0
msgid "Parents"
msgstr ""
msgstr "Tėvai"
#. module: hr
#: constraint:ir.actions.act_window:0
@@ -431,7 +431,7 @@ msgstr "Tautybė"
#. module: hr
#: field:hr.employee,otherid:0
msgid "Other ID"
msgstr ""
msgstr "Kitas ID"
#. module: hr
#: field:hr.timesheet,name:0
@@ -463,10 +463,12 @@ class hr_attendance(osv.osv):
FROM hr_timesheet_sheet_sheet s \
LEFT JOIN (hr_attendance a \
LEFT JOIN hr_employee e \
LEFT JOIN resource_resource r \
ON (e.resource_id = r.id) \
ON (a.employee_id = e.id)) \
ON (s.date_to >= date_trunc('day',a.name) \
AND s.date_from <= a.name \
AND s.user_id = e.user_id) \
AND s.user_id = r.user_id) \
WHERE a.id in (" + ",".join([str(x) for x in ids]) + ") \
GROUP BY a.id")
res = dict(cursor.fetchall())
+1 -1
View File
@@ -245,7 +245,7 @@ class product_template(osv.osv):
'description': fields.text('Description',translate=True),
'description_purchase': fields.text('Purchase Description',translate=True),
'description_sale': fields.text('Sale Description',translate=True),
'type': fields.selection([('product','Stockable Product'),('rima','Rima'),('consu', 'Consumable'),('service','Service')], 'Product Type', required=True, help="Will change the way requisitions are processed. Consumables are stockable products with infinite stock, or for use when you have no inventory management in the system."),
'type': fields.selection([('product','Stockable Product'),('consu', 'Consumable'),('service','Service')], 'Product Type', required=True, help="Will change the way requisitions are processed. Consumables are stockable products with infinite stock, or for use when you have no inventory management in the system."),
'supply_method': fields.selection([('produce','Produce'),('buy','Buy')], 'Supply method', required=True, help="Produce will generate production order or tasks, according to the product type. Purchase will trigger purchase orders when requested."),
'sale_delay': fields.float('Customer Lead Time', help="This is the average time between the confirmation of the customer order and the delivery of the finished products. It's the time you promise to your customers."),
'produce_delay': fields.float('Manufacturing Lead Time', help="Average time to produce this product. This is only for the production order and, if it is a multi-level bill of material, it's only for the level of this product. Different lead times will be summed for all levels and purchase orders."),
+23 -79
View File
@@ -29,9 +29,9 @@ from tools.translate import _
class project_task_type(osv.osv):
_name = 'project.task.type'
_description = 'Project task type'
_description = 'Task Stage'
_columns = {
'name': fields.char('Type', required=True, size=64, translate=True),
'name': fields.char('Stage Name', required=True, size=64, translate=True),
'description': fields.text('Description'),
'sequence': fields.integer('Sequence'),
}
@@ -52,7 +52,6 @@ class project(osv.osv):
res[m.id] = (m.parent_id and (m.parent_id.name + '/') or '') + m.name
return res
def check_recursion(self, cursor, user, ids, parent=None):
return super(project, self).check_recursion(cursor, user, ids,
parent=parent)
@@ -104,43 +103,30 @@ class project(osv.osv):
raise osv.except_osv(_('Operation Not Permitted !'), _('You can not delete a project with tasks. I suggest you to deactivate it.'))
return super(project, self).unlink(cr, uid, ids, *args, **kwargs)
_columns = {
# 'name': fields.char("Project Name", size=128, required=True),
# 'complete_name': fields.function(_complete_name, method=True, string="Project Name", type='char', size=128),
'complete_name': fields.function(_complete_name, method=True, string="Project Name", type='char', size=250),
'active': fields.boolean('Active', help="If the active field is set to true, it will allow you to hide the project without removing it."),
'category_id': fields.many2one('account.analytic.account','Analytic Account', help="Link this project to an analytic account if you need financial management on projects. It enables you to connect projects with budgets, planning, cost and revenue analysis, timesheets on projects, etc."),
'priority': fields.integer('Sequence'),
# 'manager': fields.many2one('res.users', 'Project Manager'),
'warn_manager': fields.boolean('Warn Manager', help="If you check this field, the project manager will receive a request each time a task is completed by his team."),
'resource_ids': fields.many2many('resource.resource', 'project_resource_rel', 'project_id', 'resource_id', 'Project Members', help="Project's member. Not used in any computation, just for information purpose."),
'tasks': fields.one2many('project.task', 'project_id', "Project tasks"),
# 'parent_id': fields.many2one('project.project', 'Parent Project',\
# help="If you have [?] in the name, it means there are no analytic account linked to project."),
# 'child_id': fields.one2many('project.project', 'parent_id', 'Subproject'),
'planned_hours': fields.function(_progress_rate, multi="progress", method=True, string='Planned Time', help="Sum of planned hours of all tasks related to this project."),
'effective_hours': fields.function(_progress_rate, multi="progress", method=True, string='Time Spent', help="Sum of spent hours of all tasks related to this project."),
'total_hours': fields.function(_progress_rate, multi="progress", method=True, string='Total Time', help="Sum of total hours of all tasks related to this project."),
'progress_rate': fields.function(_progress_rate, multi="progress", method=True, string='Progress', type='float', help="Percent of tasks closed according to the total of tasks todo."),
# 'partner_id': fields.many2one('res.partner', 'Partner'),
# 'contact_id': fields.many2one('res.partner.address', 'Contact'),
'warn_customer': fields.boolean('Warn Partner', help="If you check this, the user will have a popup when closing a task that propose a message to send by email to the customer."),
'warn_header': fields.text('Mail Header', help="Header added at the beginning of the email for the warning message sent to the customer when a task is closed."),
'warn_footer': fields.text('Mail Footer', help="Footer added at the beginning of the email for the warning message sent to the customer when a task is closed."),
# 'notes': fields.text('Notes', help="Internal description of the project."),
'resource_calendar_id': fields.many2one('resource.calendar', 'Working Time', help="Timetable working hours to adjust the gantt diagram report"),
# 'state': fields.selection([('template', 'Template'), ('open', 'Running'), ('pending', 'Pending'), ('cancelled', 'Cancelled'), ('done', 'Done')], 'State', required=True, readonly=True,
# help='The project can be in either if the states \'Template\' and \'Running\'.\n If it is template then we can make projects based on the template projects. If its in \'Running\' state it is a normal project.\
# \n If it is to be reviewed then the state is \'Pending\'.\n When the project is completed the state is set to \'Done\'.'),
# 'company_id': fields.many2one('res.company', 'Company'),
# 'timesheet_id': fields.many2one('hr.timesheet.group', 'Working Time', help="Timetable working hours to adjust the gantt diagram report"),
'type_ids': fields.many2many('project.task.type', 'project_task_type_rel', 'project_id', 'type_id', 'Allowed Task Types'),
'type_ids': fields.many2many('project.task.type', 'project_task_type_rel', 'project_id', 'type_id', 'Tasks Stages'),
}
_defaults = {
'active': lambda *a: True,
# 'manager': lambda object,cr,uid,context: uid,
'priority': lambda *a: 1,
# 'state': lambda *a: 'open',
# 'company_id': lambda self,cr,uid,c: self.pool.get('res.company')._company_default_get(cr, uid, 'project.project', context=c)
}
def _check_dates(self, cr, uid, ids):
leave = self.read(cr, uid, ids[0],['date_start','date'])
@@ -153,12 +139,6 @@ class project(osv.osv):
(_check_dates, 'Error! project start-date must be lower then project end-date.', ['date_start', 'date'])
]
# _order = "parent_id,priority,name"
# _constraints = [
# (check_recursion, 'Error ! You can not create recursive projects.', ['parent_id'])
# ]
# toggle activity of projects, their sub projects and their tasks
def set_template(self, cr, uid, ids, context={}):
res = self.setActive(cr, uid, ids, value=False, context=context)
return res
@@ -235,51 +215,12 @@ class task(osv.osv):
_description = "Tasks"
_date_name = "date_start"
# def compute_date(self,cr,uid):
# project_id = self.pool.get('project.project').search(cr,uid,[])
# for i in range(len(project_id)):
# task_ids = self.pool.get('project.task').search(cr,uid,[('project_id','=',project_id[i])])
# if task_ids:
# task_obj = self.pool.get('project.task').browse(cr,uid,task_ids)
# task_1 = task_obj[0]
# task_1.date_start = self.pool.get('project.project').browse(cr,uid,project_id[i]).date_start
# dt = mx.DateTime.strptime(task_1.date_start,"%Y-%m-%d").strftime("%Y-%m-%d")
# def Project_1():
# title = "New Project"
# start = dt
#
# def task1():
# start = dt
# effort = task_1.planned_hours
# title = "Task 1"
## project_1 = BalancedProject(Project_1)
## for t in project_1:
## print 'details:::',t.indent_name(), t.start, t.end, t.effort
def _str_get(self, task, level=0, border='***', context={}):
return border+' '+(task.user_id and task.user_id.name.upper() or '')+(level and (': L'+str(level)) or '')+(' - %.1fh / %.1fh'%(task.effective_hours or 0.0,task.planned_hours))+' '+border+'\n'+ \
border[0]+' '+(task.name or '')+'\n'+ \
(task.description or '')+'\n\n'
def _history_get(self, cr, uid, ids, name, args, context={}):
result = {}
for task in self.browse(cr, uid, ids, context=context):
result[task.id] = self._str_get(task, border='===')
t2 = task.parent_ids
level = 0
while t2:
level -= 1
result[task.id] = self._str_get(t2, level) + result[task.id]
t2 = t2.parent_ids
t3 = map(lambda x: (x,1), task.child_ids)
while t3:
t2 = t3.pop(0)
result[task.id] = result[task.id] + self._str_get(t2[0], t2[1])
t3 += map(lambda x: (x,t2[1]+1), t2[0].child_ids)
return result
# Compute: effective_hours, total_hours, progress
# Compute: effective_hours, total_hours, progress
def _hours_get(self, cr, uid, ids, field_names, args, context):
task_set = ','.join(map(str, ids))
cr.execute(("SELECT task_id, COALESCE(SUM(hours),0) FROM project_task_work WHERE task_id in (%s) GROUP BY task_id") % (task_set,))
@@ -293,6 +234,8 @@ class task(osv.osv):
res[task.id]['progress'] = round(min(100.0 * hours.get(task.id, 0.0) / res[task.id]['total_hours'], 100),2)
else:
res[task.id]['progress'] = 0.0
if task.state in ('done','cancel'):
res[task.id]['progress'] = 100.0
res[task.id]['delay_hours'] = res[task.id]['total_hours'] - task.planned_hours
return res
@@ -324,32 +267,30 @@ class task(osv.osv):
default['work_ids'] = []
return super(task, self).copy_data(cr, uid, id, default, context)
def _check_date(self,cr,uid,ids):
for res in self.browse(cr,uid,ids):
if res.date_start and res.date_end:
if res.date_start > res.date_end:
return False
return True
def _check_dates(self, cr, uid, ids):
task = self.read(cr, uid, ids[0],['date_start','date_end'])
if task['date_start'] and task['date_end']:
if task['date_start'] > task['date_end']:
return False
return True
_columns = {
'active': fields.boolean('Active', help="If the active field is set to true, it will allow you to hide the task without removing it."),
'name': fields.char('Task Summary', size=128, required=True),
'description': fields.text('Description'),
'priority' : fields.selection([('4','Very Low'), ('3','Low'), ('2','Medium'), ('1','Urgent'), ('0','Very urgent')], 'Importance'),
'sequence': fields.integer('Sequence', help="Gives the sequence order when displaying a list of tasks."),
'type': fields.many2one('project.task.type', 'Type', readonly=True),
'type': fields.many2one('project.task.type', 'Stage', readonly=True),
'state': fields.selection([('draft', 'Draft'),('open', 'In Progress'),('pending', 'Pending'), ('cancelled', 'Cancelled'), ('done', 'Done')], 'State', readonly=True, required=True,
help='If the task is created the state \'Draft\'.\n If the task is started, the state becomes \'In Progress\'.\n If review is needed the task is in \'Pending\' state.\
\n If the task is over, the states is set to \'Done\'.'),
'date_start': fields.datetime('Starting Date'),
'date_end': fields.datetime('Ending Date'),
'date_deadline': fields.datetime('Deadline'),
'date_close': fields.datetime('Date Closed', readonly=True),
'date_deadline': fields.date('Deadline'),
'project_id': fields.many2one('project.project', 'Project', ondelete='cascade',
help="If you have [?] in the project name, it means there are no analytic account linked to this project."),
'parent_ids': fields.many2many('project.task', 'project_task_parent_rel', 'task_id', 'parent_id', 'Parent Tasks'),
'child_ids': fields.many2many('project.task', 'project_task_child_rel', 'task_id', 'child_id', 'Delegated Tasks'),
'history': fields.function(_history_get, method=True, string="Task Details", type="text"),
'notes': fields.text('Notes'),
'occupation_rate': fields.float('Occupation Rate', help='The occupation rate fields indicates how much of his time a user is working on a task. A 100% occupation rate means the user works full time on the tasks. The ending date of a task is computed like this: Starting Date + Duration / Occupation Rate.'),
'planned_hours': fields.float('Planned Hours', required=True, help='Estimated time to do the task, usually set by the project manager when the task is in draft state.'),
@@ -378,8 +319,11 @@ class task(osv.osv):
'occupation_rate':lambda *a: '1',
'company_id': lambda self,cr,uid,c: self.pool.get('res.company')._company_default_get(cr, uid, 'project.task', context=c)
}
_order = "sequence, priority, date_deadline, id"
_order = "sequence, priority, date_start, id"
_constraints = [
(_check_dates, 'Error! task start-date must be lower then task end-date.', ['date_start', 'date_end'])
]
#
# Override view according to the company definition
#
@@ -427,7 +371,7 @@ class task(osv.osv):
'ref_doc1': 'project.task,%d'% (task.id,),
'ref_doc2': 'project.project,%d'% (project.id,),
})
self.write(cr, uid, [task.id], {'state': 'done', 'date_close':time.strftime('%Y-%m-%d %H:%M:%S'), 'remaining_hours': 0.0})
self.write(cr, uid, [task.id], {'state': 'done', 'date_end':time.strftime('%Y-%m-%d %H:%M:%S'), 'remaining_hours': 0.0})
if task.parent_ids and task.parent_ids.state in ('pending','draft'):
reopen = True
for child in task.parent_ids.child_ids:
@@ -506,9 +450,9 @@ class task(osv.osv):
typeid = typ.type.id
types = map(lambda x:x.id, typ.project_id.type_ids)
if types:
if typeid and typeid in types and types.index(typeid) != 0 :
if typeid and typeid in types:
index = types.index(typeid)
self.write(cr, uid, typ.id, {'type': types[index-1]})
self.write(cr, uid, typ.id, {'type': index and types[index-1] or False})
return True
task()
+27 -27
View File
@@ -3,7 +3,6 @@
<data>
<menuitem icon="terp-project" id="menu_main" name="Project Management"/>
<menuitem id="menu_project_management" name="Project Management" parent="menu_main" sequence="1"/>
<!-- <menuitem id="menu_tasks" name="Tasks" parent="menu_main"/>-->
<menuitem id="menu_pm_invoicing" name="Invoicing" parent="menu_main" sequence="7"/>
<menuitem id="menu_pm_budget" name="Budgets" parent="menu_pm_invoicing"/>
@@ -13,7 +12,7 @@
action="account_budget.act_crossovered_budget_view" />
<menuitem id="menu_definitions" name="Configuration" parent="project.menu_main" sequence="60"/>
<!--Time Tracking menu-->
<menuitem
<menuitem
id="menu_project_management_time_tracking"
name="Time Tracking"
parent="menu_main" sequence="3"/>
@@ -83,8 +82,7 @@
<label string="Date Stop: %%(date_stop)s"/>
</group>
</page>
<page string="Tasks" groups="base.group_extended">
<separator string="Task Types"/>
<page string="Tasks Stages" groups="base.group_extended">
<field nolabel="1" name="type_ids" colspan="4"/>
</page>
<page string="Notes">
@@ -168,7 +166,7 @@
<field name="view_id" ref="view_project"/>
</record>
<!--<menuitem action="open_view_template_project" id="menu_template_project" parent="project.menu_definitions"/>-->
<menuitem id="menu_template_project_specification" parent="project.menu_definitions" name="Specification" sequence="1"/>
<menuitem id="menu_template_project_specification" parent="project.menu_definitions" name="Specification" sequence="1"/>
<menuitem action="open_view_template_project" id="menu_template_project" parent="project.menu_template_project_specification" name="Specification Template"/>
<record id="view_task_work_form" model="ir.ui.view">
<field name="name">project.task.work.form</field>
@@ -268,10 +266,7 @@
<field name="name" select="1"/>
<field name="project_id" required="1" select="1"/>
<field name="total_hours" widget="float_time"/>
<field name="date_start"/>
<field name="date_deadline" select="2"/>
<field name="occupation_rate"/>
<field name="date_end"/>
<field name="user_id" select="1"/>
<field name="progress" widget="progressbar"/>
</group>
@@ -313,7 +308,7 @@
</group>
</page>
<page groups="base.group_extended" string="Delegations">
<separator string="Parent Tasks" colspan="4"/>
<separator string="Parent Tasks" colspan="4"/>
<field colspan="4" height="150" name="parent_ids" nolabel="1"/>
<separator string="Delegated tasks" colspan="4"/>
<field colspan="4" height="150" name="child_ids" nolabel="1" attrs="{'readonly':[('state','!=','draft')]}">
@@ -327,7 +322,6 @@
<field name="date_deadline"/>
</tree>
</field>
<field colspan="4" name="history" nolabel="1"/>
</page>
<page groups="base.group_extended" string="Extra Info" attrs="{'readonly':[('state','=','done')]}">
@@ -338,16 +332,17 @@
</group>
<group colspan="2" col="2">
<separator string="Dates" colspan="2"/>
<field name="date_close" select="2"/>
<field name="date_start"/>
<field name="date_end"/>
</group>
<separator string="Miscelleanous" colspan="4"/>
<field name="partner_id" select="2"/>
<field name="company_id" select="1" groups="base.group_multi_company" widget="selection"/>
<group col="4" colspan="2">
<field name="type" widget="selection" />
<button name="prev_type" string="Previous" type="object" icon="gtk-go-back" help="Change to Previous Type"/>
<button name="next_type" string="Next" type="object" icon="gtk-go-forward" help="Change to Next Type"/>
</group>
<button name="prev_type" string="Previous" type="object" icon="gtk-go-back" help="Change to Previous Stage"/>
<button name="next_type" string="Next" type="object" icon="gtk-go-forward" help="Change to Next Stage"/>
</group>
<field name="active" select="2"/>
<separator colspan="4" string="Notes"/>
<field colspan="4" name="notes" nolabel="1"/>
@@ -367,10 +362,16 @@
<field name="name"/>
<field name="project_id" icon="gtk-indent"/>
<field name="user_id"/>
<field name="remaining_hours" widget="float_time" sum="Remaining Hours"/>
<field name="date_deadline"/>
<field name="remaining_hours" widget="float_time"/>
<field name="type"/>
<button name="next_type" states="draft,open,pending" string="Change Type" type="object" icon="gtk-go-forward" help="Change Type"/>
<field name="type" groups="base.group_extended"/>
<button name="next_type"
states="draft,open,pending"
string="Change Stage"
type="object"
icon="gtk-go-forward"
groups="base.group_extended"
help="Change Type"/>
<field name="progress" widget="progressbar"/>
<field name="state"/>
<button name="do_open" states="pending,draft" string="Start Task" type="object" icon="gtk-execute" help="For changing to open state"/>
@@ -471,7 +472,7 @@
<field name="arch" type="xml">
<search string="Task Edition">
<group col="20" colspan="4">
<filter string="Draft" domain="[('state','=','draft')]" help="Draft Tasks" icon="terp-project"/>
<filter string="Current" domain="[('state','in',('open','draft'))]" help="Open and Draft Tasks" icon="terp-project" default="1"/>
<filter string="In Progress" domain="[('state','=','open')]" help="Open Tasks" icon="terp-project"/>
<filter string="Pending" domain="[('state','=','pending')]" help="Pending Tasks" icon="terp-project"/>
<separator orientation="vertical"/>
@@ -487,7 +488,8 @@
<group expand="1" string="Group By..." colspan="4" col="20">
<filter string="By Users" icon="terp-project" domain="[]" context="{'group_by':'user_id'}"/>
<filter string="By Project" icon="terp-project" domain="[]" context="{'group_by':'project_id'}"/>
<filter string="By Type" icon="terp-project" domain="[]" context="{'group_by':'type'}"/>
<filter string="By Stage" icon="terp-project" domain="[]" context="{'group_by':'type'}"/>
<filter string="By State" icon="terp-project" domain="[]" context="{'group_by':'state'}"/>
</group>
</search>
</field>
@@ -498,13 +500,11 @@
<field name="res_model">project.task</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form,calendar,gantt,graph</field>
<field eval="False" name="view_id"/>
<field eval="True" name="filter"/>
<field eval="False" name="filter"/>
<field name="view_id" ref="view_task_tree2"/>
<field name="search_view_id" ref="view_task_search_form"/>
</record>
<!--<menuitem action="action_view_task" id="menu_action_view_task" parent="project.menu_tasks"/> -->
<menuitem action="action_view_task" id="menu_action_view_task" parent="project.menu_project_management" name="Tasks" sequence="2"/>
<menuitem action="action_view_task" id="menu_action_view_task" parent="project.menu_project_management" sequence="2"/>
<record id="action_view_task_overpassed_draft" model="ir.actions.act_window">
<field name="name">Overpassed Tasks</field>
@@ -539,7 +539,7 @@
<field name="model">project.task.type</field>
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Task type">
<form string="Task Stage">
<field name="name" select="1"/>
<field name="sequence"/>
<field colspan="4" name="description" select="1"/>
@@ -551,8 +551,8 @@
<field name="model">project.task.type</field>
<field name="type">tree</field>
<field name="arch" type="xml">
<tree string="Task type">
<field name="sequence"/>
<tree string="Task Stage">
<field name="sequence"/>
<field name="name"/>
</tree>
</field>
@@ -677,7 +677,7 @@
<field name="view_id" ref="view_project_message_tree"/>
</record>
<!--<menuitem id="menu_message_main" name="Message" parent="menu_main"/>-->
<!--<menuitem id="menu_message_main" name="Message" parent="menu_main"/>-->
<menuitem action="action_view_project_messages_tree" id="menu_message_tree" parent="menu_project_management" sequence="11"/>
<act_window domain="[('user_id', '=', active_id),('state', '&lt;&gt;', 'cancelled'),('state', '&lt;&gt;', 'done')]" id="act_res_users_2_project_task_opened" name="Assigned tasks" res_model="project.task" src_model="res.users" view_mode="tree,form,gantt,calendar,graph" view_type="form"/>
+11
View File
@@ -3,10 +3,21 @@
<data>
<wizard id="wizard_close_task" menu="False" model="project.task" name="project.task.close" string="Close Task"/>
<wizard id="wizard_delegate_task" menu="False" model="project.task" name="project.task.delegate" string="Delegate Task"/>
<wizard id="wizard_compute_task" model="project.task" name="wizard.compute.tasks" string="Compute Task Scheduling"/>
<menuitem icon="terp-project" id="menu_main" name="Project Management"/>
<menuitem
action="wizard_compute_task"
id="menu_wizard_compute_task"
parent="menu_main"
type="wizard"
sequence="1"/>
<wizard
id="wizard_duplicate_template"
menu="False"
model="project.project"
name="project.duplicate.template" string="Duplicate Template"/>
</data>
</openerp>
@@ -37,47 +37,5 @@
</record>
<!--rule for Tasks-->
<record model="ir.rule.group" id="project_task_rule_group">
<field name="name">project.task.rule.group</field>
<field name="model_id" search="[('model','=','project.task')]" model="ir.model"/>
<field name="global" eval="True"/>
</record>
<record id="project_task_rule" model="ir.rule">
<field name="domain_force">['|',('project_id','=',False),('project_id','in',[user.context_project_id])]</field>
<field name="rule_group" ref="project_task_rule_group"/>
</record>
<record model="ir.rule.group" id="project_task_work_rule_group">
<field name="name">project.task.work.rule.group</field>
<field name="model_id" search="[('model','=','project.task.work')]" model="ir.model"/>
<field name="global" eval="True"/>
</record>
<record id="project_task_work_rule" model="ir.rule">
<field name="domain_force">['|',('task_id.project_id','=',False),('task_id.project_id','in',[user.context_project_id])]</field>
<field name="rule_group" ref="project_task_work_rule_group"/>
</record>
<record model="ir.rule.group" id="project_project_rule_group">
<field name="name">project.project.work.rule.group</field>
<field name="model_id" search="[('model','=','project.project')]" model="ir.model"/>
<field name="global" eval="True"/>
</record>
<record id="project_project_rule" model="ir.rule">
<field name="domain_force">['|',('id','=',False),('id','in',[user.context_project_id])]</field>
<field name="rule_group" ref="project_project_rule_group"/>
</record>
<record model="ir.rule.group" id="project_message_rule_group">
<field name="name">project.project.work.rule.group</field>
<field name="model_id" search="[('model','=','project.message')]" model="ir.model"/>
<field name="global" eval="True"/>
</record>
<record id="project_message_rule" model="ir.rule">
<field name="domain_force">['|',('project_id','=',False),('project_id','in',[user.context_project_id])]</field>
<field name="rule_group" ref="project_message_rule_group"/>
</record>
</data>
</openerp>
+4 -2
View File
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
##############################################################################
#
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
#
@@ -15,13 +15,15 @@
# 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 close_task
import task_delegate
import compute_tasks_date
import duplicate_template_wizard
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
+193
View File
@@ -0,0 +1,193 @@
# -*- 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
from tools.translate import _
import datetime
from resource.faces import *
compute_form = """<?xml version="1.0" ?>
<form string="Compute Scheduling of Tasks">
<field name="project_id" colspan="4"/>
</form>"""
success_msg = """<?xml version="1.0" ?>
<form string="Compute Scheduling of Tasks">
<label string="Task Scheduling completed scccessfully."/>
</form>"""
compute_fields = {
'project_id': {'string':'Project', 'type':'many2one', 'relation': 'project.project', 'required':'True'},
}
def timeformat_convert(cr, uid, time_string, context={}):
strg = str(time_string)[-2:]
last_strg = str(time_string)[0:-2]
if strg != '.0':
if '.' not in strg:
strg = '.' + strg
last_strg = str(time_string)[:-3]
new_strg = round(float(strg) / 0.016666667)
converted_strg = last_strg + ':' + str(new_strg)[:-2]
else:
converted_strg = last_strg + ':00'
return converted_strg
class wizard_compute_tasks(wizard.interface):
def _compute_date(self, cr, uid, data, context):
pool = pooler.get_pool(cr.dbname)
project_pool = pool.get('project.project')
task_pool = pool.get('project.task')
resource_pool = pool.get('resource.resource')
resource_leaves_pool = pool.get('resource.calendar.leaves')
resource_week_pool = pool.get('resource.calendar.week')
project_id = data['form']['project_id']
project = project_pool.browse(cr,uid,project_id)
task_ids = task_pool.search(cr,uid,[('project_id','=',project_id)])
if task_ids:
task_obj = task_pool.browse(cr,uid,task_ids)
task_1 = task_obj[0]
if task_1.date_start:
date_dt = datetime.datetime.strptime(task_1.date_start,"%Y-%m-%d %H:%M:%S")
else:
date_dt = datetime.datetime.strptime(project.date_start,"%Y-%m-%d")
dt = datetime.datetime.strftime(date_dt,"%Y-%m-%d %H:%M")
for i in range(len(task_obj)):
final_lst = []
leaves = []
if task_obj[i].user_id.id:
resource_id = resource_pool.search(cr,uid,[('user_id','=',task_obj[i].user_id.id)])
if resource_id :
resource_obj = resource_pool.browse(cr,uid,resource_id)[0]
if resource_obj.calendar_id:
calendar_id = resource_obj.calendar_id.id
resource_leave_ids = resource_leaves_pool.search(cr,uid,[('resource_id','=',resource_id)])
else:
calendar_id = project.resource_calendar_id.id
resource_leave_ids = resource_leaves_pool.search(cr,uid,[('calendar_id','=',project.resource_calendar_id.id)])
time_range = "8:00-8:00"
non_working = ""
wk = {"0":"mon","1":"tue","2":"wed","3":"thu","4":"fri","5":"sat","6":"sun"}
wk_days = {}
wk_time = {}
tlist = []
week_ids = resource_week_pool.search(cr,uid,[('calendar_id','=',calendar_id)])
week_obj = resource_week_pool.read(cr,uid,week_ids,['dayofweek','hour_from','hour_to'])
for week in week_obj:
res_str = ""
if wk.has_key(week['dayofweek']):
day = wk[week['dayofweek']]
wk_days[week['dayofweek']] = wk[week['dayofweek']]
hour_from_str = timeformat_convert(cr,uid,week['hour_from'])
hour_to_str = timeformat_convert(cr,uid,week['hour_to'])
res_str = hour_from_str + '-' + hour_to_str
tlist.append((day,res_str))
for item in tlist:
if wk_time.has_key(item[0]):
wk_time[item[0]].append(item[1])
else:
wk_time[item[0]] = [item[0]]
wk_time[item[0]].append(item[1])
for k,v in wk_time.items():
final_lst.append(tuple(v))
for k,v in wk_days.items():
if wk.has_key(k):
wk.pop(k)
for v in wk.itervalues():
non_working += v + ','
if non_working:
final_lst.append((non_working[:-1],time_range))
if resource_leave_ids:
res_leaves = resource_leaves_pool.read(cr,uid,resource_leave_ids,['date_from','date_to'])
for leave in range(len(res_leaves)):
dt_start = datetime.datetime.strptime(res_leaves[leave]['date_from'],'%Y-%m-%d %H:%M:%S')
dt_end = datetime.datetime.strptime(res_leaves[leave]['date_to'],'%Y-%m-%d %H:%M:%S')
no = dt_end - dt_start
leave_days = no.days + 1
[leaves.append((dt_start + datetime.timedelta(days=x)).strftime('%Y-%m-%d')) for x in range(int(leave_days))]
leaves.sort()
hours = task_obj[i].remaining_hours / task_obj[i].occupation_rate
hours = str(hours)[:-2] + 'H'
class user(Resource):
pass
if i == 0:
new_dt = dt
else:
data = task_pool.read(cr,uid,[task_obj[i-1].id],['date_end'])[0]
new_dt = data['date_end'][0:16]
def Project_1():
resource = user
title = project.name
start = new_dt
effort = hours
if final_lst or leaves:
working_days = final_lst
vacation = tuple(leaves)
def task1():
start = new_dt
effort = hours
title = task_obj[i].name
project_1 = BalancedProject(Project_1)
s_date = project_1.calendar.WorkingDate(project_1.task1.start).to_datetime()
e_date = project_1.calendar.WorkingDate(project_1.task1.end).to_datetime()
task_pool.write(cr,uid,[task_obj[i].id],{'date_start':s_date,'date_end':e_date})
return {}
states = {
'init': {
'actions': [],
'result': {'type':'form', 'arch':compute_form, 'fields':compute_fields, 'state':[
('end', 'Cancel'),
('compute', 'Compute')
]},
},
'compute': {
'actions': [_compute_date],
'result': {'type':'form','arch':success_msg,'fields':{}, 'state':[('end', 'Ok')]},
}
}
wizard_compute_tasks('wizard.compute.tasks')
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
@@ -8,7 +8,7 @@
<field name="object_id" search="[('model','=','project.task')]" />
</record>
<record model="basic.calendar.fields" id="caldav.map_todo_1">
<record model="basic.calendar.fields" id="map_todo_1">
<field name="name" ref="base_calendar.field_todo_status" />
<field name="type_id" ref="caldav.calendar_lines_todo" />
<field name="field_id" search="[('name','=','state'),('model_id.model','=','project.task')]" />
@@ -16,28 +16,28 @@
<field name="fn">field</field>
</record>
<record model="basic.calendar.fields" id="caldav.map_todo_3">
<record model="basic.calendar.fields" id="map_todo_3">
<field name="name" ref="base_calendar.field_todo_attendee" />
<field name="type_id" ref="caldav.calendar_lines_todo" />
<field name="field_id" search="[('name','=','attendee_ids'),('model_id.model','=','project.task')]" />
<field name="fn">field</field>
</record>
<record model="basic.calendar.fields" id="caldav.map_todo_9">
<record model="basic.calendar.fields" id="map_todo_9">
<field name="name" ref="base_calendar.field_todo_percent"/>
<field name="type_id" ref="caldav.calendar_lines_todo" />
<field name="field_id" search="[('name','=','progress'),('model_id.model','=','project.task')]" />
<field name="fn">field</field>
</record>
<record model="basic.calendar.fields" id="caldav.map_todo_12">
<record model="basic.calendar.fields" id="map_todo_12">
<field name="name" ref="base_calendar.field_event_priority"/>
<field name="type_id" ref="caldav.calendar_lines_todo" />
<field name="field_id" search="[('name','=','priority'),('model_id.model','=','project.task')]" />
<field name="fn">field</field>
</record>
<record model="basic.calendar.fields" id="caldav.map_todo_15">
<record model="basic.calendar.fields" id="map_todo_15">
<field name="name" ref="base_calendar.field_todo_duration"/>
<field name="type_id" ref="caldav.calendar_lines_todo" />
<field name="field_id" search="[('name','=','planned_hours'),('model_id.model','=','project.task')]" />
+1 -1
View File
@@ -43,7 +43,7 @@ performing those tasks.
'update_xml': [
'project_gtd_data.xml',
'project_gtd_view.xml',
'project_gtd_wizard.xml',
'project_gtd_wizard.xml',
'security/ir.model.access.csv',
],
'demo_xml': ['project_gtd_demo.xml'],
+1 -1
View File
@@ -169,7 +169,7 @@ class project_task(osv.osv):
timebox_obj = self.pool.get('project.gtd.timebox')
if res['type'] == 'search':
tt = timebox_obj.browse(cr, uid, timebox_obj.search(cr,uid,[]))
search_extended ='''<newline/><group col="%d" expand="1" string="%s">''' % (len(tt)+6,_('Getting Things Done'))
search_extended ='''<newline/><group col="%d" expand="1" string="%s" groups="project_gtd.group_project_getting">''' % (len(tt)+6,_('Getting Things Done'))
search_extended += '''<filter domain="[('timebox_id','=', False)]" icon="gtk-new" string="Inbox"/>'''
search_extended += '''<separator orientation="vertical"/>'''
for time in tt:
+12 -50
View File
@@ -7,8 +7,8 @@
<field name="type">tree</field>
<field name="arch" type="xml">
<tree string="Context">
<field name="sequence" invisible="1"/>
<field name="name"/>
<field name="project_default_id"/>
</tree>
</field>
</record>
@@ -38,7 +38,7 @@
<field name="type">tree</field>
<field name="arch" type="xml">
<tree string="Timebox">
<field name="sequence"/>
<field name="sequence" invisible="1"/>
<field name="name"/>
<field name="icon"/>
</tree>
@@ -74,10 +74,10 @@
<field name="type">tree</field>
<field name="inherit_id" ref="project.view_task_tree2" />
<field name="arch" type="xml">
<field name="project_id" position="after">
<field name="timebox_id"/>
<button name="prev_timebox" type="object" icon="gtk-go-back" string="Previous" states="draft,pending,open"/>
<button name="next_timebox" type="object" icon="gtk-go-forward" string="Next" states="draft,pending,open"/>
<field name="remaining_hours" position="after">
<field name="timebox_id" groups="project_gtd.group_project_getting"/>
<button name="prev_timebox" type="object" icon="gtk-go-back" string="Previous" states="draft,pending,open" groups="project_gtd.group_project_getting"/>
<button name="next_timebox" type="object" icon="gtk-go-forward" string="Next" states="draft,pending,open" groups="project_gtd.group_project_getting"/>
</field>
</field>
</record>
@@ -90,8 +90,8 @@
<field name="arch" type="xml">
<field name="progress" position="after">
<newline/>
<field name="context_id" select="2" widget="selection"/>
<field name="timebox_id" select="1"/>
<field name="context_id" select="2" widget="selection" groups="project_gtd.group_project_getting"/>
<field name="timebox_id" select="1" groups="project_gtd.group_project_getting"/>
<newline/>
</field>
</field>
@@ -105,11 +105,11 @@
<tree editable="top" colors="grey:state in ('done',);red:state in ('cancelled');black:state not in ('done','cancelled')" string="Tasks">
<field name="sequence" invisible="1"/>
<field name="name"/>
<field name="user_id"/>
<field name="timebox_id" widget="selection"/>
<button name="prev_timebox" type="object" icon="gtk-go-back" string="Previous" states="draft,pending,open"/>
<button name="next_timebox" type="object" icon="gtk-go-forward" string="Next" states="draft,pending,open"/>
<field name="planned_hours" widget="float_time"/>
<field name="user_id"/>
<field name="timebox_id" widget="selection" groups="project_gtd.group_project_getting"/>
<button name="prev_timebox" type="object" icon="gtk-go-back" string="Previous" states="draft,pending,open" groups="project_gtd.group_project_getting"/>
<button name="next_timebox" type="object" icon="gtk-go-forward" string="Next" states="draft,pending,open" groups="project_gtd.group_project_getting"/>
<field name="state" invisible="1"/>
<button name="%(project.wizard_close_task)d" states="draft,pending,open" string="Done" type="action" icon="gtk-jump-to" help="For changing to done state"/>
<button groups="base.group_extended" name="%(project.wizard_delegate_task)d" states="pending,open" string="Delegate" type="action" icon="gtk-sort-descending"/>
@@ -133,43 +133,5 @@
parent="project.menu_project_management"
action="action_prject_task_tree"/>
<act_window name="My Inbox"
domain="[('user_id', '=', uid),('state','in',('draft','open'))]"
res_model="project.task"
src_model="project.gtd.timebox"
view_type="form"
view_mode="tree,form"
id="act_timebox_tasks_my_inbox"/>
<act_window name="My Open Tasks"
domain="[('user_id', '=', uid),('state','in',('draft','open'))]"
res_model="project.task"
src_model="project.gtd.timebox"
view_type="form"
view_mode="tree,form"
id="act_timebox_tasks_my_open"/>
<act_window name="My Pending Tasks"
domain="[('user_id', '=', uid),('state','=','pending')]"
res_model="project.task"
src_model="project.gtd.timebox"
view_type="form"
view_mode="tree,form"
id="act_timebox_tasks_my_pending"/>
<act_window name="My Deadlines"
domain="[('user_id', '=', uid),('state','in',('draft','open','pending')),('date_deadline','&lt;&gt;',False)]"
res_model="project.task"
src_model="project.gtd.timebox"
view_type="form"
view_mode="tree,form"
id="act_timebox_tasks_my_deadline_open"/>
<act_window name="Timebox Tasks"
domain="[('timebox_id', '=', active_id)]"
res_model="project.task"
src_model="project.gtd.timebox"
view_type="form"
view_mode="tree,form"
id="act_timebox_tasks"/>
</data>
</openerp>
+1
View File
@@ -20,6 +20,7 @@
##############################################################################
import project
import wizard
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
+2 -1
View File
@@ -33,7 +33,8 @@
"demo_xml" : [#"project_demo.xml"
],
"update_xml": [
"project_view.xml",
"project_view.xml",
"project_wizard.xml"
],
'installable': True,
'active': False,
-11
View File
@@ -125,17 +125,6 @@ class task(osv.osv):
_columns = {
'phase_id': fields.many2one('project.phase', 'Project Phase')
}
def _check_dates(self, cr, uid, ids):
leave = self.read(cr, uid, ids[0],['date_start','date_end'])
if leave['date_start'] and leave['date_end']:
if leave['date_start'] > leave['date_end']:
return False
return True
_constraints = [
(_check_dates, 'Error! task start-date must be lower then task end-date.', ['date_start', 'date_end'])
]
task()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
+10 -2
View File
@@ -1,8 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<wizard id="wizard_close_task" menu="False" model="project.task" name="project.task.close" string="Close Task"/>
<!-- <wizard id="wizard_close_task" menu="False" model="project.task" name="project.task.close" string="Close Task"/>
<wizard id="wizard_delegate_task" menu="False" model="project.task" name="project.task.delegate" string="Delegate Task"/>
<wizard id="wizard_attachment_task" model="project.task" name="project.task.attachment" string="All Attachments"/>
<wizard id="wizard_attachment_task" model="project.task" name="project.task.attachment" string="All Attachments"/> -->
<wizard id="wizard_compute_phase" model="project.phase" name="wizard.compute.phases" string="Compute Phase Scheduling"/>
<menuitem icon="terp-project" id="menu_main" name="Project Management"/>
<menuitem
action="wizard_compute_phase"
id="menu_wizard_compute_phase"
parent="menu_main"
type="wizard"
sequence="1"/>
</data>
</openerp>
@@ -0,0 +1,25 @@
# -*- 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 compute_phases_date
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
@@ -0,0 +1,196 @@
# -*- 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
from tools.translate import _
import datetime
from resource.faces import *
compute_form = """<?xml version="1.0" ?>
<form string="Compute Scheduling of Tasks">
<field name="project_id" colspan="4"/>
</form>"""
compute_fields = {
'project_id': {'string':'Project', 'type':'many2one', 'relation': 'project.project', 'required':'True'},
}
def timeformat_convert(cr, uid, time_string, context={}):
strg = str(time_string)[-2:]
last_strg = str(time_string)[0:-2]
if strg != '.0':
if '.' not in strg:
strg = '.' + strg
last_strg = str(time_string)[:-3]
new_strg = round(float(strg) / 0.016666667)
converted_strg = last_strg + ':' + str(new_strg)[:-2]
else:
converted_strg = last_strg + ':00'
return converted_strg
class wizard_compute_phases(wizard.interface):
def _compute_date(self, cr, uid, data, context):
project_id = data['form']['project_id']
project = pooler.get_pool(cr.dbname).get('project.project').browse(cr,uid,project_id)
phase_ids = pooler.get_pool(cr.dbname).get('project.phase').search(cr,uid,[('project_id','=',project_id)])
if phase_ids:
phase_obj = pooler.get_pool(cr.dbname).get('project.phase').browse(cr,uid,phase_ids)
phase_1 = phase_obj[0]
if phase_1.date_start:
date_dt = datetime.datetime.strptime(phase_1.date_start,"%Y-%m-%d %H:%M:%S")
else:
date_dt = datetime.datetime.strptime(project.date_start,"%Y-%m-%d")
dt = datetime.datetime.strftime(date_dt,"%Y-%m-%d %H:%M")
for i in range(len(phase_obj)):
final_lst = []
leaves = []
# if phase_obj[i].user_id.id:
# resource_id = pooler.get_pool(cr.dbname).get('resource.resource').search(cr,uid,[('user_id','=',task_obj[i].user_id.id)])
# resource_obj = pooler.get_pool(cr.dbname).get('resource.resource').browse(cr,uid,resource_id)[0]
if project.resource_calendar_id:
non_working = ""
time_range = "8:00-8:00"
wk = {"0":"mon","1":"tue","2":"wed","3":"thu","4":"fri"}
wk_days = {}
wk_time = {}
tlist = []
hours = []
hr = 0
week_ids = pooler.get_pool(cr.dbname).get('resource.calendar.week').search(cr,uid,[('calendar_id','=',project.resource_calendar_id.id)])
week_obj = pooler.get_pool(cr.dbname).get('resource.calendar.week').read(cr,uid,week_ids,['dayofweek','hour_from','hour_to'])
for week in week_obj:
res_str = ""
if wk.has_key(week['dayofweek']):
day = wk[week['dayofweek']]
wk_days[week['dayofweek']] = wk[week['dayofweek']]
hour_from_str = timeformat_convert(cr,uid,week['hour_from'])
hour_to_str = timeformat_convert(cr,uid,week['hour_to'])
hours.append(week['hour_from'])
hours.append(week['hour_to'])
res_str = hour_from_str + '-' + hour_to_str
tlist.append((day,res_str))
for item in tlist:
if wk_time.has_key(item[0]):
wk_time[item[0]].append(item[1])
else:
wk_time[item[0]] = [item[0]]
wk_time[item[0]].append(item[1])
for k,v in wk_time.items():
final_lst.append(tuple(v))
for k,v in wk_days.items():
if wk.has_key(k):
wk.pop(k)
for v in wk.itervalues():
non_working += v + ','
if non_working:
final_lst.append((non_working[:-1],time_range))
leave_ids = pooler.get_pool(cr.dbname).get('resource.calendar.leaves').search(cr,uid,[('calendar_id','=',project.resource_calendar_id.id)])
if leave_ids:
res_leaves = pooler.get_pool(cr.dbname).get('resource.calendar.leaves').read(cr,uid,leave_ids,['date_from','date_to'])
for leave in range(len(res_leaves)):
dt_start = datetime.datetime.strptime(res_leaves[leave]['date_from'],'%Y-%m-%d %H:%M:%S')
dt_end = datetime.datetime.strptime(res_leaves[leave]['date_to'],'%Y-%m-%d %H:%M:%S')
no = dt_end - dt_start
leave_days = no.days + 1
[leaves.append((dt_start + datetime.timedelta(days=x)).strftime('%Y-%m-%d')) for x in range(int(leave_days))]
leaves.sort()
for hour in range(0,4):
if hour%2 ==0:
hr += float(hours[hour+1]) - float(hours[hour])
man_days = str(phase_obj[i].duration * hr)[:-2] + 'H'
if i == 0:
new_dt = dt
else:
data = pooler.get_pool(cr.dbname).get('project.phase').read(cr,uid,[phase_obj[i-1].id],['date_end'])[0]
if phase_obj[i].constraint_date_start and data['date_end'] < phase_obj[i].constraint_date_start:
new_dt = phase_obj[i].constraint_date_start[0:16]
else:
new_dt = data['date_end'][0:16]
class user(Resource):
pass
def Project_1():
resource = user
title = project.name
start = new_dt
effort = man_days
if final_lst or leaves:
working_days = final_lst
vacation = tuple(leaves)
def phase1():
start = new_dt
effort = man_days
title = phase_obj[i].name
project_1 = BalancedProject(Project_1)
start_date = project_1.calendar.WorkingDate(project_1.phase1.start).to_datetime()
e_date = project_1.calendar.WorkingDate(project_1.phase1.end).to_datetime()
if phase_obj[i].constraint_date_end and str(e_date) > phase_obj[i].constraint_date_end:
end_date = phase_obj[i].constraint_date_end
else:
end_date = e_date
pooler.get_pool(cr.dbname).get('project.phase').write(cr,uid,[phase_obj[i].id],{'date_start':start_date,'date_end':end_date})
return {}
states = {
'init': {
'actions': [],
'result': {'type':'form', 'arch':compute_form, 'fields':compute_fields, 'state':[
('end', 'Cancel'),
('ok', 'Ok')
]},
},
'ok': {
'actions': [_compute_date],
'result': {'type':'state', 'state':'end'},
}
}
wizard_compute_phases('wizard.compute.phases')
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
+4 -4
View File
@@ -7,13 +7,13 @@ msgstr ""
"Project-Id-Version: OpenERP Server 5.0.0\n"
"Report-Msgid-Bugs-To: support@openerp.com\n"
"POT-Creation-Date: 2009-08-28 16:01+0000\n"
"PO-Revision-Date: 2009-09-08 16:12+0000\n"
"Last-Translator: Marius Marolla <mariusmarolla@areablu.net>\n"
"PO-Revision-Date: 2010-01-31 08:53+0000\n"
"Last-Translator: Daniel Eric Maier <Unknown>\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2009-12-16 05:06+0000\n"
"X-Launchpad-Export-Date: 2010-02-01 04:45+0000\n"
"X-Generator: Launchpad (build Unknown)\n"
#. module: report_analytic
@@ -62,7 +62,7 @@ msgstr "Nome di modulo non valido nella definizione dell'azione."
#. module: report_analytic
#: model:ir.module.module,shortdesc:report_analytic.module_meta_information
msgid "Analytic Account Reporting"
msgstr ""
msgstr "Relatório de Conta Analítica"
#. module: report_analytic
#: field:report.analytic.account.close,partner_id:0
+14 -14
View File
@@ -7,13 +7,13 @@ msgstr ""
"Project-Id-Version: OpenERP Server 5.0.4\n"
"Report-Msgid-Bugs-To: support@openerp.com\n"
"POT-Creation-Date: 2009-08-28 16:01+0000\n"
"PO-Revision-Date: 2009-02-09 21:36+0000\n"
"Last-Translator: LucaSub <subluca@gmail.com>\n"
"PO-Revision-Date: 2010-01-29 14:12+0000\n"
"Last-Translator: Carlo <Unknown>\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2009-12-16 05:09+0000\n"
"X-Launchpad-Export-Date: 2010-01-30 04:39+0000\n"
"X-Generator: Launchpad (build Unknown)\n"
#. module: report_crm
@@ -30,12 +30,12 @@ msgstr "Probabilità Media"
#. module: report_crm
#: field:report.crm.case.service.dashboard,planned_revenue:0
msgid "Planned Revenue"
msgstr ""
msgstr "Entrata Pianificata"
#. module: report_crm
#: constraint:ir.actions.act_window:0
msgid "Invalid model name in the action definition."
msgstr ""
msgstr "Nome del modulo non valido nella definizione dell'azione."
#. module: report_crm
#: model:ir.model,name:report_crm.model_report_crm_case_categ
@@ -52,7 +52,7 @@ msgstr "Sezione"
#. module: report_crm
#: field:report.crm.case.service.dashboard,user_id:0
msgid "Responsible"
msgstr ""
msgstr "Responsabile"
#. module: report_crm
#: field:report.crm.case.section,perc_done:0
@@ -62,7 +62,7 @@ msgstr ""
#. module: report_crm
#: field:report.crm.case.service.dashboard,planned_cost:0
msgid "Planned Costs"
msgstr ""
msgstr "Costi Pianificati"
#. module: report_crm
#: field:report.crm.case.section,nbr_cases:0
@@ -148,7 +148,7 @@ msgstr "Stato"
#. module: report_crm
#: field:report.crm.case.service.dashboard,name:0
msgid "Description"
msgstr ""
msgstr "Descrizione"
#. module: report_crm
#: model:ir.actions.act_window,name:report_crm.action_report_crm_case_user_tree_month
@@ -169,7 +169,7 @@ msgstr "Reportistica"
#. module: report_crm
#: view:report.crm.case.service.dashboard:0
msgid "Planned costs"
msgstr ""
msgstr "Costi Pianificati"
#. module: report_crm
#: model:ir.actions.act_window,name:report_crm.action_view_closed_crm_case_dashboard
@@ -195,7 +195,7 @@ msgstr "Cai Mensili per Sezione"
#. module: report_crm
#: field:report.crm.case.service.dashboard,date:0
msgid "Date"
msgstr ""
msgstr "Data"
#. module: report_crm
#: model:ir.actions.act_window,name:report_crm.action_report_crm_case_user_tree_month_my
@@ -251,7 +251,7 @@ msgstr ""
#. module: report_crm
#: field:report.crm.case.service.dashboard,priority:0
msgid "Priority"
msgstr ""
msgstr "Priorità"
#. module: report_crm
#: field:report.crm.case.section,perc_cancel:0
@@ -298,12 +298,12 @@ msgstr "Annullato"
#. module: report_crm
#: field:report.crm.case.service.dashboard,create_date:0
msgid "Create Date"
msgstr ""
msgstr "Data Creazione"
#. module: report_crm
#: field:report.crm.case.service.dashboard,date_deadline:0
msgid "Deadline"
msgstr ""
msgstr "Scadenza"
#. module: report_crm
#: model:ir.model,name:report_crm.model_report_crm_case_user
@@ -313,7 +313,7 @@ msgstr "Casi per Utente e Sezione"
#. module: report_crm
#: field:report.crm.case.service.dashboard,partner_id:0
msgid "Partner"
msgstr ""
msgstr "Partner"
#. module: report_crm
#: model:ir.module.module,description:report_crm.module_meta_information
+30
View File
@@ -0,0 +1,30 @@
# -*- 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 pcalendar import Calendar, WorkingDate, StartDate, EndDate, Minutes
from task import Project, BalancedProject, AdjustedProject, Task, \
STRICT, SLOPPY, SMART, Multi, YearlyMax, WeeklyMax, MonthlyMax, \
DailyMax, VariableLoad
from resource import Resource
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
+79
View File
@@ -0,0 +1,79 @@
#@+leo-ver=4
#@+node:@file observer.py
#@@language python
#@<< Copyright >>
#@+node:<< Copyright >>
############################################################################
# Copyright (C) 2005, 2006, 2007, 2008 by Reithinger GmbH
# mreithinger@web.de
#
# This file is part of faces.
#
# faces is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# faces 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the
# Free Software Foundation, Inc.,
# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
############################################################################
#@-node:<< Copyright >>
#@nl
"""
This module contains the base class for all observer objects
"""
#@<< Imports >>
#@+node:<< Imports >>
#@-node:<< Imports >>
#@nl
_is_source_ = True
#@+others
#@+node:class Observer
class Observer(object):
"""
Base Class for all charts and reports.
@var visible: Specifies if the observer is visible
at the navigation bar inside the gui.
@var link_view: syncronizes the marked objects in all views.
"""
#@ << declarations >>
#@+node:<< declarations >>
__type_name__ = None
__type_image__ = None
visible = True
link_view = True
__attrib_completions__ = { "visible" : 'visible = False',
"link_view" : "link_view = False" }
#@-node:<< declarations >>
#@nl
#@ @+others
#@+node:register_editors
def register_editors(cls, registry):
pass
register_editors = classmethod(register_editors)
#@-node:register_editors
#@-others
#@-node:class Observer
#@-others
factories = { }
clear_cache_funcs = {}
#@-node:@file observer.py
#@-leo
+960
View File
@@ -0,0 +1,960 @@
#@+leo-ver=4
#@+node:@file pcalendar.py
#@@language python
#@<< Copyright >>
#@+node:<< Copyright >>
############################################################################
# Copyright (C) 2005, 2006, 2007, 2008 by Reithinger GmbH
# mreithinger@web.de
#
# This file is part of faces.
#
# faces is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# faces 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the
# Free Software Foundation, Inc.,
# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
############################################################################
#@-node:<< Copyright >>
#@nl
"""
This module contains all classes and functions for the project plan calendar
"""
#@<< Imports >>
#@+node:<< Imports >>
from string import *
import datetime
import time
import re
import locale
import bisect
import sys
TIME_RANGE_PATTERN = re.compile("(\\d+):(\\d+)\\s*-\\s*(\\d+):(\\d+)")
TIME_DELTA_PATTERN = re.compile("([-+]?\\d+(\\.\\d+)?)([dwmyMH])")
DEFAULT_MINIMUM_TIME_UNIT = 15
DEFAULT_WORKING_DAYS_PER_WEEK = 5
DEFAULT_WORKING_DAYS_PER_MONTH = 20
DEFAULT_WORKING_DAYS_PER_YEAR = 200
DEFAULT_WORKING_HOURS_PER_DAY = 8
DEFAULT_WORKING_TIMES = ( (8 * 60, 12 * 60 ),
(13 * 60, 17 * 60 ) )
DEFAULT_WORKING_DAYS = { 0 : DEFAULT_WORKING_TIMES,
1 : DEFAULT_WORKING_TIMES,
2 : DEFAULT_WORKING_TIMES,
3 : DEFAULT_WORKING_TIMES,
4 : DEFAULT_WORKING_TIMES,
5 : (),
6 : () }
#@-node:<< Imports >>
#@nl
#@+others
#@+node:to_time_range
def to_time_range(src):
"""
converts a string to a timerange, i.e
(from, to)
from, to are ints, specifing the minutes since midnight
"""
if not src: return ()
mo = TIME_RANGE_PATTERN.match(src)
if not mo:
raise ValueError("%s is no time range" % src)
from_time = int(mo.group(1)) * 60 + int(mo.group(2))
to_time = int(mo.group(3)) * 60 + int(mo.group(4))
return from_time, to_time
#@-node:to_time_range
#@+node:to_datetime
def to_datetime(src):
"""
a tolerant conversion function to convert different strings
to a datetime.dateime
"""
#to get the original value for wrappers
new = getattr(src, "_value", src)
while new is not src:
src = new
new = getattr(src, "_value", src)
if isinstance(src, _WorkingDateBase):
src = src.to_datetime()
if isinstance(src, datetime.datetime):
return src
src = str(src)
formats = [ "%x %H:%M",
"%x",
"%Y-%m-%d %H:%M",
"%y-%m-%d %H:%M",
"%d.%m.%Y %H:%M",
"%d.%m.%y %H:%M",
"%Y%m%d %H:%M",
"%d/%m/%y %H:%M",
"%d/%m/%Y %H:%M",
"%d/%m/%Y",
"%d/%m/%y",
"%Y-%m-%d",
"%y-%m-%d",
"%d.%m.%Y",
"%d.%m.%y",
"%Y%m%d" ]
for f in formats:
try:
conv = time.strptime(src, f)
return datetime.datetime(*conv[0:-3])
except Exception, e:
pass
raise TypeError("'%s' (%s) is not a datetime" % (src, str(type(src))))
#@-node:
#@+node:_to_days
def _to_days(src):
"""
converts a string of the day abreviations mon, tue, wed,
thu, fri, sat, sun to a dir with correct weekday indices.
For Example
convert_to_days('mon, tue, thu') results in
{ 0:1, 1:1, 3:1 }
"""
tokens = src.split(",")
result = { }
for t in tokens:
try:
index = { "mon" : 0,
"tue" : 1,
"wed" : 2,
"thu" : 3,
"fri" : 4,
"sat" : 5,
"sun" : 6 } [ lower(t.strip()) ]
result[index] = 1
except:
raise ValueError("%s is not a day" % (t))
return result
#@-node:_to_days
#@+node:_add_to_time_spans
def _add_to_time_spans(src, to_add, is_free):
if not isinstance(to_add, (tuple, list)):
to_add = (to_add,)
tmp = []
for start, end, f in src:
tmp.append((start, True, f))
tmp.append((end, False, f))
for v in to_add:
if isinstance(v, (tuple, list)):
start = to_datetime(v[0])
end = to_datetime(v[1])
else:
start = to_datetime(v)
end = start.replace(hour=0, minute=0) + datetime.timedelta(1)
tmp.append((start, start <= end, is_free))
tmp.append((end, start > end, is_free))
tmp.sort()
# 0: date
# 1: is_start
# 2: is_free
sequence = []
free_count = 0
work_count = 0
last = None
for date, is_start, is_free in tmp:
if is_start:
if is_free:
if not free_count and not work_count:
last = date
free_count += 1
else:
if not work_count:
if free_count: sequence.append((last, date, True))
last = date
work_count += 1
else:
if is_free:
assert(free_count > 0)
free_count -= 1
if not free_count and not work_count:
sequence.append((last, date, True))
else:
assert(work_count > 0)
work_count -= 1
if not work_count: sequence.append((last, date, False))
if free_count: last = date
return tuple(sequence)
#@-node:_add_to_time_spans
#@+node:to_timedelta
def to_timedelta(src, cal=None, is_duration=False):
"""
converts a string to a datetime.timedelta. If cal is specified
it will be used for getting the working times. if is_duration=True
working times will not be considered. Valid units are
d for Days
w for Weeks
m for Months
y for Years
H for Hours
M for Minutes
"""
cal = cal or _default_calendar
if isinstance(src, datetime.timedelta):
return datetime.timedelta(src.days, seconds=src.seconds, calendar=cal)
if isinstance(src, (long, int, float)):
src = "%sM" % str(src)
if not isinstance(src, basestring):
raise ValueError("%s is not a duration" % (repr(src)))
src = src.strip()
if is_duration:
d_p_w = 7
d_p_m = 30
d_p_y = 360
d_w_h = 24
else:
d_p_w = cal.working_days_per_week
d_p_m = cal.working_days_per_month
d_p_y = cal.working_days_per_year
d_w_h = cal.working_hours_per_day
def convert_minutes(minutes):
minutes = int(minutes)
hours = minutes / 60
minutes = minutes % 60
days = hours / d_w_h
hours = hours % d_w_h
return [ days, 0, 0, 0, minutes, hours ]
def convert_days(value):
days = int(value)
value -= days
value *= d_w_h
hours = int(value)
value -= hours
value *= 60
minutes = round(value)
return [ days, 0, 0, 0, minutes, hours ]
sum_args = [ 0, 0, 0, 0, 0, 0 ]
split = src.split(" ")
for s in split:
mo = TIME_DELTA_PATTERN.match(s)
if not mo:
raise ValueError(src +
" is not a valid duration: valid"
" units are: d w m y M H")
unit = mo.group(3)
val = float(mo.group(1))
if unit == 'd':
args = convert_days(val)
elif unit == 'w':
args = convert_days(val * d_p_w)
elif unit == 'm':
args = convert_days(val * d_p_m)
elif unit == 'y':
args = convert_days(val * d_p_y)
elif unit == 'M':
args = convert_minutes(val)
elif unit == 'H':
args = convert_minutes(val * 60)
sum_args = [ a + b for a, b in zip(sum_args, args) ]
sum_args = tuple(sum_args)
return datetime.timedelta(*sum_args)
#@-node:to_timedelta
#@+node:timedelta_to_str
def timedelta_to_str(delta, format, cal=None, is_duration=False):
cal = cal or _default_calendar
if is_duration:
d_p_w = 7
d_p_m = 30
d_p_y = 365
d_w_h = 24
else:
d_p_w = cal.working_days_per_week
d_p_m = cal.working_days_per_month
d_p_y = cal.working_days_per_year
d_w_h = cal.working_hours_per_day
has_years = format.find("%y") > -1
has_minutes = format.find("%M") > -1
has_hours = format.find("%H") > -1 or has_minutes
has_days = format.find("%d") > -1
has_weeks = format.find("%w") > -1
has_months = format.find("%m") > -1
result = format
days = delta.days
d_r = (days, format)
minutes = delta.seconds / 60
def rebase(d_r, cond1, cond2, letter, divisor):
#rebase the days
if not cond1: return d_r
days, result = d_r
if cond2:
val = days / divisor
if not val:
result = re.sub("{[^{]*?%" + letter + "[^}]*?}", "", result)
result = result.replace("%" + letter, str(val))
days %= divisor
else:
result = result.replace("%" + letter,
locale.format("%.2f",
(float(days) / divisor)))
return (days, result)
d_r = rebase(d_r, has_years, has_months or has_weeks or has_days, "y", d_p_y)
d_r = rebase(d_r, has_months, has_weeks or has_days, "m", d_p_m)
d_r = rebase(d_r, has_weeks, has_days, "w", d_p_w)
days, result = d_r
if not has_days:
minutes += days * d_w_h * 60
days = 0
if has_hours:
if not days:
result = re.sub("{[^{]*?%d[^}]*?}", "", result)
result = result.replace("%d", str(days))
else:
result = result.replace("%d",
"%.2f" % (days + float(minutes)
/ (d_w_h * 60)))
if has_hours:
if has_minutes:
val = minutes / 60
if not val:
result = re.sub("{[^{]*?%H[^}]*?}", "", result)
result = result.replace("%H", str(val))
minutes %= 60
else:
result = result.replace("%H", "%.2f" % (float(minutes) / 60))
if not minutes:
result = re.sub("{[^{]*?%M[^}]*?}", "", result)
result = result.replace("%M", str(minutes))
result = result.replace("{", "")
result = result.replace("}", "")
return result.strip()
#@-node:timedelta_to_str
#@+node:strftime
def strftime(dt, format):
"""
an extended version of strftime, that introduces some new
directives:
%IW iso week number
%IY iso year
%IB full month name appropriate to iso week
%ib abbreviated month name appropriate to iso week
%im month as decimal number appropriate to iso week
"""
iso = dt.isocalendar()
if iso[0] != dt.year:
iso_date = dt.replace(day=1, month=1)
format = format \
.replace("%IB", iso_date.strftime("%B"))\
.replace("%ib", iso_date.strftime("%b"))\
.replace("%im", iso_date.strftime("%m"))
else:
format = format \
.replace("%IB", "%B")\
.replace("%ib", "%b")\
.replace("%im", "%m")
format = format \
.replace("%IW", str(iso[1]))\
.replace("%IY", str(iso[0]))\
return dt.strftime(format)
#@-node:strftime
#@+node:union
def union(*calendars):
"""
returns a calendar that unifies all working times
"""
#@ << check arguments >>
#@+node:<< check arguments >>
if len(calendars) == 1:
calendars = calendars[0]
#@nonl
#@-node:<< check arguments >>
#@nl
#@ << intersect vacations >>
#@+node:<< intersect vacations >>
free_time = []
for c in calendars:
for start, end, is_free in c.time_spans:
if is_free:
free_time.append((start, False))
free_time.append((end, True))
count = len(calendars)
open = 0
time_spans = []
free_time.sort()
for date, is_end in free_time:
if is_end:
if open == count:
time_spans.append((start, date, True))
open -= 1
else:
open += 1
start = date
#@-node:<< intersect vacations >>
#@nl
#@ << unify extra worktime >>
#@+node:<< unify extra worktime >>
for c in calendars:
for start, end, is_free in c.time_spans:
if not is_free:
time_spans = _add_to_time_spans(time_spans, start, end)
#@nonl
#@-node:<< unify extra worktime >>
#@nl
#@ << unify working times >>
#@+node:<< unify working times >>
working_times = {}
for d in range(0, 7):
times = []
for c in calendars:
for start, end in c.working_times.get(d, []):
times.append((start, False))
times.append((end, True))
times.sort()
open = 0
ti = []
start = None
for time, is_end in times:
if not is_end:
if not start: start = time
open += 1
else:
open -= 1
if not open:
ti.append((start, time))
start = None
if ti:
working_times[d] = ti
#@-node:<< unify working times >>
#@nl
#@ << create result calendar >>
#@+node:<< create result calendar >>
result = Calendar()
result.working_times = working_times
result.time_spans = time_spans
result._recalc_working_time()
result._build_mapping()
#@nonl
#@-node:<< create result calendar >>
#@nl
return result
#@nonl
#@-node:union
#@+node:class _CalendarItem
class _CalendarItem(int):
#@ << class _CalendarItem declarations >>
#@+node:<< class _CalendarItem declarations >>
__slots__ = ()
calender = None
#@-node:<< class _CalendarItem declarations >>
#@nl
#@ @+others
#@+node:__new__
def __new__(cls, val):
try:
return int.__new__(cls, val)
except OverflowError:
return int.__new__(cls, sys.maxint)
#@-node:__new__
#@+node:round
def round(self, round_up=True):
m_t_u = self.calendar.minimum_time_unit
minutes = int(self)
base = (minutes / m_t_u) * m_t_u
minutes %= m_t_u
round_up = round_up and minutes > 0 or minutes > m_t_u / 2
if round_up: base += m_t_u
return self.__class__(base)
#@-node:round
#@-others
#@-node:class _CalendarItem
#@+node:class _Minutes
class _Minutes(_CalendarItem):
#@ << class _Minutes declarations >>
#@+node:<< class _Minutes declarations >>
__slots__ = ()
STR_FORMAT = "{%dd}{ %HH}{ %MM}"
#@-node:<< class _Minutes declarations >>
#@nl
#@ @+others
#@+node:__new__
def __new__(cls, src=0, is_duration=False):
"""
converts a timedelta in working minutes.
"""
if isinstance(src, cls) or type(src) is int:
return _CalendarItem.__new__(cls, src)
cal = cls.calendar
if not isinstance(src, datetime.timedelta):
src = to_timedelta(src, cal, is_duration)
d_w_h = is_duration and 24 or cal.working_hours_per_day
src = src.days * d_w_h * 60 + src.seconds / 60
return _CalendarItem.__new__(cls, src)
#@-node:__new__
#@+node:__cmp__
def __cmp__(self, other):
return cmp(int(self), int(self.__class__(other)))
#@-node:__cmp__
#@+node:__add__
def __add__(self, other):
try:
return self.__class__(int(self) + int(self.__class__(other)))
except:
return NotImplemented
#@-node:__add__
#@+node:__sub__
def __sub__(self, other):
try:
return self.__class__(int(self) - int(self.__class__(other)))
except:
return NotImplemented
#@-node:__sub__
#@+node:to_timedelta
def to_timedelta(self, is_duration=False):
d_w_h = is_duration and 24 or self.calendar.working_hours_per_day
minutes = int(self)
hours = minutes / 60
minutes = minutes % 60
days = hours / d_w_h
hours = hours % d_w_h
return datetime.timedelta(days, hours=hours, minutes=minutes)
#@nonl
#@-node:to_timedelta
#@+node:strftime
def strftime(self, format=None, is_duration=False):
td = self.to_timedelta(is_duration)
return timedelta_to_str(td, format or self.STR_FORMAT,
self.calendar, is_duration)
#@nonl
#@-node:strftime
#@-others
#@-node:class _Minutes
#@+node:class _WorkingDateBase
class _WorkingDateBase(_CalendarItem):
"""
A daytetime which has only valid values within the
workingtimes of a specific calendar
"""
#@ << class _WorkingDateBase declarations >>
#@+node:<< class _WorkingDateBase declarations >>
timetuple = True
STR_FORMAT = "%x %H:%M"
_minutes = _Minutes
__slots__ = ()
#@-node:<< class _WorkingDateBase declarations >>
#@nl
#@ @+others
#@+node:__new__
def __new__(cls, src):
#cls.__bases__[0] is the base of
#the calendar specific StartDate and EndDate
if isinstance(src, cls.__bases__[0]) or type(src) in (int, float):
return _CalendarItem.__new__(cls, src)
src = cls.calendar.from_datetime(to_datetime(src))
return _CalendarItem.__new__(cls, src)
#@-node:__new__
#@+node:__repr__
def __repr__(self):
return self.strftime()
#@-node:__repr__
#@+node:to_datetime
def to_datetime(self):
return self.to_starttime()
#@-node:to_datetime
#@+node:to_starttime
def to_starttime(self):
return self.calendar.to_starttime(self)
#@-node:to_starttime
#@+node:to_endtime
def to_endtime(self):
return self.calendar.to_endtime(self)
#@-node:to_endtime
#@+node:__cmp__
def __cmp__(self, other):
return cmp(int(self), int(self.__class__(other)))
#@-node:__cmp__
#@+node:__add__
def __add__(self, other):
try:
return self.__class__(int(self) + int(self._minutes(other)))
except ValueError, e:
raise e
except:
return NotImplemented
#@-node:__add__
#@+node:__sub__
def __sub__(self, other):
if isinstance(other, (datetime.timedelta, str, _Minutes)):
try:
other = self._minutes(other)
except:
pass
if isinstance(other, self._minutes):
return self.__class__(int(self) - int(other))
try:
return self._minutes(int(self) - int(self.__class__(other)))
except:
return NotImplemented
#@-node:__sub__
#@+node:strftime
def strftime(self, format=None):
return strftime(self.to_datetime(), format or self.STR_FORMAT)
#@-node:strftime
#@-others
#@-node:class _WorkingDateBase
#@+node:class Calendar
class Calendar(object):
"""
A calendar to specify working times and vacations.
The calendars epoch start at 1.1.1979
"""
#@ << declarations >>
#@+node:<< declarations >>
# january the first must be a monday
EPOCH = datetime.datetime(1979, 1, 1)
minimum_time_unit = DEFAULT_MINIMUM_TIME_UNIT
working_days_per_week = DEFAULT_WORKING_DAYS_PER_WEEK
working_days_per_month = DEFAULT_WORKING_DAYS_PER_MONTH
working_days_per_year = DEFAULT_WORKING_DAYS_PER_YEAR
working_hours_per_day = DEFAULT_WORKING_HOURS_PER_DAY
now = EPOCH
#@-node:<< declarations >>
#@nl
#@ @+others
#@+node:__init__
def __init__(self):
self.time_spans = ()
self._dt_num_can = ()
self._num_dt_can = ()
self.working_times = { }
self._recalc_working_time()
self._make_classes()
#@-node:__init__
#@+node:__or__
def __or__(self, other):
if isinstance(other, Calendar):
return union(self, other)
return NotImplemented
#@nonl
#@-node:__or__
#@+node:clone
def clone(self):
result = Calendar()
result.working_times = self.working_times.copy()
result.time_spans = self.time_spans
result._recalc_working_time()
result._build_mapping()
return result
#@nonl
#@-node:clone
#@+node:set_working_days
def set_working_days(self, day_range, trange, *further_tranges):
"""
Sets the working days of an calendar
day_range is a string of day abbreviations like 'mon, tue'
trange and further_tranges is a time range string like
'8:00-10:00'
"""
time_ranges = [ trange ] + list(further_tranges)
time_ranges = filter(bool, map(to_time_range, time_ranges))
days = _to_days(day_range)
for k in days.keys():
self.working_times[k] = time_ranges
self._recalc_working_time()
self._build_mapping()
#@-node:set_working_days
#@+node:set_vacation
def set_vacation(self, value):
"""
Sets vacation time.
value is either a datetime literal or
a sequence of items that can be
a datetime literals and or pair of datetime literals
"""
self.time_spans = _add_to_time_spans(self.time_spans, value, True)
self._build_mapping()
#@-node:set_vacation
#@+node:set_extra_work
def set_extra_work(self, value):
"""
Sets extra working time
value is either a datetime literal or
a sequence of items that can be
a datetime literals and or pair of datetime literals
"""
self.time_spans = _add_to_time_spans(self.time_spans, value, False)
self._build_mapping()
#@-node:set_extra_work
#@+node:from_datetime
def from_datetime(self, value):
assert(isinstance(value, datetime.datetime))
delta = value - self.EPOCH
days = delta.days
minutes = delta.seconds / 60
# calculate the weektime
weeks = days / 7
wtime = self.week_time * weeks
# calculate the daytime
days %= 7
dtime = sum(self.day_times[:days])
# calculate the minute time
slots = self.working_times.get(days, DEFAULT_WORKING_DAYS[days])
mtime = 0
for start, end in slots:
if minutes > end:
mtime += end - start
else:
if minutes > start:
mtime += minutes - start
break
result = wtime + dtime + mtime
# map exceptional timespans
dt_num_can = self._dt_num_can
pos = bisect.bisect(dt_num_can, (value,)) - 1
if pos >= 0:
start, end, nstart, nend, cend = dt_num_can[pos]
if value < end:
if nstart < nend:
delta = value - start
delta = delta.days * 24 * 60 + delta.seconds / 60
result = nstart + delta
else:
result = nstart
else:
result += (nend - cend) # == (result - cend) + nend
return result
#@-node:from_datetime
#@+node:split_time
def split_time(self, value):
#map exceptional timespans
num_dt_can = self._num_dt_can
pos = bisect.bisect(num_dt_can, (value, sys.maxint)) - 1
if pos >= 0:
nstart, nend, start, end, cend = num_dt_can[pos]
if value < nend:
value = start + datetime.timedelta(minutes=value - nstart)
delta = value - self.EPOCH
return delta.days / 7, delta.days % 7, delta.seconds / 60, -1
else:
value += (cend - nend) # (value - nend + cend)
#calculate the weeks since the epoch
weeks = value / self.week_time
value %= self.week_time
#calculate the remaining days
days = 0
for day_time in self.day_times:
if value < day_time: break
value -= day_time
days += 1
#calculate the remaining minutes
minutes = 0
slots = self.working_times.get(days, DEFAULT_WORKING_DAYS[days])
index = 0
for start, end in slots:
delta = end - start
if delta > value:
minutes = start + value
break
else:
value -= delta
index += 1
return weeks, days, minutes, index
#@-node:split_time
#@+node:to_starttime
def to_starttime(self, value):
weeks, days, minutes, index = self.split_time(value)
return self.EPOCH + datetime.timedelta(weeks=weeks,
days=days,
minutes=minutes)
#@-node:to_starttime
#@+node:to_endtime
def to_endtime(self, value):
return self.to_starttime(value - 1) + datetime.timedelta(minutes=1)
#@-node:to_endtime
#@+node:get_working_times
def get_working_times(self, day):
return self.working_times.get(day, DEFAULT_WORKING_DAYS[day])
#@-node:get_working_times
#@+node:_build_mapping
def _build_mapping(self):
self._dt_num_can = self._num_dt_can = ()
dt_num_can = []
num_dt_can = []
delta = self.Minutes()
for start, end, is_free in self.time_spans:
cstart = self.StartDate(start)
cend = self.EndDate(end)
nstart = cstart + delta
if not is_free:
d = end - start
d = d.days * 24 * 60 + d.seconds / 60
nend = nstart + d
else:
nend = nstart
delta += (nend - nstart) - (cend - cstart)
dt_num_can.append((start, end, nstart, nend, cend))
num_dt_can.append((nstart, nend, start, end, cend))
self._dt_num_can = tuple(dt_num_can)
self._num_dt_can = tuple(num_dt_can)
#@-node:_build_mapping
#@+node:_recalc_working_time
def _recalc_working_time(self):
def slot_sum_time(day):
slots = self.working_times.get(day, DEFAULT_WORKING_DAYS[day])
return sum(map(lambda slot: slot[1] - slot[0], slots))
self.day_times = map(slot_sum_time, range(0, 7))
self.week_time = sum(self.day_times)
#@-node:_recalc_working_time
#@+node:_make_classes
def _make_classes(self):
#ensure that the clases are instance specific
class minutes(_Minutes):
calendar = self
__slots__ = ()
class db(_WorkingDateBase):
calendar = self
_minutes = minutes
__slots__ = ()
class wdt(db): __slots__ = ()
class edt(db):
__slots__ = ()
def to_datetime(self):
return self.to_endtime()
self.Minutes, self.StartDate, self.EndDate = minutes, wdt, edt
self.WorkingDate = self.StartDate
#@-node:_make_classes
#@-others
_default_calendar = Calendar()
WorkingDate = _default_calendar.WorkingDate
StartDate = _default_calendar.StartDate
EndDate = _default_calendar.EndDate
Minutes = _default_calendar.Minutes
#@-node:class Calendar
#@-others
if __name__ == '__main__':
cal = Calendar()
start = EndDate("10.1.2005")
print "start", start.strftime(), type(start)
delay = Minutes("4H")
print "delay", delay, delay.strftime()
print "Start", cal.StartDate is StartDate
print "base", cal.StartDate.__bases__[0] == StartDate.__bases__[0]
print "type", type(start)
print "convert start"
start2 = cal.StartDate(start)
print "convert end"
start3 = cal.StartDate("10.1.2005")
print "start2", start2.strftime(), type(start2)
#@-node:@file pcalendar.py
#@-leo
+54
View File
@@ -0,0 +1,54 @@
############################################################################
# Copyright (C) 2005 by Reithinger GmbH
# mreithinger@web.de
#
# This file is part of faces.
#
# faces is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# faces 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the
# Free Software Foundation, Inc.,
# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
############################################################################
import gettext
import os.path
import locale
def _get_translation():
try:
return gettext.translation("faces")
except:
try:
if sys.frozen:
path = os.path.dirname(sys.argv[0])
path = os.path.join(path, "resources", "faces", "locale")
else:
path = os.path.split(__file__)[0]
path = os.path.join(path, "locale")
return gettext.translation("faces", path)
except Exception, e:
return None
def get_gettext():
trans = _get_translation()
if trans: return trans.ugettext
return lambda msg: msg
def get_encoding():
trans = _get_translation()
if trans: return trans.charset()
return locale.getpreferredencoding()
+866
View File
@@ -0,0 +1,866 @@
#@+leo-ver=4
#@+node:@file resource.py
#@@language python
#@<< Copyright >>
#@+node:<< Copyright >>
############################################################################
# Copyright (C) 2005, 2006, 2007, 2008 by Reithinger GmbH
# mreithinger@web.de
#
# This file is part of faces.
#
# faces is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# faces 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the
# Free Software Foundation, Inc.,
# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
############################################################################
#@-node:<< Copyright >>
#@nl
#@<< Imports >>
#@+node:<< Imports >>
import pcalendar
import datetime
import utils
import string
import bisect
import plocale
#@-node:<< Imports >>
#@nl
_is_source = True
_to_datetime = pcalendar.to_datetime
_ = plocale.get_gettext()
#@+others
#@+node:_isattrib
#@+doc
#@nonl
# is used to find snapshot attributes
#@-doc
#@@code
def _isattrib(obj, a):
return a[0] != "_" \
and not callable(getattr(obj, a)) \
and not a.endswith("_members") \
and a not in ("name")
#@-node:_isattrib
#@+node:class ResourceCalendar
class ResourceCalendar(object):
"""
The resource calendar saves the load time of a resource.
Is ia sequence of time intervals of loads. An example of
such a sequence is:
[ (datetime.min, 0),
(2006/1/1, 1.0),
(2006/1/10, 0.5),
(2006/1/15, 0) ]
That means the resource:
is free till january the first 2006
is fully booked from january the first to january 10th
is half booked from january 10th to january 15th
is free since january 15th
"""
#@ @+others
#@+node:__init__
def __init__(self, src=None):
if src:
self.bookings = list(src.bookings)
else:
self.bookings = [ (datetime.datetime.min, 0) ]
#@-node:__init__
#@+node:__str__
def __str__(self):
return str(self.bookings)
#@-node:__str__
#@+node:__repr__
def __repr__(self):
return "<ResourceCalendar %s>" % (str(self))
#@-node:__repr__
#@+node:add_load
def add_load(self, start, end, load):
start = _to_datetime(start)
end = _to_datetime(end)
bookings = self.bookings
# the load will be converted in an integer to avoid
# rouning problems
load = int(load * 10000)
start_item = (start, 0)
start_pos = bisect.bisect_left(bookings, start_item)
left_load = 0
left_load = bookings[start_pos - 1][1]
if start_pos < len(bookings) and bookings[start_pos][0] == start:
prev_load = bookings[start_pos][1]
if prev_load + load == left_load:
del bookings[start_pos]
else:
bookings[start_pos] = (start, prev_load + load)
start_pos += 1
else:
bookings.insert(start_pos, (start, load + left_load))
start_pos += 1
item = (datetime.datetime.min, 0)
for i in range(start_pos, len(bookings)):
end_pos = i
item = bookings[i]
if item[0] >= end: break
bookings[i] = (item[0], item[1] + load)
else:
end_pos = len(bookings)
left_load = bookings[end_pos - 1][1]
if item[0] == end:
if item[1] == left_load:
del bookings[end_pos]
else:
bookings.insert(end_pos, (end, left_load - load))
#@-node:add_load
#@+node:end_of_booking_interval
def end_of_booking_interval(self, date):
date = _to_datetime(date)
bookings = self.bookings
date_item = (date, 999999)
date_pos = bisect.bisect_left(bookings, date_item) - 1
next_date = datetime.datetime.max
load = 0
try:
book_item = bookings[date_pos]
load = bookings[date_pos][1] / 10000.0
next_date = bookings[date_pos + 1][0]
except:
pass
return next_date, load
#@-node:end_of_booking_interval
#@+node:find_free_time
def find_free_time(self, start, length, load, max_load):
bookings = self.bookings
if isinstance(start, datetime.datetime):
adjust_date = _to_datetime
else:
adjust_date = start.calendar.EndDate
start = _to_datetime(start)
load = int(load * 10000)
max_load = int(max_load * 10000)
lb = len(bookings)
def next_possible(index):
while index < lb:
sd, lo = bookings[index]
if lo + load <= max_load:
break
index += 1
sd = adjust_date(max(start, sd))
ed = sd + length
end = _to_datetime(ed)
index += 1
while index < lb:
date, lo = bookings[index]
if date >= end:
#I found a good start date
return None, sd
if lo + load > max_load:
return index + 1, None
index += 1
return None, sd
start_item = (start, 1000000)
i = bisect.bisect_left(bookings, start_item) - 1
next_start = None
while not next_start and i < lb:
i, next_start = next_possible(i)
assert(next_start is not None)
return next_start
#@-node:find_free_time
#@+node:get_bookings
def get_bookings(self, start, end):
start = _to_datetime(start)
end = _to_datetime(end)
bookings = self.bookings
start_item = (start, 0)
start_pos = bisect.bisect_left(bookings, start_item)
if start_pos >= len(bookings) or bookings[start_pos][0] > start:
start_pos -= 1
end_item = (end, 0)
end_pos = bisect.bisect_left(bookings, end_item)
return start_pos, end_pos, bookings
#@-node:get_bookings
#@+node:get_load
def get_load(self, date):
date = _to_datetime(date)
bookings = self.bookings
item = (date, 100000)
pos = bisect.bisect_left(bookings, item) - 1
return bookings[pos][1] / 10000.0
#@-node:get_load
#@-others
#@-node:class ResourceCalendar
#@+node:class _ResourceBase
class _ResourceBase(object):
pass
#@-node:class _ResourceBase
#@+node:class _MetaResource
class _MetaResource(type):
doc_template = """
A resource class. The resources default attributes can
be changed when the class ist instanciated, i.e.
%(name)s(max_load=2.0)
@var max_load:
Specify the maximal allowed load sum of all simultaneously
allocated tasks of a resource. A ME{max_load} of 1.0 (default)
means the resource may be fully allocated. A ME{max_load} of 1.3
means the resource may be allocated with 30%% overtime.
@var title:
Specifies an alternative more descriptive name for the task.
@var efficiency:
The efficiency of a resource can be used for two purposes. First
you can use it as a crude way to model a team. A team of 5 people
should have an efficiency of 5.0. Keep in mind that you cannot
track the member of the team individually if you use this
feature. The other use is to model performance variations between
your resources.
@var vacation:
Specifies the vacation of the resource. This attribute is
specified as a list of date literals or date literal intervals.
Be aware that the end of an interval is excluded, i.e. it is
the first working date.
"""
#@ @+others
#@+node:__init__
def __init__(self, name, bases, dict_):
super(_MetaResource, self).__init__(name, bases, dict_)
self.name = name
self.title = dict_.get("title", name)
self._calendar = { None: ResourceCalendar() }
self._tasks = { }
self.__set_vacation()
self.__add_resource(bases[0])
self.__doc__ = dict_.get("__doc__", self.doc_template) % locals()
#@-node:__init__
#@+node:__or__
def __or__(self, other):
return self().__or__(other)
#@-node:__or__
#@+node:__and__
def __and__(self, other):
return self().__and__(other)
#@-node:__and__
#@+node:__cmp__
def __cmp__(self, other):
return cmp(self.name, getattr(other, "name", None))
#@-node:__cmp__
#@+node:__repr__
def __repr__(self):
return "<Resource %s>" % self.name
#@-node:__repr__
#@+node:__str__
def __str__(self):
return repr(self)
#@-node:__str__
#@+node:__set_vacation
def __set_vacation(self):
vacation = self.vacation
if isinstance(vacation, (tuple, list)):
for v in vacation:
if isinstance(v, (tuple, list)):
self.add_vacation(v[0], v[1])
else:
self.add_vacation(v)
else:
self.add_vacation(vacation)
#@-node:__set_vacation
#@+node:__add_resource
def __add_resource(self, base):
if issubclass(base, _ResourceBase):
members = getattr(base, base.__name__ + "_members", [])
members.append(self)
setattr(base, base.__name__ + "_members", members)
#@-node:__add_resource
#@+node:get_members
def get_members(self):
return getattr(self, self.__name__ + "_members", [])
#@-node:get_members
#@+node:add_vacation
def add_vacation(self, start, end=None):
start_date = _to_datetime(start)
if not end:
end_date = start_date.replace(hour=23, minute=59)
else:
end_date = _to_datetime(end)
for cal in self._calendar.itervalues():
cal.add_load(start_date, end_date, 1)
tp = Booking()
tp.start = start_date
tp.end = end_date
tp.book_start = start_date
tp.book_end = end_date
tp.work_time = end_date - start_date
tp.load = 1.0
tp.name = tp.title = _("(vacation)")
tp._id = ""
self._tasks.setdefault("", []).append(tp)
#@-node:add_vacation
#@+node:calendar
def calendar(self, scenario):
try:
return self._calendar[scenario]
except KeyError:
cal = self._calendar[scenario] = ResourceCalendar(self._calendar[None])
return cal
#@-node:calendar
#@-others
#@-node:class _MetaResource
#@+node:make_team
def make_team(resource):
members = resource.get_members()
if not members:
return resource
result = make_team(members[0])
for r in members[1:]:
result = result & make_team(r)
return result
#@-node:make_team
#@+node:class Booking
class Booking(object):
"""
A booking unit for a task.
"""
#@ << declarations >>
#@+node:<< declarations >>
book_start = datetime.datetime.min
book_end = datetime.datetime.max
actual = False
_id = ""
#@-node:<< declarations >>
#@nl
#@ @+others
#@+node:__init__
def __init__(self, task=None):
self.__task = task
#@-node:__init__
#@+node:__cmp__
def __cmp__(self, other):
return cmp(self._id, other._id)
#@-node:__cmp__
#@+node:path
def path(self):
first_dot = self._id.find(".")
return "root" + self._id[first_dot:]
path = property(path)
#@nonl
#@-node:path
#@+node:_idendity_
def _idendity_(self):
return self._id
#@-node:_idendity_
#@+node:__getattr__
def __getattr__(self, name):
if self.__task:
return getattr(self.__task, name)
raise AttributeError("'%s' is not a valid attribute" % (name))
#@-node:__getattr__
#@-others
#@-node:class Booking
#@+node:class ResourceList
class ResourceList(list):
#@ @+others
#@+node:__init__
def __init__(self, *args):
if args: self.extend(args)
#@-node:__init__
#@-others
#@-node:class ResourceList
#@+node:class Resource
class Resource(_ResourceBase):
#@ << declarations >>
#@+node:<< declarations >>
__metaclass__ = _MetaResource
__attrib_completions__ = {\
"max_load": 'max_load = ',
"title": 'title = "|"',
"efficiency": 'efficiency = ',
"vacation": 'vacation = [("|2002-02-01", "2002-02-05")]' }
__type_image__ = "resource16"
max_load = None # the maximum sum load for all task
vacation = ()
efficiency = 1.0
#@-node:<< declarations >>
#@nl
#@ @+others
#@+node:__init__
def __init__(self, **kwargs):
for k, v in kwargs.iteritems():
setattr(self, k, v)
#@-node:__init__
#@+node:_idendity_
def _idendity_(cls):
return "resource:" + cls.__name__
_idendity_ = classmethod(_idendity_)
#@-node:_idendity_
#@+node:__repr__
def __repr__(self):
return "<Resource %s>" % self.__class__.__name__
#@-node:__repr__
#@+node:__str__
def __str__(self):
return repr(self)
#@-node:__str__
#@+node:__call__
def __call__(self):
return self
#@-node:__call__
#@+node:__hash__
def __hash__(self):
return hash(self.__class__)
#@-node:__hash__
#@+node:__cmp__
def __cmp__(self, other):
return cmp(self.name, other.name)
#@-node:__cmp__
#@+node:__or__
def __or__(self, other):
if type(other) is _MetaResource:
other = other()
result = Resource()
result._subresource = _OrResourceGroup(self, other)
return result
#@-node:__or__
#@+node:__and__
def __and__(self, other):
if type(other) is _MetaResource:
other = other()
result = Resource()
result._subresource = _AndResourceGroup(self, other)
return result
#@-node:__and__
#@+node:_permutation_count
def _permutation_count(self):
if hasattr(self, "_subresource"):
return self._subresource._permutation_count()
return 1
#@-node:_permutation_count
#@+node:_get_resources
def _get_resources(self, state):
if hasattr(self, "_subresource"):
result = self._subresource._get_resources(state)
if self.name != "Resource":
result.name = self.name
if self.title != "Resource":
result.title = self.title
return result
result = ResourceList(self)
return result
#@-node:_get_resources
#@+node:all_members
def all_members(self):
if hasattr(self, "_subresource"):
return self._subresource.all_members()
return [ self.__class__ ]
#@-node:all_members
#@+node:unbook_tasks_of_project
def unbook_tasks_of_project(cls, project_id, scenario):
try:
task_list = cls._tasks[scenario]
except KeyError:
return
add_load = cls.calendar(scenario).add_load
for task_id, bookings in task_list.items():
if task_id.startswith(project_id):
for item in bookings:
add_load(item.book_start, item.book_end, -item.load)
del task_list[task_id]
if not task_list:
del cls._tasks[scenario]
unbook_tasks_of_project = classmethod(unbook_tasks_of_project)
#@-node:unbook_tasks_of_project
#@+node:unbook_task
def unbook_task(cls, task):
identdity = task._idendity_()
scenario = task.scenario
try:
task_list = cls._tasks[scenario]
bookings = task_list[identdity]
except KeyError:
return
add_load = cls.calendar(scenario).add_load
for b in bookings:
add_load(b.book_start, b.book_end, -b.load)
del task_list[identdity]
if not task_list:
del cls._tasks[scenario]
unbook_task = classmethod(unbook_task)
#@-node:unbook_task
#@+node:correct_bookings
def correct_bookings(cls, task):
#correct the booking data with the actual task data
try:
tasks = cls._tasks[task.scenario][task._idendity_()]
except KeyError:
return
for t in tasks:
t.start = task.start.to_datetime()
t.end = task.end.to_datetime()
correct_bookings = classmethod(correct_bookings)
#@-node:correct_bookings
#@+node:book_task
def book_task(cls, task, start, end, load, work_time, actual):
if not work_time: return
start = _to_datetime(start)
end = _to_datetime(end)
identdity = task._idendity_()
task_list = cls._tasks.setdefault(task.scenario, {})
bookings = task_list.setdefault(identdity, [])
add_load = cls.calendar(task.scenario).add_load
tb = Booking(task)
tb.book_start = start
tb.book_end = end
tb._id = identdity
tb.load = load
tb.start = _to_datetime(task.start)
tb.end = _to_datetime(task.end)
tb.title = task.title
tb.name = task.name
tb.work_time = int(work_time)
tb.actual = actual
bookings.append(tb)
result = add_load(start, end, load)
return result
book_task = classmethod(book_task)
#@-node:book_task
#@+node:length_of
def length_of(cls, task):
cal = task.root.calendar
bookings = cls.get_bookings(task)
return sum(map(lambda b: task._to_delta(b.work_time).round(), bookings))
length_of = classmethod(length_of)
#@-node:length_of
#@+node:done_of
def done_of(self, task):
cal = task.root.calendar
now = cal.now
bookings = self.get_bookings(task)
if task.__dict__.has_key("effort"):
efficiency = self.efficiency * task.efficiency
else:
efficiency = 1
def book_done(booking):
if booking.book_start >= now:
return 0
factor = 1
if booking.book_end > now:
start = task._to_start(booking.book_start)
end = task._to_end(booking.book_end)
cnow = task._to_start(now)
factor = float(cnow - start) / ((end - start) or 1)
return factor * booking.work_time * efficiency
return task._to_delta(sum(map(book_done, bookings)))
#@-node:done_of
#@+node:todo_of
def todo_of(self, task):
cal = task.root.calendar
now = cal.now
bookings = self.get_bookings(task)
if task.__dict__.has_key("effort"):
efficiency = self.efficiency * task.efficiency
else:
efficiency = 1
def book_todo(booking):
if booking.book_end <= now:
return 0
factor = 1
if booking.book_start < now:
start = task._to_start(booking.book_start)
end = task._to_end(booking.book_end)
cnow = task._to_start(now)
factor = float(end - cnow) / ((end - start) or 1)
return factor * booking.work_time * efficiency
return task._to_delta(sum(map(book_todo, bookings)))
#@-node:todo_of
#@+node:get_bookings
def get_bookings(cls, task):
return cls._tasks.get(task.scenario, {}).get(task._idendity_(), ())
get_bookings = classmethod(get_bookings)
#@-node:get_bookings
#@+node:get_bookings_at
def get_bookings_at(cls, start, end, scenario):
result = []
try:
items = cls._tasks[scenario].iteritems()
except KeyError:
return ()
for task_id, bookings in items:
result += [ booking for booking in bookings
if booking.book_start < end
and booking.book_end > start ]
vacations = cls._tasks.get("", ())
result += [ booking for booking in vacations
if booking.book_start < end
and booking.book_end > start ]
return result
get_bookings_at = classmethod(get_bookings_at)
#@-node:get_bookings_at
#@+node:find_free_time
def find_free_time(cls, start, length, load, max_load, scenario):
return cls.calendar(scenario).find_free_time(start, length, load, max_load)
find_free_time = classmethod(find_free_time)
#@-node:find_free_time
#@+node:get_load
def get_load(cls, date, scenario):
return cls.calendar(scenario).get_load(date)
get_load = classmethod(get_load)
#@-node:get_load
#@+node:end_of_booking_interval
def end_of_booking_interval(cls, date, task):
return cls.calendar(task.scenario).end_of_booking_interval(date)
end_of_booking_interval = classmethod(end_of_booking_interval)
#@-node:end_of_booking_interval
#@+node:snapshot
def snapshot(self):
from task import _as_string
def isattrib(a):
if a == "max_load" and self.max_load is None: return False
if a in ("name", "title", "vacation"): return False
return _isattrib(self, a)
attribs = filter(isattrib, dir(self))
attribs = map(lambda a: "%s=%s" % (a, _as_string(getattr(self, a))),
attribs)
return self.name + "(%s)" % ", ".join(attribs)
#@-node:snapshot
#@-others
#@-node:class Resource
#@+node:class _ResourceGroup
class _ResourceGroup(object):
#@ @+others
#@+node:__init__
def __init__(self, *args):
self.resources = []
for a in args:
self.__append(a)
#@-node:__init__
#@+node:all_members
def all_members(self):
group = reduce(lambda a, b: a + b.all_members(),
self.resources, [])
group = map(lambda r: (r, True), group)
group = dict(group)
group = group.keys()
return group
#@-node:all_members
#@+node:_permutation_count
def _permutation_count(self):
abstract
#@-node:_permutation_count
#@+node:_refactor
def _refactor(self, arg):
pass
#@-node:_refactor
#@+node:__append
def __append(self, arg):
if isinstance(arg, self.__class__):
self.resources += arg.resources
for r in arg.resources:
self._refactor(r)
return
elif isinstance(arg, Resource):
subresources = getattr(arg, "_subresource", None)
if subresources:
self.__append(subresources)
return
else:
self.resources.append(arg)
else:
assert(isinstance(arg, _ResourceGroup))
self.resources.append(arg)
self._refactor(arg)
#@-node:__append
#@+node:__str__
def __str__(self):
op = lower(self.__class__.__name__[0:-13])
return "(" + \
string.join([str(r) for r in self.resources],
" " + op + " ") + \
")"
#@-node:__str__
#@-others
#@-node:class _ResourceGroup
#@+node:class _OrResourceGroup
class _OrResourceGroup(_ResourceGroup):
#@ @+others
#@+node:_get_resources
def _get_resources(self, state):
for r in self.resources:
c = r._permutation_count()
if c <= state:
state -= c
else:
return r._get_resources(state)
assert(0)
#@-node:_get_resources
#@+node:_permutation_count
def _permutation_count(self):
return sum([ r._permutation_count() for r in self.resources])
#@-node:_permutation_count
#@-others
#@-node:class _OrResourceGroup
#@+node:class _AndResourceGroup
class _AndResourceGroup(_ResourceGroup):
#@ @+others
#@+node:__init__
def __init__(self, *args):
self.factors = [ 1 ]
_ResourceGroup.__init__(self, *args)
#@-node:__init__
#@+node:_refactor
def _refactor(self, arg):
count = arg._permutation_count()
self.factors = [ count * f for f in self.factors ]
self.factors.append(1)
#@-node:_refactor
#@+node:_permutation_count
#print "AndResourceGroup", count, arg, self.factors
def _permutation_count(self):
return self.factors[0]
#@-node:_permutation_count
#@+node:_get_resources
def _get_resources(self, state):
"""delivers None when there are duplicate resources"""
result = []
for i in range(1, len(self.factors)):
f = self.factors[i]
substate = state / f
state %= f
result.append(self.resources[i - 1]._get_resources(substate))
result = ResourceList(*list(utils.flatten(result)))
dupl_test = { }
for r in result:
if dupl_test.has_key(r):
return None
else:
dupl_test[r] = 1
return result
#@-node:_get_resources
#@+node:_has_duplicates
def _has_duplicates(self, state):
resources = self._get_resources(state)
tmp = { }
for r in resources:
if tmp.has_key(r):
return True
tmp[r] = 1
return False
#@-node:_has_duplicates
#@-others
#@-node:class _AndResourceGroup
#@-others
#@-node:@file resource.py
#@-leo
+3855
View File
File diff suppressed because it is too large Load Diff
+113
View File
@@ -0,0 +1,113 @@
############################################################################
# Copyright (C) 2005 by Reithinger GmbH
# mreithinger@web.de
#
# This file is part of faces.
#
# faces is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# faces 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the
# Free Software Foundation, Inc.,
# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
############################################################################
import faces.pcalendar as pcal
import matplotlib.cbook as cbook
import datetime
import sys
class TimeScale(object):
def __init__(self, calendar):
self.data_calendar = calendar
self._create_chart_calendar()
self.now = self.to_num(self.data_calendar.now)
def to_datetime(self, xval):
return xval.to_datetime()
def to_num(self, date):
return self.chart_calendar.WorkingDate(date)
def is_free_slot(self, value):
dt1 = self.chart_calendar.to_starttime(value)
dt2 = self.data_calendar.to_starttime\
(self.data_calendar.from_datetime(dt1))
return dt1 != dt2
def is_free_day(self, value):
dt1 = self.chart_calendar.to_starttime(value)
dt2 = self.data_calendar.to_starttime\
(self.data_calendar.from_datetime(dt1))
return dt1.date() != dt2.date()
def _create_chart_calendar(self):
dcal = self.data_calendar
ccal = self.chart_calendar = pcal.Calendar()
ccal.minimum_time_unit = 1
#pad worktime slots of calendar (all days should be equally long)
slot_sum = lambda slots: sum(map(lambda slot: slot[1] - slot[0], slots))
day_sum = lambda day: slot_sum(dcal.get_working_times(day))
max_work_time = max(map(day_sum, range(7)))
#working_time should have 2/3
sum_time = 3 * max_work_time / 2
#now create timeslots for ccal
def create_time_slots(day):
src_slots = dcal.get_working_times(day)
slots = [0, src_slots, 24*60]
slots = tuple(cbook.flatten(slots))
slots = zip(slots[:-1], slots[1:])
#balance non working slots
work_time = slot_sum(src_slots)
non_work_time = sum_time - work_time
non_slots = filter(lambda s: s not in src_slots, slots)
non_slots = map(lambda s: (s[1] - s[0], s), non_slots)
non_slots.sort()
slots = []
i = 0
for l, s in non_slots:
delta = non_work_time / (len(non_slots) - i)
delta = min(l, delta)
non_work_time -= delta
slots.append((s[0], s[0] + delta))
i += 1
slots.extend(src_slots)
slots.sort()
return slots
min_delta = sys.maxint
for i in range(7):
slots = create_time_slots(i)
ccal.working_times[i] = slots
min_delta = min(min_delta, min(map(lambda s: s[1] - s[0], slots)))
ccal._recalc_working_time()
self.slot_delta = min_delta
self.day_delta = sum_time
self.week_delta = ccal.week_time
_default_scale = TimeScale(pcal._default_calendar)
+124
View File
@@ -0,0 +1,124 @@
############################################################################
# Copyright (C) 2005 by Reithinger GmbH
# mreithinger@web.de
#
# This file is part of faces.
#
# faces is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# faces 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the
# Free Software Foundation, Inc.,
# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
############################################################################
import observer
import os.path
import sys
import os.path
_call_dir = os.path.abspath(os.path.dirname(sys.argv[0]))
def get_installation_path():
try:
if sys.frozen:
path = _call_dir
else:
raise AttributeError()
except AttributeError:
path = os.path.abspath(observer.__file__)
path = os.path.split(path)[0]
path = os.path.normcase(path)
return path
def get_resource_path():
try:
if sys.frozen:
path = _call_dir
path = os.path.join(path, "resources", "faces", "gui")
else:
raise AttributeError()
except AttributeError:
path = get_installation_path()
path = os.path.join(path, "gui", "resources")
path = os.path.normcase(path)
return path
def get_template_path():
try:
if sys.frozen:
path = _call_dir
path = os.path.join(path, "resources", "faces", "templates")
else:
raise AttributeError()
except AttributeError:
path = get_installation_path()
path = os.path.join(path, "templates")
path = os.path.normcase(path)
return path
def get_howtos_path():
try:
if sys.frozen:
path = _call_dir
else:
raise AttributeError()
except AttributeError:
path = get_installation_path()
path = os.path.join(path, "howtos")
path = os.path.normcase(path)
return path
def flatten(items):
if isinstance(items, tuple):
items = list(items)
if not isinstance(items, list):
yield items
stack = [iter(items)]
while stack:
for item in stack[-1]:
if isinstance(item, tuple):
item = list(item)
if isinstance(item, list):
stack.append(iter(item))
break
yield item
else:
stack.pop()
def do_yield():
pass
def progress_start(title, maximum, message=""):
pass
def progress_update(value, message=""):
pass
def progress_end():
pass
+7 -16
View File
@@ -1,18 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<!-- <menuitem icon="terp-hr" id="base.menu_resource_root" name="Resources Management"/> -->
<!-- <menuitem-->
<!-- id="menu_resource_reporting"-->
<!-- name="Reporting"-->
<!-- parent="menu_resource_root"-->
<!-- sequence="40" />-->
<!-- <menuitem-->
<!-- id="menu_resource_configuration"-->
<!-- name="Configuration"-->
<!-- parent="menu_resource_root"-->
<!-- sequence="5"/>-->
<menuitem icon="terp-project" id="base.menu_main" name="Project Management"/>
<menuitem id="base.menu_pm_resources" name="Resources" parent="base.menu_main" sequence="2"/>
<record id="resource_calendar_form" model="ir.ui.view">
<field name="name">resource.calendar.form</field>
<field name="model">resource.calendar</field>
@@ -33,7 +24,7 @@
<field name="view_type">form</field>
<field name="view_id" eval="False"/>
</record>
<!-- <menuitem string="Resource Calendar" action="action_resource_calendar_form" id="menu_resource_calendar_form" parent="menu_resource_configuration"/>-->
<menuitem string="Resource Calendar" action="action_resource_calendar_form" id="menu_resource_calendar_form" parent="base.menu_pm_resources"/>
<record id="view_resource_calendar_week_tree" model="ir.ui.view">
<field name="name">resource.calendar.week.tree</field>
@@ -129,7 +120,7 @@
<field name="res_model">resource.resource</field>
<field name="view_mode">tree,form</field>
</record>
<menuitem icon="terp-project" id="base.menu_main" name="Project Management"/>
<menuitem id="base.menu_pm_planning" name="Planning" parent="base.menu_main" sequence="3"/>
<menuitem action="action_resource_resource_tree" id="base.menu_resource_resource_tree" parent="base.menu_pm_planning" />
@@ -164,12 +155,12 @@
</field>
</record>
<record id="action_resource_calendar_leave_tree" model="ir.actions.act_window">
<field name="name">Leaves</field>
<field name="name">Resource Leaves</field>
<field name="res_model">resource.calendar.leaves</field>
<field name="view_mode">tree,form,calendar</field>
</record>
<!-- <menuitem action="action_resource_calendar_leave_tree" id="menu_resource_calendar_leave_tree" parent="menu_resource_root"/>-->
<menuitem action="action_resource_calendar_leave_tree" id="menu_resource_calendar_leave_tree" parent="base.menu_pm_resources"/>
</data>
</openerp>
+4 -4
View File
@@ -7,13 +7,13 @@ msgstr ""
"Project-Id-Version: OpenERP Server 5.0.0_rc3\n"
"Report-Msgid-Bugs-To: support@openerp.com\n"
"POT-Creation-Date: 2009-08-28 16:01+0000\n"
"PO-Revision-Date: 2009-09-08 16:15+0000\n"
"Last-Translator: Andrius Vilciauskas <a.vilciauskas@gmail.com>\n"
"PO-Revision-Date: 2010-01-30 22:13+0000\n"
"Last-Translator: Domas Jokubauskis <domasj@gmail.com>\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2009-12-16 05:14+0000\n"
"X-Launchpad-Export-Date: 2010-01-31 04:51+0000\n"
"X-Generator: Launchpad (build Unknown)\n"
#. module: sale
@@ -72,7 +72,7 @@ msgstr ""
#: wizard_field:sale.advance_payment_inv,init,qtty:0
#: rml:sale.order:0
msgid "Quantity"
msgstr ""
msgstr "Kiekis"
#. module: sale
#: wizard_view:sale.advance_payment_inv,create:0
+3 -8
View File
@@ -932,6 +932,9 @@ class survey_question_wiz(osv.osv_memory):
surv_name_wiz.write(cr, uid, [context['sur_name_id']], {'response' : tools.ustr(response_id)})
else:
response_id = int(sur_name_read['response'])
if response_id not in surv_all_resp_obj.search(cr, uid, []):
response_id = surv_all_resp_obj.create(cr, uid, {'response_type':'link', 'user_id':uid, 'date_create':datetime.datetime.now(), 'survey_id' : context['survey_id']})
surv_name_wiz.write(cr, uid, [context['sur_name_id']], {'response' : tools.ustr(response_id)})
for key,val in sur_name_read['store_ans'].items():
for field in vals:
if field.split('_')[0] == val['question_id']:
@@ -1013,7 +1016,6 @@ class survey_question_wiz(osv.osv_memory):
if error:
for res in resp_id_list:
sur_name_read['store_ans'].pop(res)
surv_name_wiz.write(cr, uid, [context['sur_name_id']], {'response' :0})
raise osv.except_osv(_('Error !'), _("'" + que_rec['question'] + "' \n" + tools.ustr(que_rec['comment_valid_err_msg'])))
resp_obj.write(cr, uid, resp_id, {'comment':val1})
sur_name_read['store_ans'][resp_id].update({key1:val1})
@@ -1050,7 +1052,6 @@ class survey_question_wiz(osv.osv_memory):
if error:
for res in resp_id_list:
sur_name_read['store_ans'].pop(res)
surv_name_wiz.write(cr, uid, [context['sur_name_id']], {'response' :0})
raise osv.except_osv(_('Error !'), _("'" + que_rec['question'] + "' \n" + tools.ustr(que_rec['validation_valid_err_msg'])))
if key1.split('_')[1] == "single" :
resp_obj.write(cr, uid, resp_id, {'single_text':val1})
@@ -1086,19 +1087,16 @@ class survey_question_wiz(osv.osv_memory):
if comment_field and comment_value:
for res in resp_id_list:
sur_name_read['store_ans'].pop(res)
surv_name_wiz.write(cr, uid, [context['sur_name_id']], {'response' :0})
raise osv.except_osv(_('Error re !'), _("'" + que_rec['question'] + "' " + tools.ustr(que_rec['make_comment_field_err_msg'])))
if que_rec['type'] == "rating_scale" and que_rec['rating_allow_one_column_require'] and len(selected_value) > len(list(set(selected_value))):
for res in resp_id_list:
sur_name_read['store_ans'].pop(res)
surv_name_wiz.write(cr, uid, [context['sur_name_id']], {'response' :0})
raise osv.except_osv(_('Error re !'), _("'" + que_rec['question'] + "\n you cannot select same answer more than one times'"))
if not select_count:
resp_obj.write(cr, uid, resp_id, {'state':'skip'})
if que_rec['numeric_required_sum'] and numeric_sum > que_rec['numeric_required_sum']:
for res in resp_id_list:
sur_name_read['store_ans'].pop(res)
surv_name_wiz.write(cr, uid, [context['sur_name_id']], {'response' :0})
raise osv.except_osv(_('Error re !'), _("'" + que_rec['question'] + "' " + tools.ustr(que_rec['numeric_required_sum_err_msg'])))
if que_rec['type'] in ['multiple_choice_multiple_ans','matrix_of_choices_only_one_ans','matrix_of_choices_only_multi_ans','matrix_of_drop_down_menus','rating_scale','multiple_textboxes','numerical_textboxes','date','date_and_time'] and que_rec['is_require_answer']:
if matrix_list:
@@ -1109,7 +1107,6 @@ class survey_question_wiz(osv.osv_memory):
(que_rec['required_type'] == 'a range' and (len(list(set(matrix_list))) < que_rec['minimum_req_ans'] or len(list(set(matrix_list))) > que_rec['maximum_req_ans'])):
for res in resp_id_list:
sur_name_read['store_ans'].pop(res)
surv_name_wiz.write(cr, uid, [context['sur_name_id']], {'response' :0})
raise osv.except_osv(_('Error !'), _("'" + que_rec['question'] + "' " + tools.ustr(que_rec['req_error_msg'])))
elif (que_rec['required_type'] == 'all' and select_count < len(que_rec['answer_choice_ids'])) or \
(que_rec['required_type'] == 'at least' and select_count < que_rec['req_ans']) or \
@@ -1118,12 +1115,10 @@ class survey_question_wiz(osv.osv_memory):
(que_rec['required_type'] == 'a range' and (select_count < que_rec['minimum_req_ans'] or select_count > que_rec['maximum_req_ans'])):
for res in resp_id_list:
sur_name_read['store_ans'].pop(res)
surv_name_wiz.write(cr, uid, [context['sur_name_id']], {'response' :0})
raise osv.except_osv(_('Error !'), _("'" + que_rec['question'] + "' " + tools.ustr(que_rec['req_error_msg'])))
if que_rec['type'] in ['multiple_choice_only_one_ans','single_textbox','comment'] and que_rec['is_require_answer'] and select_count <= 0:
for res in resp_id_list:
sur_name_read['store_ans'].pop(res)
surv_name_wiz.write(cr, uid, [context['sur_name_id']], {'response' :0})
raise osv.except_osv(_('Error re !'), _("'" + que_rec['question'] + "' " + tools.ustr(que_rec['req_error_msg'])))
else:
resp_id_list = []
+23
View File
@@ -0,0 +1,23 @@
##############################################################################
#
# 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 users_ldap
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
+40
View File
@@ -0,0 +1,40 @@
##############################################################################
#
# 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/>.
#
##############################################################################
{
"name" : "Authenticate users with ldap server",
"version" : "0.1",
"depends" : ["base"],
"author" : "Tiny",
"description": """Add support for authentication by ldap server""",
"website" : "http://www.openerp.com",
"category" : "Generic Modules/Others",
"init_xml" : [
],
"demo_xml" : [
],
"update_xml" : [
"users_ldap_view.xml",
],
"active": False,
"installable": True
}
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
+171
View File
@@ -0,0 +1,171 @@
##############################################################################
#
# 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 fields, osv
import pooler
import tools
from service import security
import ldap
from ldap.filter import filter_format
class CompanyLDAP(osv.osv):
_name = 'res.company.ldap'
_order = 'sequence'
_rec_name = 'ldap_server'
_columns = {
'sequence': fields.integer('Sequence'),
'company': fields.many2one('res.company', 'Company', required=True,
ondelete='cascade'),
'ldap_server': fields.char('LDAP Server address', size=64, required=True),
'ldap_server_port': fields.integer('LDAP Server port', required=True),
'ldap_binddn': fields.char('LDAP binddn', size=64, required=True),
'ldap_password': fields.char('LDAP password', size=64, required=True),
'ldap_filter': fields.char('LDAP filter', size=64, required=True),
'ldap_base': fields.char('LDAP base', size=64, required=True),
'user': fields.many2one('res.users', 'Model User',
help="Model used for user creation"),
'create_user': fields.boolean('Create user',
help="Create the user if not in database"),
}
_defaults = {
'ldap_server': lambda *a: '127.0.0.1',
'ldap_server_port': lambda *a: 389,
'sequence': lambda *a: 10,
'create_user': lambda *a: True,
}
CompanyLDAP()
class res_company(osv.osv):
_inherit = "res.company"
_columns = {
'ldaps': fields.one2many('res.company.ldap', 'company', 'LDAP Parameters'),
}
res_company()
class users(osv.osv):
_inherit = "res.users"
def login(self, db, login, password):
ret = super(users,self).login(db, login, password)
tools.debug(ret)
if ret:
return ret
pool = pooler.get_pool(db)
cr = pooler.get_db(db).cursor()
action_obj = pool.get('ir.actions.actions')
cr.execute("""
SELECT id, company, ldap_server, ldap_server_port, ldap_binddn, ldap_password,
ldap_filter, ldap_base, "user", create_user
FROM res_company_ldap
WHERE ldap_server != '' and ldap_binddn != '' ORDER BY sequence""")
for res_company_ldap in cr.dictfetchall():
tools.debug(res_company_ldap)
try:
l = ldap.open(res_company_ldap['ldap_server'], res_company_ldap['ldap_server_port'])
if l.simple_bind_s(res_company_ldap['ldap_binddn'], res_company_ldap['ldap_password']):
base = res_company_ldap['ldap_base']
scope = ldap.SCOPE_SUBTREE
filter = filter_format(res_company_ldap['ldap_filter'], (login,))
retrieve_attributes = None
result_id = l.search(base, scope, filter, retrieve_attributes)
timeout = 60
result_type, result_data = l.result(result_id, timeout)
if not result_data:
continue
if result_type == ldap.RES_SEARCH_RESULT and len(result_data) == 1:
dn = result_data[0][0]
tools.debug(dn)
name = result_data[0][1]['cn'][0]
if l.bind_s(dn, password):
l.unbind()
cr.execute("SELECT id FROM res_users WHERE login=%s",(tools.ustr(login),))
res = cr.fetchone()
tools.debug(res)
if res:
cr.close()
return res[0]
if not res_company_ldap['create_user']:
continue
action_id = action_obj.search(cr, 1, [('usage', '=', 'menu')])[0]
if res_company_ldap['user']:
res = self.copy(cr, 1, res_company_ldap['user'],
default={'active': True})
self.write(cr, 1, res, {
'name': name,
'login': login.encode('utf-8'),
'company_id': res_company_ldap['company'],
})
else:
res = self.create(cr, 1, {
'name': name,
'login': login.encode('utf-8'),
'company_id': res_company_ldap['company'],
'action_id': action_id,
'menu_id': action_id,
})
cr.commit()
cr.close()
return res
l.unbind()
except Exception, e:
tools.debug(e)
continue
cr.close()
return False
def check(self, db, uid, passwd):
try:
return super(users,self).check(db, uid, passwd)
except: # AccessDenied
pass
cr = pooler.get_db(db).cursor()
user = self.browse(cr, 1, uid)
if user and user.company_id.ldaps:
for res_company_ldap in user.company_id.ldaps:
try:
l = ldap.open(res_company_ldap.ldap_server, res_company_ldap.ldap_server_port)
if l.simple_bind_s(res_company_ldap.ldap_binddn,
res_company_ldap.ldap_password):
base = res_company_ldap.ldap_base
scope = ldap.SCOPE_SUBTREE
filter = filter_format(res_company_ldap.ldap_filter, (user.login,))
retrieve_attributes = None
result_id = l.search(base, scope, filter, retrieve_attributes)
timeout = 60
result_type, result_data = l.result(result_id, timeout)
if result_data and result_type == ldap.RES_SEARCH_RESULT and len(result_data) == 1:
dn = result_data[0][0]
if l.bind_s(dn, passwd):
l.unbind()
self._uid_cache.setdefault(db, {})[uid] = passwd
cr.close()
return True
l.unbind()
except Exception, e:
tools.debug(e)
pass
cr.close()
raise security.ExceptionNoTb('AccessDenied')
users()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
+35
View File
@@ -0,0 +1,35 @@
<?xml version="1.0"?>
<openerp>
<data>
<record model="ir.ui.view" id="company_form_view">
<field name="name">res.company.form.inherit.users_ldap</field>
<field name="model">res.company</field>
<field name="type">form</field>
<field name="inherit_id" ref="base.view_company_form"/>
<field name="arch" type="xml">
<xpath expr="/form/notebook/page[@string='Configuration']" position="inside">
<field name="ldaps" nolabel="1" colspan="4">
<form string="LDAP Configuration">
<field name="ldap_server"/>
<field name="ldap_server_port"/>
<field name="ldap_binddn"/>
<field name="ldap_password"/>
<field name="ldap_base"/>
<field name="ldap_filter"/>
<field name="create_user"/>
<field name="user"/>
<newline/>
<field name="sequence"/>
</form>
<tree string="LDAP Configuration">
<field name="sequence"/>
<field name="ldap_server"/>
<field name="ldap_server_port"/>
<field name="ldap_base"/>
</tree>
</field>
</xpath>
</field>
</record>
</data>
</openerp>