merge
bzr revid: uco@tinyerp.co.in-20100129130359-i2sk7iqpp21gy70o bzr revid: sbh@tinyerp.com-20100201050827-sso64l1b6g2ry3yn
This commit is contained in:
+37
-37
@@ -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
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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': {
|
||||
|
||||
@@ -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,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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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')]" />
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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
@@ -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()
|
||||
|
||||
@@ -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', '<>', 'cancelled'),('state', '<>', '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"/>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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:
|
||||
|
||||
|
||||
@@ -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')]" />
|
||||
|
||||
@@ -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'],
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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','<>',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>
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
##############################################################################
|
||||
|
||||
import project
|
||||
import wizard
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
||||
|
||||
@@ -33,7 +33,8 @@
|
||||
"demo_xml" : [#"project_demo.xml"
|
||||
],
|
||||
"update_xml": [
|
||||
"project_view.xml",
|
||||
"project_view.xml",
|
||||
"project_wizard.xml"
|
||||
],
|
||||
'installable': True,
|
||||
'active': False,
|
||||
|
||||
@@ -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:
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
Executable
+3855
File diff suppressed because it is too large
Load Diff
@@ -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)
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 = []
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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>
|
||||
Reference in New Issue
Block a user