merge with lp:~openerp-dev/openobject-addons/trunk-social-tde

bzr revid: rgaopenerp-20120316064433-wegk0yzgu27uskow
bzr revid: rgaopenerp-20120318112818-10pjx8upu5wiw2g7
This commit is contained in:
RGA(OpenERP)
2012-03-18 16:58:18 +05:30
64 changed files with 6626 additions and 278 deletions
@@ -8,19 +8,19 @@ msgstr ""
"Project-Id-Version: openobject-addons\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2012-02-08 00:35+0000\n"
"PO-Revision-Date: 2011-11-03 22:52+0000\n"
"PO-Revision-Date: 2012-03-13 16:49+0000\n"
"Last-Translator: Milan Milosevic <Unknown>\n"
"Language-Team: Serbian latin <sr@latin@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: 2012-02-09 06:32+0000\n"
"X-Generator: Launchpad (build 14763)\n"
"X-Launchpad-Export-Date: 2012-03-14 04:42+0000\n"
"X-Generator: Launchpad (build 14933)\n"
#. module: account_analytic_analysis
#: field:account.analytic.account,revenue_per_hour:0
msgid "Revenue per Time (real)"
msgstr ""
msgstr "Prihod po vremenu (realnom)"
#. module: account_analytic_analysis
#: help:account.analytic.account,remaining_ca:0
@@ -38,11 +38,13 @@ msgid ""
"The contracts to be renewed because the deadline is passed or the working "
"hours are higher than the allocated hours"
msgstr ""
"Ugovor treba da se obnovi jer je rok pređen, ili ima više radnih sati od "
"određenih sati"
#. module: account_analytic_analysis
#: view:account.analytic.account:0
msgid "Pending contracts to renew with your customer"
msgstr ""
msgstr "Ugovori na čekanju za potvrdu sa Vašom strankom"
#. module: account_analytic_analysis
#: help:account.analytic.account,hours_qtt_non_invoiced:0
@@ -50,26 +52,28 @@ msgid ""
"Number of time (hours/days) (from journal of type 'general') that can be "
"invoiced if you invoice based on analytic account."
msgstr ""
"Broj vremena (dana/sati) (iz dnevnika tipa 'opšte') koji se može fakturisati "
"ako Vam je fakturisanje bazirano na analitičkom kontu."
#. module: account_analytic_analysis
#: view:account.analytic.account:0
msgid "Analytic Accounts with a past deadline in one month."
msgstr ""
msgstr "Analitička konta sa pređenim rokom u jednom mesecu."
#. module: account_analytic_analysis
#: view:account.analytic.account:0
msgid "Group By..."
msgstr ""
msgstr "Grupiši po..."
#. module: account_analytic_analysis
#: view:account.analytic.account:0
msgid "End Date"
msgstr ""
msgstr "Datum završetka"
#. module: account_analytic_analysis
#: view:account.analytic.account:0
msgid "Create Invoice"
msgstr ""
msgstr "Napravi fakturu"
#. module: account_analytic_analysis
#: field:account.analytic.account,last_invoice_date:0
@@ -87,16 +91,18 @@ msgid ""
"Number of time you spent on the analytic account (from timesheet). It "
"computes quantities on all journal of type 'general'."
msgstr ""
"Vreme potrošeno na analitički konto (s vremenske tabele). Obračunava "
"kvantitete na sve dnevnike tipa 'opšte'."
#. module: account_analytic_analysis
#: view:account.analytic.account:0
msgid "Contracts in progress"
msgstr ""
msgstr "Ugovori u toku"
#. module: account_analytic_analysis
#: field:account.analytic.account,is_overdue_quantity:0
msgid "Overdue Quantity"
msgstr ""
msgstr "Preloraečnja"
#. module: account_analytic_analysis
#: model:ir.actions.act_window,help:account_analytic_analysis.action_account_analytic_overdue
@@ -108,6 +114,11 @@ msgid ""
"pending accounts and reopen or close the according to the negotiation with "
"the customer."
msgstr ""
"Naći ćete ovde ugovore koje treba obnoviti jer je rok istekao, ili su radni "
"sati veći od određenih. OpenERP automatski postavlja ta analitička konta na "
"stanje 'na čekanju', da bi se iznelo upozorenje za vreme sačuvavanja u "
"tabelama vremena. Prodavci bi trebalo da provere sva konta na čekanju i "
"zatvore ili ih ponovo otvore, u zavisnosti od dogovora sa strankama."
#. module: account_analytic_analysis
#: field:account.analytic.account,ca_theorical:0
@@ -117,7 +128,7 @@ msgstr "Teoretski prihod"
#. module: account_analytic_analysis
#: field:account.analytic.account,hours_qtt_non_invoiced:0
msgid "Uninvoiced Time"
msgstr ""
msgstr "Neobračunato vreme"
#. module: account_analytic_analysis
#: help:account.analytic.account,last_worked_invoiced_date:0
@@ -131,7 +142,7 @@ msgstr ""
#. module: account_analytic_analysis
#: view:account.analytic.account:0
msgid "To Renew"
msgstr ""
msgstr "Za obnovu"
#. module: account_analytic_analysis
#: field:account.analytic.account,last_worked_date:0
@@ -141,24 +152,25 @@ msgstr "Datum poslednje Cene / Rada"
#. module: account_analytic_analysis
#: field:account.analytic.account,hours_qtt_invoiced:0
msgid "Invoiced Time"
msgstr ""
msgstr "Obračunato vreme"
#. module: account_analytic_analysis
#: view:account.analytic.account:0
msgid ""
"A contract in OpenERP is an analytic account having a partner set on it."
msgstr ""
"Ugovor u OpenERP-u je analitički konto sa partnerom koji mu je pripisan."
#. module: account_analytic_analysis
#: field:account.analytic.account,remaining_hours:0
msgid "Remaining Time"
msgstr ""
msgstr "Preostalo vreme"
#. module: account_analytic_analysis
#: model:ir.actions.act_window,name:account_analytic_analysis.action_account_analytic_overdue
#: model:ir.ui.menu,name:account_analytic_analysis.menu_action_account_analytic_overdue
msgid "Contracts to Renew"
msgstr ""
msgstr "Ugovori za obnovu"
#. module: account_analytic_analysis
#: field:account.analytic.account,theorical_margin:0
@@ -168,7 +180,7 @@ msgstr "Teoretska stopa marže"
#. module: account_analytic_analysis
#: view:account.analytic.account:0
msgid " +1 Month"
msgstr ""
msgstr " +1 mesec"
#. module: account_analytic_analysis
#: help:account.analytic.account,ca_theorical:0
@@ -183,7 +195,7 @@ msgstr ""
#. module: account_analytic_analysis
#: view:account.analytic.account:0
msgid "Pending"
msgstr ""
msgstr "Na čekanju"
#. module: account_analytic_analysis
#: field:account.analytic.account,ca_to_invoice:0
@@ -198,7 +210,7 @@ msgstr "Izračunato pomoću formule: Fakturisani iznos - Ukupni troškovi."
#. module: account_analytic_analysis
#: view:account.analytic.account:0
msgid "Parent"
msgstr ""
msgstr "Parent"
#. module: account_analytic_analysis
#: field:account.analytic.account,user_ids:0
@@ -214,7 +226,7 @@ msgstr "Izračunava se pomoću formule: (Realna marža / ukupni troškovi) * 100
#. module: account_analytic_analysis
#: model:ir.model,name:account_analytic_analysis.model_account_analytic_analysis_summary_user
msgid "Hours Summary by User"
msgstr "Ukupno sati po Korisniku"
msgstr "Ukupno sati po korisniku"
#. module: account_analytic_analysis
#: field:account.analytic.account,ca_invoiced:0
@@ -229,7 +241,7 @@ msgstr "Datum poslednjeg obračunatog troška"
#. module: account_analytic_analysis
#: view:account.analytic.account:0
msgid "Contract"
msgstr ""
msgstr "Ugovor"
#. module: account_analytic_analysis
#: field:account.analytic.account,real_margin_rate:0
@@ -264,7 +276,7 @@ msgstr "Preostali prihod"
#. module: account_analytic_analysis
#: help:account.analytic.account,remaining_hours:0
msgid "Computed using the formula: Maximum Time - Total Time"
msgstr ""
msgstr "Obračunato koristeći formulu: Maksimalno vreme - ukupno vreme"
#. module: account_analytic_analysis
#: help:account.analytic.account,hours_qtt_invoiced:0
@@ -272,6 +284,8 @@ msgid ""
"Number of time (hours/days) that can be invoiced plus those that already "
"have been invoiced."
msgstr ""
"Trajanje vremena (dani/sati) koji se mogu obračunati, plus ono koje je već "
"obračunato."
#. module: account_analytic_analysis
#: help:account.analytic.account,ca_to_invoice:0
@@ -285,7 +299,7 @@ msgstr ""
#. module: account_analytic_analysis
#: help:account.analytic.account,revenue_per_hour:0
msgid "Computed using the formula: Invoiced Amount / Total Time"
msgstr ""
msgstr "Izračunato koristeći formulu: obračunat iznos / ukupno vreme"
#. module: account_analytic_analysis
#: field:account.analytic.account,total_cost:0
@@ -304,18 +318,18 @@ msgstr "Mesec"
#: field:account_analytic_analysis.summary.user,account_id:0
#: model:ir.model,name:account_analytic_analysis.model_account_analytic_account
msgid "Analytic Account"
msgstr "Analitički nalog"
msgstr "Analitički konto"
#. module: account_analytic_analysis
#: model:ir.actions.act_window,name:account_analytic_analysis.action_account_analytic_overdue_all
#: model:ir.ui.menu,name:account_analytic_analysis.menu_action_account_analytic_overdue_all
msgid "Contracts"
msgstr ""
msgstr "Ugovori"
#. module: account_analytic_analysis
#: view:account.analytic.account:0
msgid "Manager"
msgstr ""
msgstr "Rukovodilac"
#. module: account_analytic_analysis
#: model:ir.actions.act_window,name:account_analytic_analysis.action_hr_tree_invoiced_all
@@ -326,22 +340,22 @@ msgstr "Sve neobračunate stavke"
#. module: account_analytic_analysis
#: help:account.analytic.account,last_invoice_date:0
msgid "If invoice from the costs, this is the date of the latest invoiced."
msgstr ""
msgstr "ako se obračunava iz troškova, ovo je datum poslednjeg obračuna."
#. module: account_analytic_analysis
#: view:account.analytic.account:0
msgid "Associated Partner"
msgstr ""
msgstr "Povezani partner"
#. module: account_analytic_analysis
#: view:account.analytic.account:0
msgid "Open"
msgstr ""
msgstr "Otvori"
#. module: account_analytic_analysis
#: view:account.analytic.account:0
msgid "Contracts that are not assigned to an account manager."
msgstr ""
msgstr "Ugovori koji oš nisu pripisani rukovodiocu naloga."
#. module: account_analytic_analysis
#: field:account.analytic.account,hours_quantity:0
+793
View File
@@ -0,0 +1,793 @@
# Lithuanian translation for openobject-addons
# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012
# This file is distributed under the same license as the openobject-addons package.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2012.
#
msgid ""
msgstr ""
"Project-Id-Version: openobject-addons\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2012-02-08 01:37+0100\n"
"PO-Revision-Date: 2012-03-09 15:10+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: Lithuanian <lt@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: 2012-03-10 04:54+0000\n"
"X-Generator: Launchpad (build 14914)\n"
#. module: account_asset
#: view:account.asset.asset:0
msgid "Assets in draft and open states"
msgstr ""
#. module: account_asset
#: field:account.asset.category,method_end:0
#: field:account.asset.history,method_end:0 field:asset.modify,method_end:0
msgid "Ending date"
msgstr "Pabaigos data"
#. module: account_asset
#: field:account.asset.asset,value_residual:0
msgid "Residual Value"
msgstr ""
#. module: account_asset
#: field:account.asset.category,account_expense_depreciation_id:0
msgid "Depr. Expense Account"
msgstr ""
#. module: account_asset
#: view:asset.depreciation.confirmation.wizard:0
msgid "Compute Asset"
msgstr ""
#. module: account_asset
#: view:asset.asset.report:0
msgid "Group By..."
msgstr ""
#. module: account_asset
#: field:asset.asset.report,gross_value:0
msgid "Gross Amount"
msgstr ""
#. module: account_asset
#: view:account.asset.asset:0 field:account.asset.asset,name:0
#: field:account.asset.depreciation.line,asset_id:0
#: field:account.asset.history,asset_id:0 field:account.move.line,asset_id:0
#: view:asset.asset.report:0 field:asset.asset.report,asset_id:0
#: model:ir.model,name:account_asset.model_account_asset_asset
msgid "Asset"
msgstr ""
#. module: account_asset
#: help:account.asset.asset,prorata:0 help:account.asset.category,prorata:0
msgid ""
"Indicates that the first depreciation entry for this asset have to be done "
"from the purchase date instead of the first January"
msgstr ""
#. module: account_asset
#: field:account.asset.history,name:0
msgid "History name"
msgstr ""
#. module: account_asset
#: field:account.asset.asset,company_id:0
#: field:account.asset.category,company_id:0 view:asset.asset.report:0
#: field:asset.asset.report,company_id:0
msgid "Company"
msgstr ""
#. module: account_asset
#: view:asset.modify:0
msgid "Modify"
msgstr ""
#. module: account_asset
#: selection:account.asset.asset,state:0 view:asset.asset.report:0
#: selection:asset.asset.report,state:0
msgid "Running"
msgstr ""
#. module: account_asset
#: field:account.asset.depreciation.line,amount:0
msgid "Depreciation Amount"
msgstr ""
#. module: account_asset
#: view:asset.asset.report:0
#: model:ir.actions.act_window,name:account_asset.action_asset_asset_report
#: model:ir.model,name:account_asset.model_asset_asset_report
#: model:ir.ui.menu,name:account_asset.menu_action_asset_asset_report
msgid "Assets Analysis"
msgstr ""
#. module: account_asset
#: field:asset.modify,name:0
msgid "Reason"
msgstr ""
#. module: account_asset
#: field:account.asset.asset,method_progress_factor:0
#: field:account.asset.category,method_progress_factor:0
msgid "Degressive Factor"
msgstr ""
#. module: account_asset
#: model:ir.actions.act_window,name:account_asset.action_account_asset_asset_list_normal
#: model:ir.ui.menu,name:account_asset.menu_action_account_asset_asset_list_normal
msgid "Asset Categories"
msgstr ""
#. module: account_asset
#: view:asset.depreciation.confirmation.wizard:0
msgid ""
"This wizard will post the depreciation lines of running assets that belong "
"to the selected period."
msgstr ""
#. module: account_asset
#: field:account.asset.asset,account_move_line_ids:0
#: field:account.move.line,entry_ids:0
#: model:ir.actions.act_window,name:account_asset.act_entries_open
msgid "Entries"
msgstr ""
#. module: account_asset
#: view:account.asset.asset:0
#: field:account.asset.asset,depreciation_line_ids:0
msgid "Depreciation Lines"
msgstr ""
#. module: account_asset
#: help:account.asset.asset,salvage_value:0
msgid "It is the amount you plan to have that you cannot depreciate."
msgstr ""
#. module: account_asset
#: field:account.asset.depreciation.line,depreciation_date:0
#: view:asset.asset.report:0 field:asset.asset.report,depreciation_date:0
msgid "Depreciation Date"
msgstr ""
#. module: account_asset
#: field:account.asset.category,account_asset_id:0
msgid "Asset Account"
msgstr ""
#. module: account_asset
#: field:asset.asset.report,posted_value:0
msgid "Posted Amount"
msgstr ""
#. module: account_asset
#: view:account.asset.asset:0 view:asset.asset.report:0
#: model:ir.actions.act_window,name:account_asset.action_account_asset_asset_form
#: model:ir.ui.menu,name:account_asset.menu_action_account_asset_asset_form
#: model:ir.ui.menu,name:account_asset.menu_finance_assets
#: model:ir.ui.menu,name:account_asset.menu_finance_config_assets
msgid "Assets"
msgstr ""
#. module: account_asset
#: field:account.asset.category,account_depreciation_id:0
msgid "Depreciation Account"
msgstr ""
#. module: account_asset
#: view:account.asset.asset:0 view:account.asset.category:0
#: view:account.asset.history:0 view:asset.modify:0 field:asset.modify,note:0
msgid "Notes"
msgstr ""
#. module: account_asset
#: field:account.asset.depreciation.line,move_id:0
msgid "Depreciation Entry"
msgstr ""
#. module: account_asset
#: sql_constraint:account.move.line:0
msgid "Wrong credit or debit value in accounting entry !"
msgstr ""
#. module: account_asset
#: view:asset.asset.report:0 field:asset.asset.report,nbr:0
msgid "# of Depreciation Lines"
msgstr ""
#. module: account_asset
#: view:asset.asset.report:0
msgid "Assets in draft state"
msgstr ""
#. module: account_asset
#: field:account.asset.asset,method_end:0
#: selection:account.asset.asset,method_time:0
#: selection:account.asset.category,method_time:0
#: selection:account.asset.history,method_time:0
msgid "Ending Date"
msgstr ""
#. module: account_asset
#: field:account.asset.asset,code:0
msgid "Reference"
msgstr ""
#. module: account_asset
#: constraint:account.invoice:0
msgid "Invalid BBA Structured Communication !"
msgstr ""
#. module: account_asset
#: view:account.asset.asset:0
msgid "Account Asset"
msgstr ""
#. module: account_asset
#: model:ir.actions.act_window,name:account_asset.action_asset_depreciation_confirmation_wizard
#: model:ir.ui.menu,name:account_asset.menu_asset_depreciation_confirmation_wizard
msgid "Compute Assets"
msgstr ""
#. module: account_asset
#: field:account.asset.depreciation.line,sequence:0
msgid "Sequence of the depreciation"
msgstr ""
#. module: account_asset
#: field:account.asset.asset,method_period:0
#: field:account.asset.category,method_period:0
#: field:account.asset.history,method_period:0
#: field:asset.modify,method_period:0
msgid "Period Length"
msgstr ""
#. module: account_asset
#: selection:account.asset.asset,state:0 view:asset.asset.report:0
#: selection:asset.asset.report,state:0
msgid "Draft"
msgstr ""
#. module: account_asset
#: view:asset.asset.report:0
msgid "Date of asset purchase"
msgstr ""
#. module: account_asset
#: help:account.asset.asset,method_number:0
msgid "Calculates Depreciation within specified interval"
msgstr ""
#. module: account_asset
#: view:account.asset.asset:0
msgid "Change Duration"
msgstr ""
#. module: account_asset
#: field:account.asset.category,account_analytic_id:0
msgid "Analytic account"
msgstr ""
#. module: account_asset
#: field:account.asset.asset,method:0 field:account.asset.category,method:0
msgid "Computation Method"
msgstr ""
#. module: account_asset
#: help:account.asset.asset,method_period:0
msgid "State here the time during 2 depreciations, in months"
msgstr ""
#. module: account_asset
#: constraint:account.asset.asset:0
msgid ""
"Prorata temporis can be applied only for time method \"number of "
"depreciations\"."
msgstr ""
#. module: account_asset
#: help:account.asset.history,method_time:0
msgid ""
"The method to use to compute the dates and number of depreciation lines.\n"
"Number of Depreciations: Fix the number of depreciation lines and the time "
"between 2 depreciations.\n"
"Ending Date: Choose the time between 2 depreciations and the date the "
"depreciations won't go beyond."
msgstr ""
#. module: account_asset
#: field:account.asset.asset,purchase_value:0
msgid "Gross value "
msgstr ""
#. module: account_asset
#: constraint:account.asset.asset:0
msgid "Error ! You can not create recursive assets."
msgstr ""
#. module: account_asset
#: help:account.asset.history,method_period:0
msgid "Time in month between two depreciations"
msgstr ""
#. module: account_asset
#: view:asset.asset.report:0 field:asset.asset.report,name:0
msgid "Year"
msgstr ""
#. module: account_asset
#: view:asset.modify:0
#: model:ir.actions.act_window,name:account_asset.action_asset_modify
#: model:ir.model,name:account_asset.model_asset_modify
msgid "Modify Asset"
msgstr ""
#. module: account_asset
#: view:account.asset.asset:0
msgid "Other Information"
msgstr ""
#. module: account_asset
#: field:account.asset.asset,salvage_value:0
msgid "Salvage Value"
msgstr ""
#. module: account_asset
#: field:account.invoice.line,asset_category_id:0 view:asset.asset.report:0
msgid "Asset Category"
msgstr ""
#. module: account_asset
#: view:account.asset.asset:0
msgid "Set to Close"
msgstr ""
#. module: account_asset
#: model:ir.actions.wizard,name:account_asset.wizard_asset_compute
msgid "Compute assets"
msgstr ""
#. module: account_asset
#: model:ir.actions.wizard,name:account_asset.wizard_asset_modify
msgid "Modify asset"
msgstr ""
#. module: account_asset
#: view:account.asset.asset:0
msgid "Assets in closed state"
msgstr ""
#. module: account_asset
#: field:account.asset.asset,parent_id:0
msgid "Parent Asset"
msgstr ""
#. module: account_asset
#: view:account.asset.history:0
#: model:ir.model,name:account_asset.model_account_asset_history
msgid "Asset history"
msgstr ""
#. module: account_asset
#: view:asset.asset.report:0
msgid "Assets purchased in current year"
msgstr ""
#. module: account_asset
#: field:account.asset.asset,state:0 field:asset.asset.report,state:0
msgid "State"
msgstr ""
#. module: account_asset
#: model:ir.model,name:account_asset.model_account_invoice_line
msgid "Invoice Line"
msgstr ""
#. module: account_asset
#: constraint:account.move.line:0
msgid ""
"The selected account of your Journal Entry forces to provide a secondary "
"currency. You should remove the secondary currency on the account or select "
"a multi-currency view on the journal."
msgstr ""
#. module: account_asset
#: view:asset.asset.report:0
msgid "Month"
msgstr ""
#. module: account_asset
#: view:account.asset.asset:0
msgid "Depreciation Board"
msgstr ""
#. module: account_asset
#: model:ir.model,name:account_asset.model_account_move_line
msgid "Journal Items"
msgstr ""
#. module: account_asset
#: field:asset.asset.report,unposted_value:0
msgid "Unposted Amount"
msgstr ""
#. module: account_asset
#: field:account.asset.asset,method_time:0
#: field:account.asset.category,method_time:0
#: field:account.asset.history,method_time:0
msgid "Time Method"
msgstr ""
#. module: account_asset
#: view:account.asset.category:0
msgid "Analytic information"
msgstr ""
#. module: account_asset
#: view:asset.modify:0
msgid "Asset durations to modify"
msgstr ""
#. module: account_asset
#: constraint:account.move.line:0
msgid ""
"The date of your Journal Entry is not in the defined period! You should "
"change the date or remove this constraint from the journal."
msgstr ""
#. module: account_asset
#: field:account.asset.asset,note:0 field:account.asset.category,note:0
#: field:account.asset.history,note:0
msgid "Note"
msgstr ""
#. module: account_asset
#: help:account.asset.asset,method:0 help:account.asset.category,method:0
msgid ""
"Choose the method to use to compute the amount of depreciation lines.\n"
" * Linear: Calculated on basis of: Gross Value / Number of Depreciations\n"
" * Degressive: Calculated on basis of: Remaining Value * Degressive Factor"
msgstr ""
#. module: account_asset
#: help:account.asset.asset,method_time:0
#: help:account.asset.category,method_time:0
msgid ""
"Choose the method to use to compute the dates and number of depreciation "
"lines.\n"
" * Number of Depreciations: Fix the number of depreciation lines and the "
"time between 2 depreciations.\n"
" * Ending Date: Choose the time between 2 depreciations and the date the "
"depreciations won't go beyond."
msgstr ""
#. module: account_asset
#: view:asset.asset.report:0
msgid "Assets in running state"
msgstr ""
#. module: account_asset
#: view:account.asset.asset:0
msgid "Closed"
msgstr ""
#. module: account_asset
#: field:account.asset.asset,partner_id:0
#: field:asset.asset.report,partner_id:0
msgid "Partner"
msgstr ""
#. module: account_asset
#: view:asset.asset.report:0 field:asset.asset.report,depreciation_value:0
msgid "Amount of Depreciation Lines"
msgstr ""
#. module: account_asset
#: view:asset.asset.report:0
msgid "Posted depreciation lines"
msgstr ""
#. module: account_asset
#: constraint:account.move.line:0
msgid "Company must be the same for its related account and period."
msgstr ""
#. module: account_asset
#: field:account.asset.asset,child_ids:0
msgid "Children Assets"
msgstr ""
#. module: account_asset
#: view:asset.asset.report:0
msgid "Date of depreciation"
msgstr ""
#. module: account_asset
#: field:account.asset.history,user_id:0
msgid "User"
msgstr ""
#. module: account_asset
#: field:account.asset.history,date:0
msgid "Date"
msgstr ""
#. module: account_asset
#: view:asset.asset.report:0
msgid "Assets purchased in current month"
msgstr ""
#. module: account_asset
#: constraint:account.move.line:0
msgid "You can not create journal items on an account of type view."
msgstr ""
#. module: account_asset
#: view:asset.asset.report:0
msgid "Extended Filters..."
msgstr ""
#. module: account_asset
#: view:account.asset.asset:0 view:asset.depreciation.confirmation.wizard:0
msgid "Compute"
msgstr ""
#. module: account_asset
#: view:account.asset.category:0
msgid "Search Asset Category"
msgstr ""
#. module: account_asset
#: model:ir.model,name:account_asset.model_asset_depreciation_confirmation_wizard
msgid "asset.depreciation.confirmation.wizard"
msgstr ""
#. module: account_asset
#: field:account.asset.asset,active:0
msgid "Active"
msgstr ""
#. module: account_asset
#: model:ir.actions.wizard,name:account_asset.wizard_asset_close
msgid "Close asset"
msgstr ""
#. module: account_asset
#: field:account.asset.depreciation.line,parent_state:0
msgid "State of Asset"
msgstr ""
#. module: account_asset
#: field:account.asset.depreciation.line,name:0
msgid "Depreciation Name"
msgstr ""
#. module: account_asset
#: view:account.asset.asset:0 field:account.asset.asset,history_ids:0
msgid "History"
msgstr ""
#. module: account_asset
#: sql_constraint:account.invoice:0
msgid "Invoice Number must be unique per Company!"
msgstr ""
#. module: account_asset
#: field:asset.depreciation.confirmation.wizard,period_id:0
msgid "Period"
msgstr ""
#. module: account_asset
#: view:account.asset.asset:0
msgid "General"
msgstr ""
#. module: account_asset
#: field:account.asset.asset,prorata:0 field:account.asset.category,prorata:0
msgid "Prorata Temporis"
msgstr ""
#. module: account_asset
#: view:account.asset.category:0
msgid "Accounting information"
msgstr ""
#. module: account_asset
#: model:ir.model,name:account_asset.model_account_invoice
msgid "Invoice"
msgstr ""
#. module: account_asset
#: model:ir.actions.act_window,name:account_asset.action_account_asset_asset_form_normal
msgid "Review Asset Categories"
msgstr ""
#. module: account_asset
#: view:asset.depreciation.confirmation.wizard:0 view:asset.modify:0
msgid "Cancel"
msgstr ""
#. module: account_asset
#: selection:account.asset.asset,state:0 selection:asset.asset.report,state:0
msgid "Close"
msgstr ""
#. module: account_asset
#: view:account.asset.asset:0 view:account.asset.category:0
msgid "Depreciation Method"
msgstr ""
#. module: account_asset
#: field:account.asset.asset,purchase_date:0 view:asset.asset.report:0
#: field:asset.asset.report,purchase_date:0
msgid "Purchase Date"
msgstr ""
#. module: account_asset
#: selection:account.asset.asset,method:0
#: selection:account.asset.category,method:0
msgid "Degressive"
msgstr ""
#. module: account_asset
#: help:asset.depreciation.confirmation.wizard,period_id:0
msgid ""
"Choose the period for which you want to automatically post the depreciation "
"lines of running assets"
msgstr ""
#. module: account_asset
#: view:account.asset.asset:0
msgid "Current"
msgstr ""
#. module: account_asset
#: field:account.asset.depreciation.line,remaining_value:0
msgid "Amount to Depreciate"
msgstr ""
#. module: account_asset
#: field:account.asset.category,open_asset:0
msgid "Skip Draft State"
msgstr ""
#. module: account_asset
#: view:account.asset.asset:0 view:account.asset.category:0
#: view:account.asset.history:0
msgid "Depreciation Dates"
msgstr ""
#. module: account_asset
#: field:account.asset.asset,currency_id:0
msgid "Currency"
msgstr ""
#. module: account_asset
#: field:account.asset.category,journal_id:0
msgid "Journal"
msgstr ""
#. module: account_asset
#: field:account.asset.depreciation.line,depreciated_value:0
msgid "Amount Already Depreciated"
msgstr ""
#. module: account_asset
#: field:account.asset.depreciation.line,move_check:0
#: view:asset.asset.report:0 field:asset.asset.report,move_check:0
msgid "Posted"
msgstr ""
#. module: account_asset
#: help:account.asset.asset,state:0
msgid ""
"When an asset is created, the state is 'Draft'.\n"
"If the asset is confirmed, the state goes in 'Running' and the depreciation "
"lines can be posted in the accounting.\n"
"You can manually close an asset when the depreciation is over. If the last "
"line of depreciation is posted, the asset automatically goes in that state."
msgstr ""
#. module: account_asset
#: field:account.asset.category,name:0
msgid "Name"
msgstr ""
#. module: account_asset
#: help:account.asset.category,open_asset:0
msgid ""
"Check this if you want to automatically confirm the assets of this category "
"when created by invoices."
msgstr ""
#. module: account_asset
#: view:account.asset.asset:0
msgid "Set to Draft"
msgstr ""
#. module: account_asset
#: selection:account.asset.asset,method:0
#: selection:account.asset.category,method:0
msgid "Linear"
msgstr ""
#. module: account_asset
#: view:asset.asset.report:0
msgid "Month-1"
msgstr ""
#. module: account_asset
#: model:ir.model,name:account_asset.model_account_asset_depreciation_line
msgid "Asset depreciation line"
msgstr ""
#. module: account_asset
#: field:account.asset.asset,category_id:0 view:account.asset.category:0
#: field:asset.asset.report,asset_category_id:0
#: model:ir.model,name:account_asset.model_account_asset_category
msgid "Asset category"
msgstr ""
#. module: account_asset
#: view:asset.asset.report:0
msgid "Assets purchased in last month"
msgstr ""
#. module: account_asset
#: code:addons/account_asset/wizard/wizard_asset_compute.py:49
#, python-format
msgid "Created Asset Moves"
msgstr ""
#. module: account_asset
#: constraint:account.move.line:0
msgid "You can not create journal items on closed account."
msgstr ""
#. module: account_asset
#: model:ir.actions.act_window,help:account_asset.action_asset_asset_report
msgid ""
"From this report, you can have an overview on all depreciation. The tool "
"search can also be used to personalise your Assets reports and so, match "
"this analysis to your needs;"
msgstr ""
#. module: account_asset
#: help:account.asset.category,method_period:0
msgid "State here the time between 2 depreciations, in months"
msgstr ""
#. module: account_asset
#: field:account.asset.asset,method_number:0
#: selection:account.asset.asset,method_time:0
#: field:account.asset.category,method_number:0
#: selection:account.asset.category,method_time:0
#: field:account.asset.history,method_number:0
#: selection:account.asset.history,method_time:0
#: field:asset.modify,method_number:0
msgid "Number of Depreciations"
msgstr ""
#. module: account_asset
#: view:account.asset.asset:0
msgid "Create Move"
msgstr ""
#. module: account_asset
#: view:asset.depreciation.confirmation.wizard:0
msgid "Post Depreciation Lines"
msgstr ""
#. module: account_asset
#: view:account.asset.asset:0
msgid "Confirm Asset"
msgstr ""
#. module: account_asset
#: model:ir.actions.act_window,name:account_asset.action_account_asset_asset_tree
#: model:ir.ui.menu,name:account_asset.menu_action_account_asset_asset_tree
msgid "Asset Hierarchy"
msgstr ""
@@ -0,0 +1,376 @@
# Arabic translation for openobject-addons
# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012
# This file is distributed under the same license as the openobject-addons package.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2012.
#
msgid ""
msgstr ""
"Project-Id-Version: openobject-addons\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2012-02-08 00:35+0000\n"
"PO-Revision-Date: 2012-03-09 13:28+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: Arabic <ar@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: 2012-03-10 04:54+0000\n"
"X-Generator: Launchpad (build 14914)\n"
#. module: account_bank_statement_extensions
#: view:account.bank.statement.line:0
msgid "Search Bank Transactions"
msgstr "بحث عن المعاملات البنكية"
#. module: account_bank_statement_extensions
#: view:account.bank.statement.line:0
#: selection:account.bank.statement.line,state:0
msgid "Confirmed"
msgstr "مصدق عليه"
#. module: account_bank_statement_extensions
#: view:account.bank.statement:0
#: view:account.bank.statement.line:0
msgid "Glob. Id"
msgstr ""
#. module: account_bank_statement_extensions
#: selection:account.bank.statement.line.global,type:0
msgid "CODA"
msgstr ""
#. module: account_bank_statement_extensions
#: field:account.bank.statement.line.global,parent_id:0
msgid "Parent Code"
msgstr "الكود الأم"
#. module: account_bank_statement_extensions
#: view:account.bank.statement.line:0
msgid "Debit"
msgstr "مدين"
#. module: account_bank_statement_extensions
#: view:cancel.statement.line:0
#: model:ir.actions.act_window,name:account_bank_statement_extensions.action_cancel_statement_line
#: model:ir.model,name:account_bank_statement_extensions.model_cancel_statement_line
msgid "Cancel selected statement lines"
msgstr "الغاء الاسطر المحددة في الكشف"
#. module: account_bank_statement_extensions
#: constraint:res.partner.bank:0
msgid "The RIB and/or IBAN is not valid"
msgstr ""
#. module: account_bank_statement_extensions
#: view:account.bank.statement.line:0
msgid "Group By..."
msgstr "تجميع حسب"
#. module: account_bank_statement_extensions
#: field:account.bank.statement.line,state:0
msgid "State"
msgstr "حالة"
#. module: account_bank_statement_extensions
#: view:account.bank.statement.line:0
#: selection:account.bank.statement.line,state:0
msgid "Draft"
msgstr "مسودة"
#. module: account_bank_statement_extensions
#: view:account.bank.statement.line:0
msgid "Statement"
msgstr "كشف حساب"
#. module: account_bank_statement_extensions
#: view:confirm.statement.line:0
#: model:ir.actions.act_window,name:account_bank_statement_extensions.action_confirm_statement_line
#: model:ir.model,name:account_bank_statement_extensions.model_confirm_statement_line
msgid "Confirm selected statement lines"
msgstr ""
#. module: account_bank_statement_extensions
#: report:bank.statement.balance.report:0
#: model:ir.actions.report.xml,name:account_bank_statement_extensions.bank_statement_balance_report
msgid "Bank Statement Balances Report"
msgstr ""
#. module: account_bank_statement_extensions
#: view:cancel.statement.line:0
msgid "Cancel Lines"
msgstr "الغى الأسطر"
#. module: account_bank_statement_extensions
#: view:account.bank.statement.line.global:0
#: model:ir.model,name:account_bank_statement_extensions.model_account_bank_statement_line_global
msgid "Batch Payment Info"
msgstr ""
#. module: account_bank_statement_extensions
#: view:confirm.statement.line:0
msgid "Confirm Lines"
msgstr "تأكيد الأسطر"
#. module: account_bank_statement_extensions
#: code:addons/account_bank_statement_extensions/account_bank_statement.py:130
#, python-format
msgid ""
"Delete operation not allowed ! Please go to the associated bank "
"statement in order to delete and/or modify this bank statement line"
msgstr ""
"إلغاء العملية غير مسموح! يرجى الذهاب إلى الكشف البنكي المرتبط حتى تتمكن من "
"الغاء اوتغيير اسطر هذا الكشف"
#. module: account_bank_statement_extensions
#: field:account.bank.statement.line.global,type:0
msgid "Type"
msgstr ""
#. module: account_bank_statement_extensions
#: view:account.bank.statement.line:0
#: field:account.bank.statement.line,journal_id:0
#: report:bank.statement.balance.report:0
msgid "Journal"
msgstr "السجل اليومي"
#. module: account_bank_statement_extensions
#: view:account.bank.statement.line:0
msgid "Confirmed Statement Lines."
msgstr ""
#. module: account_bank_statement_extensions
#: view:account.bank.statement.line:0
msgid "Credit Transactions."
msgstr ""
#. module: account_bank_statement_extensions
#: model:ir.actions.act_window,help:account_bank_statement_extensions.action_cancel_statement_line
msgid "cancel selected statement lines."
msgstr ""
#. module: account_bank_statement_extensions
#: field:account.bank.statement.line,counterparty_number:0
msgid "Counterparty Number"
msgstr ""
#. module: account_bank_statement_extensions
#: view:account.bank.statement.line.global:0
msgid "Transactions"
msgstr "المعاملات البنكية"
#. module: account_bank_statement_extensions
#: code:addons/account_bank_statement_extensions/account_bank_statement.py:130
#, python-format
msgid "Warning"
msgstr "تحذير"
#. module: account_bank_statement_extensions
#: report:bank.statement.balance.report:0
msgid "Closing Balance"
msgstr "رصيد الإغلاق"
#. module: account_bank_statement_extensions
#: report:bank.statement.balance.report:0
msgid "Date"
msgstr "التاريخ"
#. module: account_bank_statement_extensions
#: view:account.bank.statement.line:0
#: field:account.bank.statement.line,globalisation_amount:0
msgid "Glob. Amount"
msgstr ""
#. module: account_bank_statement_extensions
#: view:account.bank.statement.line:0
msgid "Debit Transactions."
msgstr "معاملات المدين"
#. module: account_bank_statement_extensions
#: view:account.bank.statement.line:0
msgid "Extended Filters..."
msgstr ""
#. module: account_bank_statement_extensions
#: view:confirm.statement.line:0
msgid "Confirmed lines cannot be changed anymore."
msgstr ""
#. module: account_bank_statement_extensions
#: constraint:res.partner.bank:0
msgid ""
"\n"
"Please define BIC/Swift code on bank for bank type IBAN Account to make "
"valid payments"
msgstr ""
#. module: account_bank_statement_extensions
#: field:account.bank.statement.line,val_date:0
msgid "Valuta Date"
msgstr ""
#. module: account_bank_statement_extensions
#: model:ir.actions.act_window,help:account_bank_statement_extensions.action_confirm_statement_line
msgid "Confirm selected statement lines."
msgstr ""
#. module: account_bank_statement_extensions
#: view:cancel.statement.line:0
msgid "Are you sure you want to cancel the selected Bank Statement lines ?"
msgstr ""
#. module: account_bank_statement_extensions
#: report:bank.statement.balance.report:0
msgid "Name"
msgstr ""
#. module: account_bank_statement_extensions
#: selection:account.bank.statement.line.global,type:0
msgid "ISO 20022"
msgstr ""
#. module: account_bank_statement_extensions
#: view:account.bank.statement.line:0
msgid "Notes"
msgstr ""
#. module: account_bank_statement_extensions
#: selection:account.bank.statement.line.global,type:0
msgid "Manual"
msgstr ""
#. module: account_bank_statement_extensions
#: view:account.bank.statement.line:0
msgid "Credit"
msgstr ""
#. module: account_bank_statement_extensions
#: field:account.bank.statement.line.global,amount:0
msgid "Amount"
msgstr ""
#. module: account_bank_statement_extensions
#: view:account.bank.statement.line:0
msgid "Fin.Account"
msgstr ""
#. module: account_bank_statement_extensions
#: field:account.bank.statement.line,counterparty_currency:0
msgid "Counterparty Currency"
msgstr ""
#. module: account_bank_statement_extensions
#: field:account.bank.statement.line,counterparty_bic:0
msgid "Counterparty BIC"
msgstr ""
#. module: account_bank_statement_extensions
#: field:account.bank.statement.line.global,child_ids:0
msgid "Child Codes"
msgstr ""
#. module: account_bank_statement_extensions
#: view:confirm.statement.line:0
msgid "Are you sure you want to confirm the selected Bank Statement lines ?"
msgstr ""
#. module: account_bank_statement_extensions
#: constraint:account.bank.statement.line:0
msgid ""
"The amount of the voucher must be the same amount as the one on the "
"statement line"
msgstr ""
#. module: account_bank_statement_extensions
#: help:account.bank.statement.line,globalisation_id:0
msgid ""
"Code to identify transactions belonging to the same globalisation level "
"within a batch payment"
msgstr ""
#. module: account_bank_statement_extensions
#: view:account.bank.statement.line:0
msgid "Draft Statement Lines."
msgstr ""
#. module: account_bank_statement_extensions
#: view:account.bank.statement.line:0
msgid "Glob. Am."
msgstr ""
#. module: account_bank_statement_extensions
#: model:ir.model,name:account_bank_statement_extensions.model_account_bank_statement_line
msgid "Bank Statement Line"
msgstr ""
#. module: account_bank_statement_extensions
#: field:account.bank.statement.line.global,code:0
msgid "Code"
msgstr ""
#. module: account_bank_statement_extensions
#: field:account.bank.statement.line,counterparty_name:0
msgid "Counterparty Name"
msgstr ""
#. module: account_bank_statement_extensions
#: field:account.bank.statement.line.global,name:0
msgid "Communication"
msgstr ""
#. module: account_bank_statement_extensions
#: model:ir.model,name:account_bank_statement_extensions.model_res_partner_bank
msgid "Bank Accounts"
msgstr ""
#. module: account_bank_statement_extensions
#: constraint:account.bank.statement:0
msgid "The journal and period chosen have to belong to the same company."
msgstr ""
#. module: account_bank_statement_extensions
#: model:ir.model,name:account_bank_statement_extensions.model_account_bank_statement
msgid "Bank Statement"
msgstr ""
#. module: account_bank_statement_extensions
#: view:account.bank.statement.line:0
msgid "Statement Line"
msgstr ""
#. module: account_bank_statement_extensions
#: sql_constraint:account.bank.statement.line.global:0
msgid "The code must be unique !"
msgstr ""
#. module: account_bank_statement_extensions
#: field:account.bank.statement.line.global,bank_statement_line_ids:0
#: model:ir.actions.act_window,name:account_bank_statement_extensions.action_bank_statement_line
#: model:ir.ui.menu,name:account_bank_statement_extensions.bank_statement_line
msgid "Bank Statement Lines"
msgstr ""
#. module: account_bank_statement_extensions
#: view:account.bank.statement.line.global:0
msgid "Child Batch Payments"
msgstr ""
#. module: account_bank_statement_extensions
#: view:cancel.statement.line:0
#: view:confirm.statement.line:0
msgid "Cancel"
msgstr ""
#. module: account_bank_statement_extensions
#: view:account.bank.statement.line:0
msgid "Statement Lines"
msgstr ""
#. module: account_bank_statement_extensions
#: view:account.bank.statement.line:0
msgid "Total Amount"
msgstr ""
#. module: account_bank_statement_extensions
#: field:account.bank.statement.line,globalisation_id:0
msgid "Globalisation ID"
msgstr ""
@@ -8,14 +8,14 @@ msgstr ""
"Project-Id-Version: openobject-addons\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2012-02-08 00:35+0000\n"
"PO-Revision-Date: 2012-03-07 18:50+0000\n"
"PO-Revision-Date: 2012-03-13 16:55+0000\n"
"Last-Translator: Milan Milosevic <Unknown>\n"
"Language-Team: Serbian Latin <sr@latin@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: 2012-03-08 04:43+0000\n"
"X-Generator: Launchpad (build 14914)\n"
"X-Launchpad-Export-Date: 2012-03-14 04:43+0000\n"
"X-Generator: Launchpad (build 14933)\n"
#. module: account_bank_statement_extensions
#: view:account.bank.statement.line:0
@@ -274,7 +274,7 @@ msgstr "Podređeni kodovi"
#. module: account_bank_statement_extensions
#: view:confirm.statement.line:0
msgid "Are you sure you want to confirm the selected Bank Statement lines ?"
msgstr "Da li zaista želite da izabrane redove izvoda banke?"
msgstr "Da li zaista želite sa potvrdite izabrane redove izvoda banke?"
#. module: account_bank_statement_extensions
#: constraint:account.bank.statement.line:0
@@ -300,82 +300,82 @@ msgstr "Nacrt redova izvoda."
#. module: account_bank_statement_extensions
#: view:account.bank.statement.line:0
msgid "Glob. Am."
msgstr ""
msgstr "Glob.izn."
#. module: account_bank_statement_extensions
#: model:ir.model,name:account_bank_statement_extensions.model_account_bank_statement_line
msgid "Bank Statement Line"
msgstr ""
msgstr "Red izvoda banke"
#. module: account_bank_statement_extensions
#: field:account.bank.statement.line.global,code:0
msgid "Code"
msgstr ""
msgstr "Kod"
#. module: account_bank_statement_extensions
#: field:account.bank.statement.line,counterparty_name:0
msgid "Counterparty Name"
msgstr ""
msgstr "Ime druge strane"
#. module: account_bank_statement_extensions
#: field:account.bank.statement.line.global,name:0
msgid "Communication"
msgstr ""
msgstr "Komunikacija"
#. module: account_bank_statement_extensions
#: model:ir.model,name:account_bank_statement_extensions.model_res_partner_bank
msgid "Bank Accounts"
msgstr ""
msgstr "Bankovni računi"
#. module: account_bank_statement_extensions
#: constraint:account.bank.statement:0
msgid "The journal and period chosen have to belong to the same company."
msgstr ""
msgstr "Dnevnik i izabrani period treba da pripadaju istom preduzeću."
#. module: account_bank_statement_extensions
#: model:ir.model,name:account_bank_statement_extensions.model_account_bank_statement
msgid "Bank Statement"
msgstr ""
msgstr "Izvod banke"
#. module: account_bank_statement_extensions
#: view:account.bank.statement.line:0
msgid "Statement Line"
msgstr ""
msgstr "Red izvoda"
#. module: account_bank_statement_extensions
#: sql_constraint:account.bank.statement.line.global:0
msgid "The code must be unique !"
msgstr ""
msgstr "Kod mora biti jedinstven !"
#. module: account_bank_statement_extensions
#: field:account.bank.statement.line.global,bank_statement_line_ids:0
#: model:ir.actions.act_window,name:account_bank_statement_extensions.action_bank_statement_line
#: model:ir.ui.menu,name:account_bank_statement_extensions.bank_statement_line
msgid "Bank Statement Lines"
msgstr ""
msgstr "Redovi izvoda banke"
#. module: account_bank_statement_extensions
#: view:account.bank.statement.line.global:0
msgid "Child Batch Payments"
msgstr ""
msgstr "Podređena grupna plaćanja"
#. module: account_bank_statement_extensions
#: view:cancel.statement.line:0
#: view:confirm.statement.line:0
msgid "Cancel"
msgstr ""
msgstr "Otkaži"
#. module: account_bank_statement_extensions
#: view:account.bank.statement.line:0
msgid "Statement Lines"
msgstr ""
msgstr "Redovi izvoda"
#. module: account_bank_statement_extensions
#: view:account.bank.statement.line:0
msgid "Total Amount"
msgstr ""
msgstr "Ukupan iznos"
#. module: account_bank_statement_extensions
#: field:account.bank.statement.line,globalisation_id:0
msgid "Globalisation ID"
msgstr ""
msgstr "ID globalizacije"
+23
View File
@@ -0,0 +1,23 @@
# Lithuanian translation for openobject-addons
# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012
# This file is distributed under the same license as the openobject-addons package.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2012.
#
msgid ""
msgstr ""
"Project-Id-Version: openobject-addons\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2012-02-08 00:35+0000\n"
"PO-Revision-Date: 2012-03-09 15:21+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: Lithuanian <lt@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: 2012-03-10 04:54+0000\n"
"X-Generator: Launchpad (build 14914)\n"
#. module: account_cancel
#: view:account.invoice:0
msgid "Cancel"
msgstr "Atšaukti"
+94
View File
@@ -0,0 +1,94 @@
# Gujarati translation for openobject-addons
# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012
# This file is distributed under the same license as the openobject-addons package.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2012.
#
msgid ""
msgstr ""
"Project-Id-Version: openobject-addons\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2012-02-08 00:36+0000\n"
"PO-Revision-Date: 2012-03-12 15:04+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: Gujarati <gu@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: 2012-03-13 05:08+0000\n"
"X-Generator: Launchpad (build 14933)\n"
#. module: base_iban
#: constraint:res.partner.bank:0
msgid ""
"\n"
"Please define BIC/Swift code on bank for bank type IBAN Account to make "
"valid payments"
msgstr ""
#. module: base_iban
#: code:addons/base_iban/base_iban.py:139
#, python-format
msgid "This IBAN does not pass the validation check, please verify it"
msgstr ""
#. module: base_iban
#: model:res.partner.bank.type,format_layout:base_iban.bank_iban
msgid "%(bank_name)s: IBAN %(acc_number)s - BIC %(bank_bic)s"
msgstr ""
#. module: base_iban
#: model:res.partner.bank.type.field,name:base_iban.bank_swift_field
msgid "bank_bic"
msgstr ""
#. module: base_iban
#: model:res.partner.bank.type.field,name:base_iban.bank_zip_field
msgid "zip"
msgstr ""
#. module: base_iban
#: help:res.partner.bank,iban:0
msgid "International Bank Account Number"
msgstr ""
#. module: base_iban
#: model:ir.model,name:base_iban.model_res_partner_bank
msgid "Bank Accounts"
msgstr ""
#. module: base_iban
#: model:res.partner.bank.type.field,name:base_iban.bank_country_field
msgid "country_id"
msgstr ""
#. module: base_iban
#: code:addons/base_iban/base_iban.py:136
#, python-format
msgid ""
"The IBAN does not seem to be correct. You should have entered something like "
"this %s"
msgstr ""
#. module: base_iban
#: field:res.partner.bank,iban:0
msgid "IBAN"
msgstr ""
#. module: base_iban
#: code:addons/base_iban/base_iban.py:140
#, python-format
msgid "The IBAN is invalid, it should begin with the country code"
msgstr ""
#. module: base_iban
#: model:res.partner.bank.type,name:base_iban.bank_iban
msgid "IBAN Account"
msgstr ""
#. module: base_iban
#: constraint:res.partner.bank:0
msgid "The RIB and/or IBAN is not valid"
msgstr ""
#~ msgid "Invalid XML for View Architecture!"
#~ msgstr "દ્રશ્ય બંધારણ માટે અમાન્ય એક્સ.એમ.એલ!"
+2 -8
View File
@@ -267,7 +267,7 @@ class crm_base(object):
data = {'state': 'open', 'active': True}
if not case.user_id:
data['user_id'] = uid
self.write(cr, uid, case.id, data)
self.write(cr, uid, [case.id], data)
self._action(cr, uid, cases, 'open')
return True
@@ -393,12 +393,11 @@ class crm_case(crm_base):
def case_open(self, cr, uid, ids, *args):
"""Opens Case"""
cases = self.browse(cr, uid, ids)
self.message_append(cr, uid, cases, _('Open'))
for case in cases:
data = {'state': 'open', 'active': True }
if not case.user_id:
data['user_id'] = uid
self.write(cr, uid, case.id, data)
self.write(cr, uid, [case.id], data)
self._action(cr, uid, cases, 'open')
return True
@@ -406,7 +405,6 @@ class crm_case(crm_base):
"""Closes Case"""
cases = self.browse(cr, uid, ids)
cases[0].state # to fill the browse record cache
self.message_append(cr, uid, cases, _('Close'))
self.write(cr, uid, ids, {'state': 'done',
'date_closed': time.strftime('%Y-%m-%d %H:%M:%S'),
})
@@ -430,7 +428,6 @@ class crm_case(crm_base):
raise osv.except_osv(_('Error !'), _('You can not escalate, you are already at the top level regarding your sales-team category.'))
self.write(cr, uid, [case.id], data)
cases = self.browse(cr, uid, ids)
self.message_append(cr, uid, cases, _('Escalate'))
self._action(cr, uid, cases, 'escalate')
return True
@@ -438,7 +435,6 @@ class crm_case(crm_base):
"""Cancels Case"""
cases = self.browse(cr, uid, ids)
cases[0].state # to fill the browse record cache
self.message_append(cr, uid, cases, _('Cancel'))
self.write(cr, uid, ids, {'state': 'cancel',
'active': True})
self._action(cr, uid, cases, 'cancel')
@@ -451,7 +447,6 @@ class crm_case(crm_base):
"""Marks case as pending"""
cases = self.browse(cr, uid, ids)
cases[0].state # to fill the browse record cache
self.message_append(cr, uid, cases, _('Pending'))
self.write(cr, uid, ids, {'state': 'pending', 'active': True})
self._action(cr, uid, cases, 'pending')
return True
@@ -463,7 +458,6 @@ class crm_case(crm_base):
state = 'open'
cases = self.browse(cr, uid, ids)
cases[0].state # to fill the browse record cache
self.message_append(cr, uid, cases, _('Draft'))
self.write(cr, uid, ids, {'state': state, 'active': True})
self._action(cr, uid, cases, state)
return True
+24 -21
View File
@@ -304,7 +304,10 @@
<field name="company_currency"/>
</t>
</td>
<td valign="top" width="22"><img t-att-src="kanban_gravatar(record.user_email.value, 22)" class="oe_kanban_gravatar" t-att-title="record.user_id.value"/></td>
<td valign="top" width="22">
<img t-att-src="kanban_image('res.users', 'avatar_mini', record.user_id.raw_value[0])" t-att-title="record.user_id.value"
width="22" height="22" class="oe_kanban_gravatar"/>
</td>
</tr>
</table>
@@ -547,27 +550,27 @@
</group>
</page>
<page string="Communication &amp; History" groups="base.group_extended">
<group colspan="4">
<group colspan="4">
<field colspan="4" name="email_cc" widget="char" size="512"/>
</group>
<field name="message_ids" colspan="4" nolabel="1" mode="tree" readonly="1">
<tree string="History">
<field name="display_text" string="History Information"/>
<field name="email_from" invisible="1"/>
<button
string="Reply" attrs="{'invisible': [('email_from', '=', False)]}"
name="%(mail.action_email_compose_message_wizard)d"
context="{'mail.compose.message.mode':'reply', 'message_id':active_id}"
icon="terp-mail-replied" type="action" />
</tree>
</field>
<button string="Add Internal Note"
name="%(crm.action_crm_add_note)d"
context="{'model': 'crm.lead' }"
icon="terp-document-new" type="action" />
<button string="Send New Email"
name="%(mail.action_email_compose_message_wizard)d"
icon="terp-mail-message-new" type="action"/>
</group>
<field name="message_ids" colspan="4" nolabel="1" mode="tree" readonly="1">
<tree string="History">
<field name="display_text" string="History Information"/>
<field name="email_from" invisible="1"/>
<button
string="Reply" attrs="{'invisible': [('email_from', '=', False)]}"
name="%(mail.action_email_compose_message_wizard)d"
context="{'mail.compose.message.mode':'reply', 'message_id':active_id}"
icon="terp-mail-replied" type="action" />
</tree>
</field>
<button string="Add Internal Note"
name="%(crm.action_crm_add_note)d"
context="{'model': 'crm.lead' }"
icon="terp-document-new" type="action" />
<button string="Send New Email"
name="%(mail.action_email_compose_message_wizard)d"
icon="terp-mail-message-new" type="action"/>
</page>
<page string="Extra Info" groups="base.group_extended">
<group col="2" colspan="2">
+4
View File
@@ -0,0 +1,4 @@
CRM kanban avatar specs
=======================
Kanban view of opportunities has been updated to use the newly-introduced avatar_mini field of res.users. The avatar is placed in the right-side of the containing kanban box.
+6
View File
@@ -0,0 +1,6 @@
:orphan:
CRM module documentation
========================
.. include:: index.rst.inc
+8
View File
@@ -0,0 +1,8 @@
CRM Module
''''''''''
.. toctree::
:maxdepth: 1
crm_kanban_avatar
+7 -7
View File
@@ -8,14 +8,14 @@ msgstr ""
"Project-Id-Version: openobject-addons\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2012-02-08 00:36+0000\n"
"PO-Revision-Date: 2012-02-15 20:06+0000\n"
"PO-Revision-Date: 2012-03-12 08:00+0000\n"
"Last-Translator: Carlos @ smile-iberia <Unknown>\n"
"Language-Team: Spanish <es@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: 2012-02-16 05:06+0000\n"
"X-Generator: Launchpad (build 14781)\n"
"X-Launchpad-Export-Date: 2012-03-13 05:08+0000\n"
"X-Generator: Launchpad (build 14933)\n"
#. module: crm_claim
#: field:crm.claim.report,nbr:0
@@ -254,7 +254,7 @@ msgstr "Acción siguiente"
#. module: crm_claim
#: view:crm.claim.report:0
msgid "My Sales Team(s)"
msgstr ""
msgstr "Mi(s) equipo(s) de ventas"
#. module: crm_claim
#: model:crm.case.stage,name:crm_claim.stage_claim3
@@ -475,7 +475,7 @@ msgstr "Junio"
#. module: crm_claim
#: view:res.partner:0
msgid "Partners Claim"
msgstr ""
msgstr "Reclamaciones de empresas"
#. module: crm_claim
#: field:crm.claim,partner_phone:0
@@ -490,7 +490,7 @@ msgstr "Usuario"
#. module: crm_claim
#: field:crm.claim,active:0
msgid "Active"
msgstr ""
msgstr "Activo"
#. module: crm_claim
#: selection:crm.claim.report,month:0
@@ -786,7 +786,7 @@ msgstr "ID"
#. module: crm_claim
#: constraint:res.partner:0
msgid "Error ! You cannot create recursive associated members."
msgstr ""
msgstr "¡Error! No puede crear miembros asociados recursivamente."
#. module: crm_claim
#: view:crm.claim:0
@@ -138,7 +138,7 @@ class crm_lead_forward_to_partner(osv.osv_memory):
if email_to not in new_cc:
new_cc.append(to)
update_vals = {'email_cc' : ', '.join(new_cc) }
lead.write(cr, uid, case.id, update_vals, context=context)
lead.write(cr, uid, [case.id], update_vals, context=context)
return res
def _get_info_body_text(self, cr, uid, lead, context=None):
+49
View File
@@ -0,0 +1,49 @@
# Lithuanian translation for openobject-addons
# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012
# This file is distributed under the same license as the openobject-addons package.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2012.
#
msgid ""
msgstr ""
"Project-Id-Version: openobject-addons\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2012-02-08 00:36+0000\n"
"PO-Revision-Date: 2012-03-09 15:34+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: Lithuanian <lt@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: 2012-03-10 04:54+0000\n"
"X-Generator: Launchpad (build 14914)\n"
#. module: decimal_precision
#: field:decimal.precision,digits:0
msgid "Digits"
msgstr "Skaitmenys"
#. module: decimal_precision
#: model:ir.actions.act_window,name:decimal_precision.action_decimal_precision_form
#: model:ir.ui.menu,name:decimal_precision.menu_decimal_precision_form
msgid "Decimal Accuracy"
msgstr "Dešimtainis tikslumas"
#. module: decimal_precision
#: field:decimal.precision,name:0
msgid "Usage"
msgstr "Naudojimas"
#. module: decimal_precision
#: sql_constraint:decimal.precision:0
msgid "Only one value can be defined for each given usage!"
msgstr ""
#. module: decimal_precision
#: view:decimal.precision:0
msgid "Decimal Precision"
msgstr "Dešimtainis tikslumas"
#. module: decimal_precision
#: model:ir.model,name:decimal_precision.model_decimal_precision
msgid "decimal.precision"
msgstr "decimal.precision"
+1
View File
@@ -37,6 +37,7 @@ It defines a new kind of service products that offers you the possibility to cho
'depends': ['event','sale','sale_crm'],
'update_xml': [
'event_sale_view.xml',
'event_demo.xml',
],
'test':['test/confirm.yml'],
'installable': True,
+25
View File
@@ -0,0 +1,25 @@
<?xml version="1.0"?>
<openerp>
<data>
<record id="event_technical_training" model="event.event">
<field name="name">Technical training in Grand-Rosiere</field>
<field eval="(DateTime.now() + timedelta(15)).strftime('%Y-%m-%d 7:00:00')" name="date_begin"/>
<field eval="(DateTime.now() + timedelta(20)).strftime('%Y-%m-%d 16:00:00')" name="date_end"/>
<field name="type" ref="event.event_type_4"/>
<field name="user_id" ref="base.user_root"/>
</record>
<record id="event_product" model="product.product">
<field name="name">Technical Training</field>
<field name="event_ok">True</field>
<field name="event_type_id" ref="event.event_type_4"/>
<field name="categ_id" ref="product.cat1"/>
<field name="type">service</field>
<field name="list_price">2500.0</field>
<field name="standard_price">1000.0</field>
<field name="supply_method">produce</field>
<field name="uom_id" ref="product.product_uom_unit"/>
<field name="uom_po_id" ref="product.product_uom_unit"/>
</record>
</data>
</openerp>
+21
View File
@@ -0,0 +1,21 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
+40
View File
@@ -0,0 +1,40 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
{
'name': 'Events Sales layout',
'version': '0.1',
'category': 'Tools',
'complexity': "easy",
'description': """
This module ensures the compatibility of the changes made on the sale.order.line form view in event_sale with the sale_layout module (that is replacing the whole field in the view by another one). Its installation is automatically triggered when both modules are installed.
""",
'author': 'OpenERP SA',
'depends': ['event_sale','sale_layout'],
'update_xml': [
'event_sale_layout.xml',
],
'installable': True,
'auto_install':True,
'category': 'Hidden',
}
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
@@ -0,0 +1,18 @@
<openerp>
<data>
<record model="ir.ui.view" id="view_order_form_inherit_1">
<field name="name">event.sale.order</field>
<field name="model">sale.order</field>
<field name="type">form</field>
<field name="priority">10000</field>
<field name="inherit_id" ref="sale.view_order_form" />
<field name="arch" type="xml">
<xpath expr="//field[@name='discount']" position="after">
<field name="event_id" colspan="3" domain="[('type', 'in', list(set((event_type_id,False))))]" attrs="{'invisible': [('event_ok', '=', False)],'required': [('event_ok', '!=', False)]}"/>
<field name="event_type_id" invisible='1'/>
<field name="event_ok" invisible='1'/>
</xpath>
</field>
</record>
</data>
</openerp>
+120
View File
@@ -0,0 +1,120 @@
# French translation for openobject-addons
# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012
# This file is distributed under the same license as the openobject-addons package.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2012.
#
msgid ""
msgstr ""
"Project-Id-Version: openobject-addons\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2012-02-08 00:36+0000\n"
"PO-Revision-Date: 2012-03-12 21:03+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: French <fr@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: 2012-03-13 05:08+0000\n"
"X-Generator: Launchpad (build 14933)\n"
#. module: google_base_account
#: field:res.users,gmail_user:0
msgid "Username"
msgstr "Utilisateur"
#. module: google_base_account
#: model:ir.actions.act_window,name:google_base_account.act_google_login_form
msgid "Google Login"
msgstr ""
#. module: google_base_account
#: code:addons/google_base_account/wizard/google_login.py:29
#, python-format
msgid "Google Contacts Import Error!"
msgstr ""
#. module: google_base_account
#: view:res.users:0
msgid " Synchronization "
msgstr " Synchronisation "
#. module: google_base_account
#: sql_constraint:res.users:0
msgid "You can not have two users with the same login !"
msgstr "Vous ne pouvez pas avoir deux utilisateurs avec le même login !"
#. module: google_base_account
#: code:addons/google_base_account/wizard/google_login.py:75
#, python-format
msgid "Error"
msgstr "Erreur"
#. module: google_base_account
#: view:google.login:0
msgid "Google login"
msgstr "Login Google"
#. module: google_base_account
#: model:ir.model,name:google_base_account.model_res_users
msgid "res.users"
msgstr "res.users"
#. module: google_base_account
#: field:google.login,password:0
msgid "Google Password"
msgstr "Mot de passe Google"
#. module: google_base_account
#: view:google.login:0
msgid "_Cancel"
msgstr "Annuler"
#. module: google_base_account
#: view:res.users:0
msgid "Google Account"
msgstr "Compte Google"
#. module: google_base_account
#: field:google.login,user:0
msgid "Google Username"
msgstr ""
#. module: google_base_account
#: code:addons/google_base_account/wizard/google_login.py:29
#, python-format
msgid ""
"Please install gdata-python-client from http://code.google.com/p/gdata-"
"python-client/downloads/list"
msgstr ""
#. module: google_base_account
#: model:ir.model,name:google_base_account.model_google_login
msgid "Google Contact"
msgstr ""
#. module: google_base_account
#: view:google.login:0
msgid "_Login"
msgstr ""
#. module: google_base_account
#: constraint:res.users:0
msgid "The chosen company is not in the allowed companies for this user"
msgstr "La société choisie n'est pas autorisée pour cet utilisateur."
#. module: google_base_account
#: field:res.users,gmail_password:0
msgid "Password"
msgstr "Mot de passe"
#. module: google_base_account
#: code:addons/google_base_account/wizard/google_login.py:75
#, python-format
msgid "Authentication fail check the user and password !"
msgstr ""
"Echec de l'uthentification, vérifier l'utilisateur et le mot de passe!"
#. module: google_base_account
#: view:google.login:0
msgid "ex: user@gmail.com"
msgstr "ex: nom.prenom@gmail.com"
+8
View File
@@ -0,0 +1,8 @@
HR photo specs
==============
This revision modifies the photo for HR employees. Two fields now exist in the hr.employee model:
- photo, a binary field holding the image
- photo_mini, a binary field holding an automatically resized version of the avatar. Dimensions of the resized avatar are 180x150.
Employee photo should be used only when dealing with employees, using the photo_mini field. When dealing with users, use the res.users avatar_mini field instead.
+6
View File
@@ -0,0 +1,6 @@
:orphan:
HR module documentation
=======================
.. include:: index.rst.inc
+8
View File
@@ -0,0 +1,8 @@
HR Module
'''''''''
.. toctree::
:maxdepth: 1
hr_employee_img
+33 -2
View File
@@ -23,6 +23,9 @@ from osv import fields, osv
import logging
import addons
import io, StringIO
from PIL import Image
class hr_employee_category(osv.osv):
def name_get(self, cr, uid, ids, context=None):
@@ -145,6 +148,30 @@ class hr_employee(osv.osv):
_name = "hr.employee"
_description = "Employee"
_inherits = {'resource.resource': "resource_id"}
def onchange_photo_mini(self, cr, uid, ids, value, context=None):
return {'value': {'photo': value, 'photo_mini': self._photo_resize(cr, uid, value) } }
def _set_photo_mini(self, cr, uid, id, name, value, args, context=None):
return self.write(cr, uid, [id], {'photo': value}, context=context)
def _photo_resize(self, cr, uid, photo, context=None):
image_stream = io.BytesIO(photo.decode('base64'))
img = Image.open(image_stream)
img.thumbnail((180, 150), Image.ANTIALIAS)
img_stream = StringIO.StringIO()
img.save(img_stream, "JPEG")
return img_stream.getvalue().encode('base64')
def _get_photo_mini(self, cr, uid, ids, name, args, context=None):
result = {}
for hr_empl in self.browse(cr, uid, ids, context=context):
if not hr_empl.photo:
result[hr_empl.id] = False
else:
result[hr_empl.id] = self._photo_resize(cr, uid, hr_empl.photo, context=context)
return result
_columns = {
'country_id': fields.many2one('res.country', 'Nationality'),
'birthday': fields.date("Date of Birth"),
@@ -171,6 +198,10 @@ class hr_employee(osv.osv):
'coach_id': fields.many2one('hr.employee', 'Coach'),
'job_id': fields.many2one('hr.job', 'Job'),
'photo': fields.binary('Photo'),
'photo_mini': fields.function(_get_photo_mini, fnct_inv=_set_photo_mini, string='Photo Mini', type="binary",
store = {
'hr.employee': (lambda self, cr, uid, ids, c={}: ids, ['photo'], 10),
}),
'passport_id':fields.char('Passport No', size=64),
'color': fields.integer('Color Index'),
'city': fields.related('address_id', 'city', type='char', string='City'),
@@ -217,11 +248,11 @@ class hr_employee(osv.osv):
def _get_photo(self, cr, uid, context=None):
photo_path = addons.get_module_resource('hr','images','photo.png')
return open(photo_path, 'rb').read().encode('base64')
return self._photo_resize(cr, uid, open(photo_path, 'rb').read().encode('base64'))
_defaults = {
'active': 1,
'photo': _get_photo,
'photo_mini': _get_photo,
'marital': 'single',
'color': 0,
}
File diff suppressed because one or more lines are too long
+2 -3
View File
@@ -33,7 +33,7 @@
<field name="parent_id" />
</group>
<group colspan="2" col="1">
<field name="photo" widget='image' nolabel="1"/>
<field name="photo_mini" widget='image' nolabel="1" on_change="onchange_photo_mini(photo_mini)"/>
</group>
</group>
<notebook colspan="6">
@@ -130,13 +130,12 @@
<field name="model">hr.employee</field>
<field name="type">kanban</field>
<field name="arch" type="xml">
<kanban>
<templates>
<t t-name="kanban-box">
<div class="oe_employee_vignette">
<div class="oe_employee_image">
<a type="edit"><img t-att-src="kanban_image('hr.employee', 'photo', record.id.value)" class="oe_employee_picture"/></a>
<a type="edit"><img t-att-src="kanban_image('hr.employee', 'photo_mini', record.id.value)" class="oe_employee_picture"/></a>
</div>
<div class="oe_employee_details">
<h4><a type="edit"><field name="name"/> (<field name="login"/>)</a></h4>
+76 -3
View File
@@ -93,6 +93,7 @@ class hr_holidays(osv.osv):
_name = "hr.holidays"
_description = "Leave"
_order = "type desc, date_from asc"
_inherit = ['mail.thread']
def _employee_get(self, cr, uid, context=None):
ids = self.pool.get('hr.employee').search(cr, uid, [('user_id', '=', uid)], context=context)
@@ -150,7 +151,12 @@ class hr_holidays(osv.osv):
('date_check2', "CHECK ( (type='add') OR (date_from <= date_to))", "The start date must be before the end date !"),
('date_check', "CHECK ( number_of_days_temp >= 0 )", "The number of days must be greater than 0 !"),
]
def create(self, cr, uid, vals, context=None):
obj_id = super(hr_holidays, self).create(cr, uid, vals, context=context)
self.create_notificate(cr, uid, [obj_id], context=context)
return obj_id
def _create_resource_leave(self, cr, uid, leaves, context=None):
'''This method will create entry in resource calendar leave object at the time of holidays validated '''
obj_res_leave = self.pool.get('resource.calendar.leaves')
@@ -250,8 +256,9 @@ class hr_holidays(osv.osv):
obj_emp = self.pool.get('hr.employee')
ids2 = obj_emp.search(cr, uid, [('user_id', '=', uid)])
manager = ids2 and ids2[0] or False
self.holidays_validate_notificate(cr, uid, ids, context=context)
return self.write(cr, uid, ids, {'state':'validate1', 'manager_id': manager})
def holidays_validate2(self, cr, uid, ids, context=None):
self.check_holidays(cr, uid, ids, context=context)
obj_emp = self.pool.get('hr.employee')
@@ -301,13 +308,15 @@ class hr_holidays(osv.osv):
wf_service.trg_validate(uid, 'hr.holidays', leave_id, 'validate', cr)
wf_service.trg_validate(uid, 'hr.holidays', leave_id, 'second_validate', cr)
if holiday_ids:
self.holidays_valid2_notificate(self, cr, uid, [holiday_ids], context=context)
self.write(cr, uid, holiday_ids, {'manager_id2': manager})
return True
def holidays_confirm(self, cr, uid, ids, context=None):
self.check_holidays(cr, uid, ids, context=context)
self.holidays_confirm_notificate(cr, uid, ids, context=context)
return self.write(cr, uid, ids, {'state':'confirm'})
def holidays_refuse(self, cr, uid, ids, approval, context=None):
obj_emp = self.pool.get('hr.employee')
ids2 = obj_emp.search(cr, uid, [('user_id', '=', uid)])
@@ -316,6 +325,7 @@ class hr_holidays(osv.osv):
self.write(cr, uid, ids, {'state': 'refuse', 'manager_id': manager})
else:
self.write(cr, uid, ids, {'state': 'refuse', 'manager_id2': manager})
self.holidays_refuse_notificate(cr, uid, ids, approval, context=context)
self.holidays_cancel(cr, uid, ids, context=context)
return True
@@ -343,6 +353,69 @@ class hr_holidays(osv.osv):
if leaves_rest < record.number_of_days_temp:
raise osv.except_osv(_('Warning!'),_('You cannot validate leaves for employee %s: too few remaining days (%s).') % (record.employee_id.name, leaves_rest))
return True
# -----------------------------
# OpenChatter and notifications
# -----------------------------
def get_needaction_user_ids(self, cr, uid, ids, context=None):
result = dict.fromkeys(ids, [])
for obj in self.browse(cr, uid, ids, context=context):
if obj.state == 'confirm' and obj.employee_id.parent_id:
result[obj.id] = [obj.employee_id.parent_id.user_id.id]
elif obj.state == 'validate1' or obj.state == 'validate':
# get group_hr_manager: everyone will be warned of second validation
res = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'base', 'group_hr_manager') or False
obj_id = res and res[1] or False
if obj_id:
hr_manager_group = self.pool.get('res.groups').read(cr, uid, [obj_id], ['users'], context=context)[0]
result[obj.id] = hr_manager_group['users']
return result
def message_get_subscribers(self, cr, uid, ids, context=None):
sub_ids = self.message_get_subscribers_ids(cr, uid, ids, context=context);
# add the employee and its manager if specified to the subscribed users
for obj in self.browse(cr, uid, ids, context=context):
if obj.employee_id.parent_id:
sub_ids.append(obj.employee_id.parent_id.user_id.id)
return self.pool.get('res.users').read(cr, uid, sub_ids, context=context)
def create_notificate(self, cr, uid, ids, context=None):
for obj in self.browse(cr, uid, ids, context=context):
self.message_append_note(cr, uid, ids, _('System notification'),
_("The %s request has been <b>created</b> and is waiting confirmation")
% ('leave' if obj.type == 'remove' else 'allocation',), type='notification', context=context)
return True
def holidays_confirm_notificate(self, cr, uid, ids, context=None):
for obj in self.browse(cr, uid, ids):
self.message_append_note(cr, uid, [obj.id], _('System notification'),
_("The %s request has been <b>confirmed</b> and is waiting for validation by the manager.")
% ('leave' if obj.type == 'remove' else 'allocation',), type='notification')
def holidays_validate_notificate(self, cr, uid, ids, context=None):
for obj in self.browse(cr, uid, ids):
if obj.holiday_status_id.double_validation:
self.message_append_note(cr, uid, [obj.id], _('System notification'),
_("The %s request has been <b>validated</b>. A second validation is necessary and is now pending.")
% ('leave' if obj.type == 'remove' else 'allocation',), type='notification', context=context)
else:
self.message_append_note(cr, uid, [obj.id], _('System notification'),
_("The %s request has been <b>validated</b>. The validation process is now over.")
% ('leave' if obj.type == 'remove' else 'allocation',), type='notification', context=context)
def holidays_valid2_notificate(self, cr, uid, ids, context=None):
for obj in self.browse(cr, uid, ids):
self.message_append_note(cr, uid, [obj.id], _('System notification'),
_("The %s request has been <b>double validated</b>. The validation process is now over.")
% ('leave' if obj.type == 'remove' else 'allocation',), type='notification', context=context)
def holidays_refuse_notificate(self, cr, uid, ids, approval, context=None):
for obj in self.browse(cr, uid, ids):
self.message_append_note(cr, uid, [obj.id], _('System notification'),
_("The %s request has been <b>refused</b>. The validation process is now over.")
% ('leave' if obj.type == 'remove' else 'allocation',), type='notification', context=context)
hr_holidays()
class resource_calendar_leaves(osv.osv):
+1
View File
@@ -95,6 +95,7 @@
<button string="Approved" name="second_validate" states="validate1" type="workflow" icon="gtk-apply" groups="base.group_hr_user"/>
<button string="Set to Draft" name="set_to_draft" states="refuse,validate" type="object" icon="gtk-convert" groups="base.group_hr_user"/>
</group>
<field name="message_ids_social" colspan="4" widget="ThreadView" nolabel="1"/>
</page>
</notebook>
</form>
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -68,7 +68,7 @@ class hr_recruitment_partner_create(osv.osv_memory):
'email': case.email_from
}, context=context)
case_obj.write(cr, uid, case.id, {
case_obj.write(cr, uid, [case.id], {
'partner_id': partner_id,
'partner_address_id': contact_id
}, context=context)
@@ -88,4 +88,4 @@ class hr_recruitment_partner_create(osv.osv_memory):
hr_recruitment_partner_create()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
@@ -516,7 +516,7 @@ class hr_timesheet_line(osv.osv):
store={
'hr_timesheet_sheet.sheet': (_get_hr_timesheet_sheet, ['employee_id', 'date_from', 'date_to'], 10),
'account.analytic.line': (_get_account_analytic_line, ['user_id', 'date'], 10),
'hr.analytic.timesheet': (lambda self,cr,uid,ids,context=None: ids, ['line_id'], 10),
'hr.analytic.timesheet': (lambda self,cr,uid,ids,context=None: ids, None, 10),
},
),
}
@@ -69,6 +69,7 @@
- account_id: account.analytic_sednacom
date: !eval time.strftime('%Y-%m-%d')
name: 'Develop yaml for hr module'
user_id: base.user_qdp
unit_amount: 6.00
amount: -90.00
product_id: product.product_consultant
@@ -96,6 +97,7 @@
date: !eval time.strftime('%Y-%m-%d')
name: 'Develop yaml for hr module'
unit_amount: 2.00
user_id: base.user_qdp
amount: -90.00
product_id: product.product_consultant
general_account_id: account.a_expense
+3
View File
@@ -21,6 +21,9 @@
import mail_message
import mail_thread
import mail_group
import mail_subscription
import res_users
import res_partner
import wizard
+18 -1
View File
@@ -58,7 +58,9 @@ The main features are:
'data': [
"wizard/mail_compose_message_view.xml",
"mail_message_view.xml",
"mail_subscription_view.xml",
"mail_thread_view.xml",
"mail_group_view.xml",
"res_partner_view.xml",
'security/ir.model.access.csv',
'mail_data.xml',
@@ -66,6 +68,21 @@ The main features are:
'installable': True,
'auto_install': False,
'certificate': '001056784984222247309',
'images': ['images/customer_history.jpeg','images/messages_form.jpeg','images/messages_list.jpeg'],
'images': [
'images/customer_history.jpeg',
'images/messages_form.jpeg',
'images/messages_list.jpeg',
'static/src/img/email_icong.png',
],
'css': [
'static/src/css/mail.css',
'static/src/css/mail_group.css',
],
'js': [
'static/src/js/mail.js',
],
'qweb': [
'static/src/xml/mail.xml',
],
}
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
+6
View File
@@ -0,0 +1,6 @@
:orphan:
Mail module documentation
=========================
.. include:: index.rst.inc
+8
View File
@@ -0,0 +1,8 @@
Mail Module
'''''''''''
.. toctree::
:maxdepth: 1
mail_thread
+6
View File
@@ -0,0 +1,6 @@
.. _mail_thread:
OpenChatter
===========
TODO
+116
View File
@@ -0,0 +1,116 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2010-today OpenERP SA (<http://www.openerp.com>)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>
#
##############################################################################
import tools
from osv import osv
from osv import fields
from tools.translate import _
import io, StringIO
from PIL import Image
class mail_group(osv.osv):
"""
A mail_group is a collection of users sharing messages in a discussion
group. Group users are users that follow the mail group, using the
subscription/follow mechanism of OpenSocial.
"""
_name = 'mail.group'
_inherit = ['mail.thread']
def action_group_join(self, cr, uid, ids, context={}):
return self.message_subscribe(cr, uid, ids, context=context);
def action_group_leave(self, cr, uid, ids, context={}):
return self.message_unsubscribe(cr, uid, ids, context=context);
def onchange_photo_mini(self, cr, uid, ids, value, context=None):
return {'value': {'photo': value, 'photo_mini': self._photo_resize(cr, uid, value) } }
def _set_photo_mini(self, cr, uid, id, name, value, args, context=None):
self.write(cr, uid, [id], {'photo': value}, context=context)
return True
def _photo_resize(self, cr, uid, photo, context=None):
image_stream = io.BytesIO(photo.decode('base64'))
img = Image.open(image_stream)
img.thumbnail((120, 100), Image.ANTIALIAS)
img_stream = StringIO.StringIO()
img.save(img_stream, "JPEG")
return img_stream.getvalue().encode('base64')
def _get_photo_mini(self, cr, uid, ids, name, args, context=None):
result = {}
for group in self.browse(cr, uid, ids, context=context):
if not group.photo:
result[group.id] = False
else:
result[group.id] = self._photo_resize(cr, uid, group.photo, context=context)
return result
def is_subscriber(self, cr, uid, ids, name, args, context=None):
result = {}
for id in ids:
result[id] = self.message_is_subscriber(cr, uid, [id], context=context)
return result
def get_messages_nbr(self, cr, uid, ids, name, args, context=None):
result = {}
for id in ids:
result[id] = self.message_get_messages_nbr(cr, uid, [id], context=context)
return result
def get_discussions_nbr(self, cr, uid, ids, name, args, context=None):
result = {}
for id in ids:
result[id] = self.message_get_discussions_nbr(cr, uid, [id], context=context)
return result
def get_members_nbr(self, cr, uid, ids, name, args, context=None):
result = {}
for id in ids:
result[id] = len(self.message_get_subscribers_ids(cr, uid, [id], context=context))
return result
_columns = {
'name': fields.char('Name', size=64, required=True),
'description': fields.text('Description'),
'responsible_id': fields.many2one('res.users', string='Responsible',
ondelete='set null', required=True, select=1),
'public': fields.boolean('Public', help='This group is visible by non members'),
'photo': fields.binary('Photo'),
'photo_mini': fields.function(_get_photo_mini, fnct_inv=_set_photo_mini, string='Photo Mini', type="binary",
store = {
'mail.group': (lambda self, cr, uid, ids, c={}: ids, ['photo'], 10),
}),
'joined': fields.function(is_subscriber, type='boolean', string='Joined'),
'messages_nbr': fields.function(get_messages_nbr, type='integer', string='Messages count'),
'discussions_nbr': fields.function(get_discussions_nbr, type='integer', string='Discussions count'),
'members_nbr': fields.function(get_members_nbr, type='integer', string='Members count'),
}
_defaults = {
'public': True,
'responsible_id': (lambda s, cr, uid, ctx: uid),
}
mail_group()
+117
View File
@@ -0,0 +1,117 @@
<?xml version="1.0"?>
<openerp>
<data>
<!-- Group Kanban View !-->
<record model="ir.ui.view" id="view_group_kanban">
<field name="name">mail.group.kanban</field>
<field name="model">mail.group</field>
<field name="type">kanban</field>
<field name="priority" eval="10"/>
<field name="arch" type="xml">
<kanban>
<templates>
<t t-name="kanban-description">
<div class="oe_group_description" t-if="record.description.raw_value">
<field name="description"/>
</div>
</t>
<t t-name="kanban-box">
<div t-attf-class="{record.joined.raw_value} oe_group_vignette">
<div class="oe_group_image">
<a type="edit"><img t-att-src="kanban_image('mail.group', 'photo', record.id.value)" class="oe_group_photo" tooltip="kanban-description"/></a>
</div>
<div class="oe_group_details">
<h4><a type="edit"><field name="name"/></a></h4>
<span style="display: none;"><field name="joined"/></span>
<ul>
<li><field name="members_nbr"/> members</li>
<li t-if="record.joined.raw_value"><b>Joined</b></li>
<li t-if="! record.joined.raw_value"><a name="action_group_join" string="Join" type="object">Join</a></li>
<li t-if="record.joined.raw_value"><a name="action_group_leave" string="Join" type="object">Leave</a></li>
<li><field name="messages_nbr"/> messages in <field name="discussions_nbr"/> discussions</li>
</ul>
</div>
</div>
<script>
$('.oe_group_photo').load(function() { if($(this).width() > $(this).height()) { $(this).addClass('oe_group_photo_wide') } });
</script>
</t>
</templates>
</kanban>
</field>
</record>
<!-- Group Form View !-->
<record model="ir.ui.view" id="view_group_form">
<field name="name">mail.group.form</field>
<field name="model">mail.group</field>
<field name="type">form</field>
<field name="priority" eval="10"/>
<field name="arch" type="xml">
<form string="Group">
<group colspan="4" col="8">
<group colspan="6" col="4">
<separator string="General information" colspan="4"/>
<field name="name" colspan="2"/>
<field name="responsible_id" colspan="2"/>
<newline/>
<field name="description" colspan="4"/>
</group>
<group colspan="1" col="2">
<separator string="Group image" colspan="2"/>
<field name="photo_mini" widget='image' nolabel="1" on_change="onchange_photo_mini(photo_mini)"/>
</group>
<group colspan="1" col="2">
<separator string="Privacy settings" colspan="2"/>
<field name="public" nolabel="1"/>
<label string="This group is visible by non members" colspan="2"/>
</group>
</group>
<field name="message_ids_social" colspan="4" widget="ThreadView" nolabel="1"/>
</form>
</field>
</record>
<!-- Group List View !-->
<record model="ir.ui.view" id="view_group_tree">
<field name="name">mail.group.tree</field>
<field name="model">mail.group</field>
<field name="type">tree</field>
<field name="priority" eval="10"/>
<field name="arch" type="xml">
<tree string="Groups">
<field name="name" colspan="2"/>
<field name="responsible_id" colspan="2"/>
</tree>
</field>
</record>
<!-- Group Search View !-->
<record model="ir.ui.view" id="view_group_search">
<field name="name">mail.group.search</field>
<field name="model">mail.group</field>
<field name="type">search</field>
<field name="priority" eval="10"/>
<field name="arch" type="xml">
<search string="Search groups">
<field name="name" colspan="2"/>
<field name="responsible_id" colspan="2"/>
</search>
</field>
</record>
<!-- group record !-->
<record id="action_view_groups" model="ir.actions.act_window">
<field name="name">Groups</field>
<field name="res_model">mail.group</field>
<field name="view_type">form</field>
<field name="view_mode">kanban,tree,form</field>
<field name="search_view_id" ref="view_group_search"/>
</record>
<!-- left-side menu: Groups !-->
<menuitem id="mail_groups" name="Groups" sequence="15" parent="mail_feeds_main"/>
<menuitem id="mail_allgroups" name="All groups" parent="mail_groups" action="action_view_groups"/>
</data>
</openerp>
+27 -11
View File
@@ -2,7 +2,7 @@
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2010-2011 OpenERP SA (<http://www.openerp.com>)
# Copyright (C) 2010-today OpenERP SA (<http://www.openerp.com>)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
@@ -26,6 +26,7 @@ import email
import logging
import re
import time
import datetime
from email.header import decode_header
from email.message import Message
@@ -71,10 +72,10 @@ class mail_message_common(osv.osv_memory):
_rec_name = 'subject'
_columns = {
'subject': fields.char('Subject', size=512, required=True),
'model': fields.char('Related Document model', size=128, select=1, readonly=1),
'res_id': fields.integer('Related Document ID', select=1, readonly=1),
'model': fields.char('Related Document model', size=128, select=1),
'res_id': fields.integer('Related Document ID', select=1),
'date': fields.datetime('Date'),
'email_from': fields.char('From', size=128, help='Message sender, taken from user preferences. If empty, this is not a mail but a message.'),
'email_from': fields.char('From', size=128, help='Message sender, taken from user preferences.'),
'email_to': fields.char('To', size=256, help='Message recipients'),
'email_cc': fields.char('Cc', size=256, help='Carbon copy message recipients'),
'email_bcc': fields.char('Bcc', size=256, help='Blind carbon copy message recipients'),
@@ -88,15 +89,19 @@ class mail_message_common(osv.osv_memory):
"select plaintext or rich text contents accordingly", readonly=1),
'body_text': fields.text('Text contents', help="Plain-text version of the message"),
'body_html': fields.text('Rich-text contents', help="Rich-text/HTML version of the message"),
'parent_id': fields.many2one('mail.message', 'Parent message', help="Parent message, used for displaying as threads with hierarchy"),
}
_defaults = {
'subtype': 'plain'
'subtype': 'plain',
'date': (lambda *a: fields.datetime.now()),
}
class mail_message(osv.osv):
'''Model holding RFC2822 email messages, and providing facilities
to parse, queue and send new messages
'''Model holding messages: system notification (replacing res.log
notifications), comments (for OpenSocial feature) and
RFC2822 email messages. This model also provides facilities to
parse, queue and send new email messages.
Messages that do not have a value for the email_from column
are simple log messages (e.g. document state changes), while
@@ -107,7 +112,7 @@ class mail_message(osv.osv):
_name = 'mail.message'
_inherit = 'mail.message.common'
_description = 'Email Message'
_description = 'Generic Message (Email, Comment, Notification)'
_order = 'date desc'
# XXX to review - how to determine action to use?
@@ -160,7 +165,7 @@ class mail_message(osv.osv):
msg_txt += (message.subject or '')
result[message.id] = msg_txt
return result
_columns = {
'partner_id': fields.many2one('res.partner', 'Related partner'),
'user_id': fields.many2one('res.users', 'Related user', readonly=1),
@@ -176,12 +181,23 @@ class mail_message(osv.osv):
], 'State', readonly=True),
'auto_delete': fields.boolean('Auto Delete', help="Permanently delete this email after sending it, to save space"),
'original': fields.binary('Original', help="Original version of the message, as it was sent on the network", readonly=1),
'type': fields.selection([
('email', 'e-mail'),
('comment', 'Comment'),
('notification', 'System notification'),
], 'Type', help="Message type: e-mail for e-mail message, notification for system message, comment for other messages such as user replies"),
}
_defaults = {
'state': 'received',
'type': 'comment',
}
#------------------------------------------------------
# E-Mail api
#------------------------------------------------------
def init(self, cr):
cr.execute("""SELECT indexname FROM pg_indexes WHERE indexname = 'mail_message_model_res_id_idx'""")
if not cr.fetchone():
+77 -1
View File
@@ -1,6 +1,62 @@
<?xml version="1.0"?>
<openerp>
<data>
<!-- mail.message tree: short view !-->
<record model="ir.ui.view" id="view_message_tree_short">
<field name="name">mail.message.tree.short</field>
<field name="model">mail.message</field>
<field name="type">tree</field>
<field name="priority">20</field>
<field name="arch" type="xml">
<tree string="Messages">
<field name="date"/>
<field name="subject"/>
<field name="user_id"/>
<field name="model"/>
<field name="res_id"/>
</tree>
</field>
</record>
<!-- mail.message form: short view !-->
<record model="ir.ui.view" id="view_message_form_short">
<field name="name">mail.message.form.short</field>
<field name="model">mail.message</field>
<field name="type">form</field>
<field name="priority">20</field>
<field name="arch" type="xml">
<form string="Message">
<group colspan="2" col="2">
<field name="subject"/>
<field name="date"/>
<field name="type"/>
<field name="body_text"/>
</group>
<group colspan="2" col="2">
<field name="user_id" string="User" readonly="0"/>
<field name="model"/>
<field name="res_id"/>
<field name="parent_id"/>
</group>
</form>
</field>
</record>
<!-- mail.message search: short view !-->
<record model="ir.ui.view" id="view_message_search_short">
<field name="name">mail.message.search.short</field>
<field name="model">mail.message</field>
<field name="type">search</field>
<field name="priority">20</field>
<field name="arch" type="xml">
<search string="Messages Search">
<field name="user_id"/>
<field name="model"/>
<field name="date"/>
</search>
</field>
</record>
<record model="ir.ui.view" id="view_email_message_form">
<field name="name">mail.message.form</field>
<field name="model">mail.message</field>
@@ -124,12 +180,21 @@
</field>
</record>
<record id="action_view_all_messages_short" model="ir.actions.act_window">
<field name="name">Messages</field>
<field name="res_model">mail.message</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
<field name="context">{'tree_view_ref': 'mail.view_message_tree_short', 'form_view_ref': 'mail.view_message_form_short'}</field>
<field name="search_view_id" ref="view_message_search_short"/>
</record>
<record id="action_view_mail_message" model="ir.actions.act_window">
<field name="name">Messages</field>
<field name="res_model">mail.message</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
<field name="domain">['|',('state','in',['outgoing','exception']),('email_from', '!=', False)]</field>
<field name="domain">[('type', '=', 'email'), '|',('state','in',['outgoing','exception']),('email_from', '!=', False)]</field>
<field name="search_view_id" ref="view_email_message_search"/>
</record>
@@ -145,5 +210,16 @@
parent="base.menu_email"
action="action_view_mail_message" />
<record id="action_mail_all_feeds" model="ir.actions.client">
<field name="name">All Feeds</field>
<field name="tag">mail.all_feeds</field>
<field name="params" eval="{'search_view_id': ref('view_message_search_short')}"/>
</record>
<record id="action_mail_my_feeds" model="ir.actions.client">
<field name="name">My Feeds</field>
<field name="tag">mail.all_feeds</field>
<field name="params" eval="{'search_view_id': ref('view_message_search_short'), 'my_feeds': True}"/>
</record>
</data>
</openerp>
+62
View File
@@ -0,0 +1,62 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2009-today OpenERP SA (<http://www.openerp.com>)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>
#
##############################################################################
from osv import osv
from osv import fields
class mail_subscription(osv.osv):
"""
mail_subscription holds the data related to the follow mechanism inside OpenERP.
A subscription is characterized by:
- res_model: model of the followed objects
- res_id: ID of resource (may be 0 for every objects)
"""
_name = 'mail.subscription'
_rec_name = 'id'
_order = 'res_model asc'
_columns = {
'res_model': fields.char('Related Document Model', size=128,
select=1, required=True),
'res_id': fields.integer('Related Document ID', select=1),
'user_id': fields.many2one('res.users', string='Related User ID',
ondelete='cascade', required=True, select=1),
}
class mail_notification(osv.osv):
"""
mail_notification is a relational table modeling messages pushed to users.
- read: todo
"""
_name = 'mail.notification'
_rec_name = 'id'
_log_access = False
_order = 'message_id desc'
_columns = {
'user_id': fields.many2one('res.users', string='User',
ondelete='cascade', required=True, select=1),
'message_id': fields.many2one('mail.message', string='Message',
ondelete='cascade', required=True, select=1),
'read': fields.boolean('Read'),
# TODO: add a timestamp ? or use message date ?
}
_defaults = {
'read': False,
}
+56
View File
@@ -0,0 +1,56 @@
<?xml version="1.0"?>
<openerp>
<data>
<!--
SUBSCRIPTION
!-->
<record model="ir.ui.view" id="view_subscription_tree">
<field name="name">mail.subscription.tree</field>
<field name="model">mail.subscription</field>
<field name="type">tree</field>
<field name="sequence">10</field>
<field name="arch" type="xml">
<tree string="Subscription">
<field name="res_model"/>
<field name="res_id"/>
<field name="user_id"/>
</tree>
</field>
</record>
<!--
NOTIFICATION
!-->
<record model="ir.ui.view" id="view_notification_tree">
<field name="name">mail.notification.tree</field>
<field name="model">mail.notification</field>
<field name="type">tree</field>
<field name="sequence">10</field>
<field name="arch" type="xml">
<tree string="Subscription">
<field name="user_id"/>
<field name="message_id"/>
<field name="read"/>
</tree>
</field>
</record>
<record id="action_view_subscriptions" model="ir.actions.act_window">
<field name="name">Subscriptions</field>
<field name="res_model">mail.subscription</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
</record>
<record id="action_view_notifications" model="ir.actions.act_window">
<field name="name">Pushed notif</field>
<field name="res_model">mail.notification</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
</record>
</data>
</openerp>
+326 -106
View File
@@ -2,7 +2,7 @@
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2009-Today OpenERP SA (<http://www.openerp.com>)
# Copyright (C) 2009-today OpenERP SA (<http://www.openerp.com>)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
@@ -25,13 +25,14 @@ import base64
import email
from email.utils import parsedate
import re
import logging
import xmlrpclib
from osv import osv, fields
from tools.translate import _
from mail_message import decode, to_email
_logger = logging.getLogger('mail')
_logger = logging.getLogger(__name__)
class mail_thread(osv.osv):
'''Mixin model, meant to be inherited by any model that needs to
@@ -40,24 +41,127 @@ class mail_thread(osv.osv):
name collisions with methods of the models that will inherit
from this mixin.
``mail.thread`` adds a one2many of mail.messages, acting as the
thread's history, and a few methods that may be overridden to
implement model-specific behavior upon arrival of new messages.
``mail.thread`` is designed to work without adding any field
to the extended models. All functionalities and expected behavior
are managed by mail.thread, using model name and record ids.
A widget has been designed for the 6.1 and following version of OpenERP
web-client. However, due to technical limitations, ``mail.thread``
adds a simulated one2many field, to display the web widget by
overriding the default field displayed. Using this field
is not recommanded has it will disappeear in future version
of OpenERP, leading to a pure mixin class.
Inheriting classes are not required to implement any method, as the
default implementation will work for any model. However it is common
to override at least the ``message_new`` and ``message_update``
methods (calling ``super``) to add model-specific behavior at
creation and update of a thread.
creation and update of a thread; and ``message_get_subscribers``
to manage more precisely the social aspect of the thread through
the followers.
'''
_name = 'mail.thread'
_description = 'Email Thread'
_inherit = ['base.needaction']
def _get_message_ids(self, cr, uid, ids, name, arg, context=None):
res = {}
for thread in self.browse(cr, uid, ids, context=context):
records = self.message_load(cr, uid, [thread.id], context=context)
res[thread.id] = [obj['id'] for obj in records]
return res
# OpenSocial: message_ids_social is a dummy field that should not be used
_columns = {
'message_ids': fields.one2many('mail.message', 'res_id', 'Messages', readonly=True),
'message_ids_social': fields.function(_get_message_ids, method=True,
type='one2many', obj='mail.message', string='Temp messages',
_fields_id = 'res_id'),
}
#------------------------------------------------------
# Automatic subscription when creating/reading
#------------------------------------------------------
def create(self, cr, uid, vals, context=None):
thread_id = super(mail_thread, self).create(cr, uid, vals, context=context);
self.message_subscribe(cr, uid, [thread_id], [uid], context=context)
return thread_id;
def write(self, cr, uid, ids, vals, context=None):
write_res = super(mail_thread, self).write(cr, uid, ids, vals, context=context);
if write_res:
self.message_subscribe(cr, uid, ids, [uid], context=context)
return write_res;
def unlink(self, cr, uid, ids, context=None):
"""Override unlink, to automatically delete
- subscriptions
- messages
that are linked with res_model and res_id, not through
a foreign key with a 'cascade' ondelete attribute.
Notifications will be deleted with messages
"""
if context is None:
context = {}
subscr_obj = self.pool.get('mail.subscription')
msg_obj = self.pool.get('mail.message')
subscr_to_del_ids = subscr_obj.search(cr, uid, [('res_model', '=', self._name), ('res_id', 'in', ids)], context=context)
subscr_obj.unlink(cr, uid, subscr_to_del_ids, context=context)
msg_to_del_ids = msg_obj.search(cr, uid, [('model', '=', self._name), ('res_id', 'in', ids)], context=context)
msg_obj.unlink(cr, uid, msg_to_del_ids, context=context)
super(mail_thread, self).unlink(cr, uid, ids, context=context)
#------------------------------------------------------
# Generic message api
#------------------------------------------------------
def message_create(self, cr, uid, thread_id, vals, context=None):
"""OpenSocial: wrapper of mail.message create method
- creates the mail.message
- automatically subscribe the message writer if not already done
- push the message to subscribed users"""
if context is None:
context = {}
need_action_pushed = False
user_to_push_ids = []
subscription_obj = self.pool.get('mail.subscription')
notification_obj = self.pool.get('mail.notification')
# create message
msg_id = self.pool.get('mail.message').create(cr, uid, vals, context=context)
thread_obj = self.browse(cr, uid, [thread_id], context=context)[0]
# automatically subscribe the writer of the message if not subscribed
if vals['user_id'] and not self.message_is_subscriber(cr, vals['user_id'], [thread_id], context=context):
self.message_subscribe(cr, uid, [thread_id], [vals['user_id']], context=context)
# push the message to suscribed users
users = self.message_get_subscribers(cr, uid, [thread_id], context=context)
user_to_push_ids += [user['id'] for user in users]
# parse message to get requested users
user_to_push_ids += self.message_parse_users(cr, uid, [msg_id], vals['body_text'], context=context)
# push to need_action_user_id if set
#if thread_obj.need_action_user_id and thread_obj.need_action_user_id.id not in user_to_push_ids:
#user_to_push_ids.append(thread_obj.need_action_user_id.id)
# effectively push message to users
for id in user_to_push_ids:
notification_obj.create(cr, uid, {'user_id': id, 'message_id': msg_id}, context=context)
return msg_id
def message_parse_users(self, cr, uid, ids, string, context=None):
'''Parse message content; if find @login -(^|\s)@(\w*)-: returns the related ids'''
regex = re.compile('(^|\s)@(\w*)')
login_lst = [item[1] for item in regex.findall(string)]
if not login_lst: return []
user_ids = self.pool.get('res.users').search(cr, uid, [('login', 'in', login_lst)], context=context)
return user_ids
def message_capable_models(self, cr, uid, context=None):
ret_dict = {}
for model_name in self.pool.obj_list():
@@ -66,97 +170,11 @@ class mail_thread(osv.osv):
ret_dict[model_name] = model._description
return ret_dict
def message_thread_followers(self, cr, uid, ids, context=None):
"""Returns a list of email addresses of the people following
this thread, including the sender of each mail, and the
people who were in CC of the messages, if any.
"""
res = {}
if isinstance(ids, (str, int, long)):
ids = [long(ids)]
for thread in self.browse(cr, uid, ids, context=context):
l = set()
for message in thread.message_ids:
l.add((message.user_id and message.user_id.user_email) or '')
l.add(message.email_from or '')
l.add(message.email_cc or '')
res[thread.id] = filter(None, l)
return res
def copy(self, cr, uid, id, default=None, context=None):
"""Overrides default copy method to empty the thread of
messages attached to this record, as the copied object
will have its own thread and does not have to share it.
"""
if default is None:
default = {}
default.update({
'message_ids': [],
})
return super(mail_thread, self).copy(cr, uid, id, default, context=context)
def message_new(self, cr, uid, msg_dict, custom_values=None, context=None):
"""Called by ``message_process`` when a new message is received
for a given thread model, if the message did not belong to
an existing thread.
The default behavior is to create a new record of the corresponding
model (based on some very basic info extracted from the message),
then attach the message to the newly created record
(by calling ``message_append_dict``).
Additional behavior may be implemented by overriding this method.
:param dict msg_dict: a map containing the email details and
attachments. See ``message_process`` and
``mail.message.parse`` for details.
:param dict custom_values: optional dictionary of additional
field values to pass to create()
when creating the new thread record.
Be careful, these values may override
any other values coming from the message.
:param dict context: if a ``thread_model`` value is present
in the context, its value will be used
to determine the model of the record
to create (instead of the current model).
:rtype: int
:return: the id of the newly created thread object
"""
if context is None:
context = {}
model = context.get('thread_model') or self._name
model_pool = self.pool.get(model)
fields = model_pool.fields_get(cr, uid, context=context)
data = model_pool.default_get(cr, uid, fields, context=context)
if 'name' in fields and not data.get('name'):
data['name'] = msg_dict.get('from','')
if custom_values and isinstance(custom_values, dict):
data.update(custom_values)
res_id = model_pool.create(cr, uid, data, context=context)
self.message_append_dict(cr, uid, [res_id], msg_dict, context=context)
return res_id
def message_update(self, cr, uid, ids, msg_dict, vals={}, default_act=None, context=None):
"""Called by ``message_process`` when a new message is received
for an existing thread. The default behavior is to create a
new mail.message in the given thread (by calling
``message_append_dict``)
Additional behavior may be implemented by overriding this
method.
:param dict msg_dict: a map containing the email details and
attachments. See ``message_process`` and
``mail.message.parse()`` for details.
:param dict context: if a ``thread_model`` value is present
in the context, its value will be used
to determine the model of the thread to
update (instead of the current model).
"""
return self.message_append_dict(cr, uid, ids, msg_dict, context=context)
def message_append(self, cr, uid, threads, subject, body_text=None, email_to=False,
email_from=False, email_cc=None, email_bcc=None, reply_to=None,
email_date=None, message_id=False, references=None,
attachments=None, body_html=None, subtype=None, headers=None,
original=None, context=None):
def message_append(self, cr, uid, threads, subject, parent_id=False, body_text=None, type='email',
email_to=False, email_from=False, email_cc=None, email_bcc=None,
reply_to=None, email_date=None, message_id=False, references=None,
attachments=None, body_html=None, subtype=None, headers=None,
original=None, context=None):
"""Creates a new mail.message attached to the current mail.thread,
containing all the details passed as parameters. All attachments
will be attached to the thread record as well as to the actual
@@ -189,7 +207,7 @@ class mail_thread(osv.osv):
to determine the model of the thread to
update (instead of the current model).
"""
if context is None:
if context is None:
context = {}
if attachments is None:
attachments = {}
@@ -229,14 +247,16 @@ class mail_thread(osv.osv):
data = {
'subject': subject,
'user_id': uid,
'parent_id': parent_id,
'model' : thread._name,
'partner_id': partner_id,
'res_id': thread.id,
'date': time.strftime('%Y-%m-%d %H:%M:%S'),
'message_id': message_id,
'body_text': body_text or (hasattr(thread, 'description') and thread.description or False),
'body_text': body_text or (hasattr(thread, 'description') and thread.description or ''),
'attachment_ids': [(6, 0, to_attach)],
'state' : 'received',
'state': 'received',
'type': type,
}
if email_from:
@@ -266,7 +286,7 @@ class mail_thread(osv.osv):
'reply_to': reply_to,
'original': original,
}
mail_message.create(cr, uid, data, context=context)
self.message_create(cr, uid, thread.id, data, context=context)
return True
def message_append_dict(self, cr, uid, ids, msg_dict, context=None):
@@ -285,9 +305,13 @@ class mail_thread(osv.osv):
to determine the model of the thread to
update (instead of the current model).
"""
# OpenSocial: add default email type for old API
if not 'type' in msg_dict: msg_dict['type'] = 'email'
return self.message_append(cr, uid, ids,
subject = msg_dict.get('subject'),
parent_id = msg_dict.get('parent_id', False),
body_text = msg_dict.get('body_text'),
type = msg_dict.get('type'),
email_to = msg_dict.get('to'),
email_from = msg_dict.get('from'),
email_cc = msg_dict.get('cc'),
@@ -304,6 +328,81 @@ class mail_thread(osv.osv):
original = msg_dict.get('original'),
context = context)
# Message loading
def _message_get_parent_ids(self, cr, uid, ids, child_ids, root_ids, context=None):
if context is None:
context = {}
msg_obj = self.pool.get('mail.message')
msgs_tmp = msg_obj.read(cr, uid, child_ids, context=context)
parent_ids = [msg['parent_id'][0] for msg in msgs_tmp if msg['parent_id'] not in root_ids and msg['parent_id'][0] not in child_ids]
child_ids += parent_ids
cur_iter = 0; max_iter = 10;
while (parent_ids and (cur_iter < max_iter)):
cur_iter += 1
msgs_tmp = msg_obj.read(cr, uid, parent_ids, context=context)
parent_ids = [msg['parent_id'][0] for msg in msgs_tmp if msg['parent_id'] not in root_ids and msg['parent_id'][0] not in child_ids]
child_ids += parent_ids
return child_ids
def message_load_ids(self, cr, uid, ids, limit=100, offset=0, domain=[], ascent=False, root_ids=[False], context=None):
""" OpenSocial feature: return thread messages ids (for web compatibility)
loading messages: search in mail.messages where res_id = ids, (res_)model = current model
see get_pushed_messages for parameters explanation
"""
if context is None:
context = {}
msg_obj = self.pool.get('mail.message')
msg_ids = msg_obj.search(cr, uid, ['&', ('res_id', 'in', ids), ('model', '=', self._name)] + domain,
limit=limit, offset=offset, context=context)
if (ascent): msg_ids = self._message_get_parent_ids(cr, uid, ids, msg_ids, root_ids, context=context)
return msg_ids
def message_load(self, cr, uid, ids, limit=100, offset=0, domain=[], ascent=False, root_ids=[False], context=None):
""" OpenSocial feature: return thread messages
loading messages: search in mail.messages where res_id = ids, (res_)model = current model
see get_pushed_messages for parameters explanation
"""
msg_ids = self.message_load_ids(cr, uid, ids, limit, offset, domain, ascent, root_ids, context=context)
return self.pool.get('mail.message').read(cr, uid, msg_ids, context=context)
def get_pushed_messages(self, cr, uid, ids, limit=100, offset=0, domain=[], ascent=False, root_ids=[False], context=None):
"""OpenSocial: wall: get messages to display (=pushed notifications)
:param domain: domain to add to the search; especially child_of is interesting when dealing with threaded display
:param deep: performs an ascended search; will add to fetched msgs all their parents until root_ids
WARNING: must be used in combinaison with a child_of domain
EXAMPLE: domain = ['id', 'child_of', [32, 33]], root_ids=[32,33]
:param root_ids: root_ids when performing an ascended search
:return: list of mail.messages sorted by date
"""
if context is None: context = {}
notification_obj = self.pool.get('mail.notification')
msg_obj = self.pool.get('mail.message')
# get user notifications
notification_ids = notification_obj.search(cr, uid, [('user_id', '=', uid)], context=context)
notifications = notification_obj.browse(cr, uid, notification_ids, context=context)
msg_ids = [notification.message_id.id for notification in notifications]
# search messages: ids in notifications, add domain coming from wall search view
search_domain = [('id', 'in', msg_ids)] + domain
msg_ids = msg_obj.search(cr, uid, search_domain, limit=limit, offset=offset, context=context)
if (ascent): msg_ids = self._message_get_parent_ids(cr, uid, ids, msg_ids, root_ids, context=context)
msgs = msg_obj.read(cr, uid, msg_ids, context=context)
return msgs
# Message tools
def message_get_discussions_nbr(self, cr, uid, ids, context=None):
message_obj = self.pool.get('mail.message')
count = message_obj.search(cr, uid, [('model', '=', self._name), ('res_id', '=', ids), ('parent_id', '=', False)], count=True)
return count
def message_get_messages_nbr(self, cr, uid, ids, context=None):
message_obj = self.pool.get('mail.message')
count = message_obj.search(cr, uid, [('model', '=', self._name), ('res_id', 'in', ids)], count=True)
return count
#------------------------------------------------------
# Email specific
#------------------------------------------------------
# message_process will call either message_new or message_update.
def message_process(self, cr, uid, model, message, custom_values=None,
save_original=False, strip_attachments=False,
@@ -389,8 +488,79 @@ class mail_thread(osv.osv):
self.message_forward(cr, uid, model, [res_id], msg_txt, context=context)
return res_id
# for backwards-compatibility with old scripts
process_email = message_process
def message_new(self, cr, uid, msg_dict, custom_values=None, context=None):
"""Called by ``message_process`` when a new message is received
for a given thread model, if the message did not belong to
an existing thread.
The default behavior is to create a new record of the corresponding
model (based on some very basic info extracted from the message),
then attach the message to the newly created record
(by calling ``message_append_dict``).
Additional behavior may be implemented by overriding this method.
:param dict msg_dict: a map containing the email details and
attachments. See ``message_process`` and
``mail.message.parse`` for details.
:param dict custom_values: optional dictionary of additional
field values to pass to create()
when creating the new thread record.
Be careful, these values may override
any other values coming from the message.
:param dict context: if a ``thread_model`` value is present
in the context, its value will be used
to determine the model of the record
to create (instead of the current model).
:rtype: int
:return: the id of the newly created thread object
"""
if context is None:
context = {}
model = context.get('thread_model') or self._name
model_pool = self.pool.get(model)
fields = model_pool.fields_get(cr, uid, context=context)
data = model_pool.default_get(cr, uid, fields, context=context)
if 'name' in fields and not data.get('name'):
data['name'] = msg_dict.get('from','')
if custom_values and isinstance(custom_values, dict):
data.update(custom_values)
res_id = model_pool.create(cr, uid, data, context=context)
self.message_append_dict(cr, uid, [res_id], msg_dict, context=context)
return res_id
def message_update(self, cr, uid, ids, msg_dict, vals={}, default_act=None, context=None):
"""Called by ``message_process`` when a new message is received
for an existing thread. The default behavior is to create a
new mail.message in the given thread (by calling
``message_append_dict``)
Additional behavior may be implemented by overriding this
method.
:param dict msg_dict: a map containing the email details and
attachments. See ``message_process`` and
``mail.message.parse()`` for details.
:param dict context: if a ``thread_model`` value is present
in the context, its value will be used
to determine the model of the thread to
update (instead of the current model).
"""
return self.message_append_dict(cr, uid, ids, msg_dict, context=context)
def message_thread_followers(self, cr, uid, ids, context=None):
"""Returns a list of email addresses of the people following
this thread, including the sender of each mail, and the
people who were in CC of the messages, if any.
"""
res = {}
if isinstance(ids, (str, int, long)):
ids = [long(ids)]
for thread in self.browse(cr, uid, ids, context=context):
l = set()
for message in thread.message_ids:
l.add((message.user_id and message.user_id.user_email) or '')
l.add(message.email_from or '')
l.add(message.email_cc or '')
res[thread.id] = filter(None, l)
return res
def message_forward(self, cr, uid, model, thread_ids, msg, email_error=False, context=None):
"""Sends an email to all people following the given threads.
@@ -469,4 +639,54 @@ class mail_thread(osv.osv):
res['partner_id'] = address.partner_id.id
return res
# for backwards-compatibility with old scripts
process_email = message_process
#------------------------------------------------------
# Note specific
#------------------------------------------------------
def message_append_note(self, cr, uid, ids, subject, body, parent_id=False, type='notification', context=None):
return self.message_append(cr, uid, ids, subject, body_text=body, parent_id=parent_id, type=type, context=context)
#------------------------------------------------------
# Subscription mechanism
#------------------------------------------------------
def message_get_subscribers_ids(self, cr, uid, ids, context=None):
subscr_obj = self.pool.get('mail.subscription')
subscr_ids = subscr_obj.search(cr, uid, ['&', ('res_model', '=', self._name), ('res_id', 'in', ids)], context=context)
subs = subscr_obj.read(cr, uid, subscr_ids, context=context)
return [sub['user_id'][0] for sub in subs]
def message_get_subscribers(self, cr, uid, ids, context=None):
user_ids = self.message_get_subscribers_ids(cr, uid, ids, context=context)
users = self.pool.get('res.users').read(cr, uid, user_ids, fields=['id', 'name', 'avatar_mini'], context=context)
return users
def message_is_subscriber(self, cr, uid, ids, user_id = None, context=None):
users = self.message_get_subscribers(cr, uid, ids, context=context)
sub_user_id = uid if user_id is None else user_id
if sub_user_id in [user['id'] for user in users]:
return True
return False
def message_subscribe(self, cr, uid, ids, user_ids = None, context=None):
subscription_obj = self.pool.get('mail.subscription')
to_subscribe_uids = [uid] if user_ids is None else user_ids
create_ids = []
for id in ids:
for user_id in to_subscribe_uids:
if self.message_is_subscriber(cr, uid, [id], user_id=user_id, context=context): continue
create_ids.append(subscription_obj.create(cr, uid, {'res_model': self._name, 'res_id': id, 'user_id': user_id}, context=context))
return create_ids
def message_unsubscribe(self, cr, uid, ids, user_ids = None, context=None):
subscription_obj = self.pool.get('mail.subscription')
to_unsubscribe_uids = [uid] if user_ids is None else user_ids
to_delete_sub_ids = subscription_obj.search(cr, uid,
['&', '&', ('res_model', '=', self._name), ('res_id', 'in', ids), ('user_id', 'in', to_unsubscribe_uids)], context=context)
subscription_obj.unlink(cr, uid, to_delete_sub_ids, context=context)
return True
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
+23 -44
View File
@@ -1,53 +1,32 @@
<?xml version="1.0"?>
<openerp>
<data>
<record model="ir.ui.view" id="view_mailgate_thread_form">
<field name="name">mail.thread.form</field>
<field name="model">mail.thread</field>
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Email Thread">
<separator string="Communication History" colspan="4"/>
<field name="message_ids" nolabel="1" colspan="4" mode="tree">
<tree string="Communication History">
<field name="display_text"/>
</tree>
</field>
</form>
</field>
<!-- toplevel menu -->
<record id="mail_feeds_main" model="ir.ui.menu">
<field name="name">Feeds</field>
<field name="action" ref="action_mail_all_feeds"/>
</record>
<record model="ir.ui.view" id="view_mailgate_thread_tree">
<field name="name">mail.thread.tree</field>
<field name="model">mail.thread</field>
<field name="type">tree</field>
<field name="arch" type="xml">
<tree string="Email Threads">
<field name="message_ids" />
</tree>
</field>
<!-- left-side menu: Feeds !-->
<menuitem id="mail_feeds" name="Feeds" parent="mail_feeds_main"/>
<record id="mail_wallfeeds" model="ir.ui.menu">
<field name="name">All Feeds</field>
<field name="sequence" eval="10"/>
<field name="action" ref="action_mail_all_feeds"/>
<field name="parent_id" ref="mail_feeds"/>
</record>
<!-- Emails thread action -->
<record model="ir.actions.act_window" id="action_view_mailgate_thread">
<field name="name">Email Threads</field>
<field name="res_model">mail.thread</field>
<field name="view_mode">tree,form</field>
<field name="view_type">form</field>
<field name="view_id" ref="view_mailgate_thread_tree"/>
<record id="mail_myfeeds" model="ir.ui.menu">
<field name="name">My Feeds</field>
<field name="sequence" eval="11"/>
<field name="action" ref="action_mail_my_feeds"/>
<field name="parent_id" ref="mail_feeds"/>
</record>
<record model="ir.actions.act_window.view" id="action_view_mailgate_thread_view1">
<field name="sequence" eval="1"/>
<field name="view_mode">tree</field>
<field name="view_id" ref="view_mailgate_thread_tree"/>
<field name="act_window_id" ref="action_view_mailgate_thread"/>
</record>
<record model="ir.actions.act_window.view" id="action_view_mailgate_thread_view2">
<field name="sequence" eval="2"/>
<field name="view_mode">form</field>
<field name="view_id" ref="view_mailgate_thread_form"/>
<field name="act_window_id" ref="action_view_mailgate_thread"/>
</record>
<!-- left-side menu: Tmp !-->
<menuitem id="mail_debug" name="Debug/Tmp" sequence="20" parent="mail_feeds_main"/>
<menuitem id="mail_debug_msgs" name="Messages" parent="mail_debug" action="action_view_all_messages_short"/>
<menuitem id="mail_debug_subs" name="Subscriptions" parent="mail_debug" action="action_view_subscriptions"/>
<menuitem id="mail_debug_notifs" name="Pushed notif" parent="mail_debug" action="action_view_notifications"/>
</data>
</openerp>
+33
View File
@@ -0,0 +1,33 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2009-Today OpenERP SA (<http://www.openerp.com>)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>
#
##############################################################################
from osv import osv
class res_users(osv.osv):
_name = 'res.users'
_inherit = ['res.users', 'mail.thread']
def create(self, cr, uid, data, context=None):
user_id = super(res_users, self).create(cr, uid, data, context=context)
# make user follow itself
self.message_subscribe(cr, uid, [user_id], [user_id], context=context)
return user_id
+2
View File
@@ -1,3 +1,5 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_mail_message,mail.message,model_mail_message,,1,0,1,0
access_mail_thread,mail.thread,model_mail_thread,,1,0,1,0
access_mail_subscription,mail.subscription,model_mail_subscription,,1,0,1,0
access_mail_notification,mail.notification,model_mail_notification,,1,0,1,0
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_mail_message mail.message model_mail_message 1 0 1 0
3 access_mail_thread mail.thread model_mail_thread 1 0 1 0
4 access_mail_subscription mail.subscription model_mail_subscription 1 0 1 0
5 access_mail_notification mail.notification model_mail_notification 1 0 1 0
+239
View File
@@ -0,0 +1,239 @@
/* ------------------------------ */
/* Wall */
/* ------------------------------ */
.oe_mail_wall {
overflow: auto;
padding: 5px;
}
.oe_mail_wall_search {
width: 55%;
}
/* 2 columns view */
.oe_mail_wall_left {
float: left;
width: 65%;
}
.oe_mail_wall_right {
float: right;
width: 34%;
}
.oe_mail_wall_thread div.oe_mail_thread_act .oe_mail_action_textarea {
height: 20px;
padding: 2px;
}
.oe_mail_thread_subthread div.oe_mail_thread_act .oe_mail_msg_image, .oe_mail_thread_subthread div.oe_mail_thread_display .oe_mail_msg_image {
width: 30px;
height: 30px;
}
.oe_mail_thread_subthread .oe_mail_msg_content, .oe_mail_thread_subthread .oe_mail_msg_content {
margin-left: 40px;
}
.oe_mail_wall_more {
text-align: center;
}
/* ------------------------------ */
/* RecordThread */
/* ------------------------------ */
.oe_mail_recthread {
overflow: auto;
padding: 5px 5px 5px 5px;
}
/* Left-side CSS */
.oe_mail_actions {
margin-bottom: 10px;
}
.oe_mail_followers {
margin-bottom: 10px;
}
.oe_mail_followers_action, .oe_mail_followers_display {
}
/* RecordThread: 2 columns view */
.oe_mail_recthread_left {
float: left;
width: 55%;
}
.oe_mail_recthread_right {
float: right;
width: 25%;
margin-right: 5%;
}
.oe_mail_button_follow, .oe_mail_button_unfollow {
width: 120px;
}
.oe_mail_button_followers {
display: inline;
width: 120px;
}
.oe_mail_followers h4 {
margin: 0 0 8px;
}
/* ------------------------------ */
/* ThreadDisplay */
/* ------------------------------ */
div.oe_mail_thread_act {
white-space: normal;
margin-bottom: 5px;
}
div.oe_mail_thread_display {
white-space: normal;
margin-bottom: 5px;
}
div.oe_mail_thread_subthread {
margin-top: 8px;
padding-left: 5%;
}
div.oe_mail_thread_more {
border-bottom: 1px solid #D2D9E7;
}
div.oe_mail_thread_msg {
padding: 4px;
-moz-border-radius: 4px;
-webkit-border-radius: 4px;
-o-border-radius: 4px;
-ms-border-radius: 4px;
border-radius: 4px;
}
div.notification {
background: #E0E0E0;
}
div.email {
background: #E0E0E0;
}
div.oe_mail_thread_msg:after, div.oe_mail_thread_act:after {
content: "";
display: block;
clear: both;
}
.oe_mail_msg_content {
margin-left: 60px;
}
.oe_mail_action_textarea {
height: 50px;
padding: 5px;
}
.oe_mail_msg_image {
margin-right: 8px;
width: 45px;
height: 45px;
text-align: center;
overflow: hidden;
-moz-border-radius: 4px;
-webkit-border-radius: 4px;
-o-border-radius: 4px;
-ms-border-radius: 4px;
border-radius: 4px;
-moz-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.4);
-webkit-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.4);
-o-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.4);
-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.4);
clip: rect(5px, 40px, 45px, 0px);
}
.oe_mail_msg_p {
padding: 0;
margin: 0;
}
.oe_mail_msg_p_email_header {
border-bottom: 1px solid #D2D9E7;
}
.oe_mail_msg_body a.reduce, .oe_mail_msg_body_short a.expand {
color: #4E43E7;
}
/* ------------------------------ */
/* Styling (should be openerp) */
/* ------------------------------ */
input.oe_mail, textarea.oe_mail {
width: 100%;
padding: 4px;
font-size: 12px;
border: 1px solid #cccccc;
-webkit-transition: border linear 0.2s, box-shadow linear 0.2s;
-moz-transition: border linear 0.2s, box-shadow linear 0.2s;
-ms-transition: border linear 0.2s, box-shadow linear 0.2s;
-o-transition: border linear 0.2s, box-shadow linear 0.2s;
transition: border linear 0.2s, box-shadow linear 0.2s;
-moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
-webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
-moz-border-radius: 3px;
-webkit-border-radius: 3px;
border-radius: 3px;
}
input.oe_mail:focus, textarea.oe_mail:focus {
outline: 0;
border-color: rgba(82, 168, 236, 0.8);
-moz-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1), 0 0 8px rgba(82, 168, 236, 0.6);
-webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1), 0 0 8px rgba(82, 168, 236, 0.6);
-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1), 0 0 8px rgba(82, 168, 236, 0.6);
}
p.oe_mail_msg {
padding: 0;
margin: 0;
}
.oe_mail_oe_right {
float: right;
margin-right: 8px;
}
.oe_mail_oe_left {
float: left;
margin-right: 8px;
}
.oe_mail_oe_fade {
font-size: 12px;
color: #888888;
}
.oe_mail_oe_bold {
font-weight: bold;
}
a.oe_mail_oe_intlink {
color: #8786b7;
}
.oe_mail_oe_warning, .oe_mail_oe_warning a {
color: #C03000;
}
.oe_mail_oe_space {
margin-left: 15px;
}
+74
View File
@@ -0,0 +1,74 @@
.oe_group_vignette {
padding: 8px 0;
min-height: 100px;
}
.oe_group_image, .oe_group_details {
display: inline-block;
vertical-align: top;
}
.oe_group_image {
width: 100px;
height: 100px;
text-align: center;
overflow: hidden;
-moz-border-radius: 3px;
-webkit-border-radius: 3px;
-o-border-radius: 3px;
-ms-border-radius: 3px;
border-radius: 3px;
-moz-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.4);
-webkit-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.4);
-o-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.4);
-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.4);
}
.oe_group_photo {
width: 100px;
height: auto;
clip: rect(10px, 100px, 110px, 0px);
}
.oe_group_photo_wide {
height: 100px;
width: auto;
clip: rect(0px, 115px, 100px, 15px);
}
.oe_group_details {
width: 220px;
font-size: 13px;
padding: 2px 5px;
color: #4c4c4c;
min-height: 120px;
}
.oe_group_details h4 {
margin: 0;
font-size: 13px;
}
.oe_group_details h4 a {
color: #4c4c4c;
}
.oe_group_details h4 a:hover {
text-decoration: underline;
}
.oe_group_details ul {
margin: 3px 0 5px;
padding: 0;
list-style: none;
}
.oe_group_details li {
margin: 2px 0;
}
.oe_group_description {
}
.oe_kanban_record div.true {
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

+728
View File
@@ -0,0 +1,728 @@
openerp.mail = function(session) {
var mail = session.mail = {};
/* Add ThreadDisplay widget to registry */
session.web.form.widgets.add(
'Thread', 'openerp.mail.Thread');
session.web.page.readonly.add(
'Thread', 'openerp.mail.Thread');
/**
* ThreadDisplay widget: this widget handles the display of a thread of messages.
* Two displays are managed through the [thread_level] parameter that sets
* the level number in the thread:
* - root message
* - - sub message (parent_id = root message)
* - - - sub sub message (parent id = sub message)
* - - sub message (parent_id = root message)
* This widget has 2 ways of initialization, either you give records to be rendered,
* either it will fetch [limit] messages related to [res_model]:[res_id].
*/
mail.Thread = session.web.Widget.extend({
template: 'Thread',
/**
* @param {Object} parent parent
* @param {Object} [params]
* @param {String} [params.res_model] res_model of mail.thread object
* @param {Number} [params.res_id] res_id of record
* @param {Number} [params.parent_id=false] parent_id of message
* @param {Number} [params.uid] user id
* @param {Number} [params.thread_level=0] number of levels in the thread (only 0 or 1 currently)
* @param {Number} [params.msg_more_limit=100] number of character to display before having a "show more" link;
* note that the text will not be truncated if it does not have 110% of
* the parameter (ex: 110 characters needed to be truncated and be displayed
* as a 100-characters message)
* @param {Number} [params.limit=10] maximum number of messages to fetch
* @param {Number} [params.offset=0] offset for fetching messages
* @param {Number} [params.records=null] records to show instead of fetching messages
*/
init: function(parent, params) {
this._super(parent);
this.params = params;
this.params.parent_id = this.params.parent_id || false;
this.params.thread_level = this.params.thread_level || 0;
this.params.msg_more_limit = this.params.msg_more_limit || 100;
this.params.limit = this.params.limit || 2;
this.params.offset = this.params.offset || 0;
this.params.records = this.params.records || null;
/* DataSets and internal vars */
this.ds = new session.web.DataSet(this, this.params.res_model);
this.ds_users = new session.web.DataSet(this, 'res.users');
this.ds_msg = new session.web.DataSet(this, 'mail.message');
this.name = 'Unknown record'
this.sorted_comments = {'root_ids': [], 'root_id_msg_list': {}};
/* Display vars */
this.display = {};
this.display.show_post_comment = this.params.show_post_comment || false;
this.display.show_reply = (this.params.thread_level > 0);
this.display.show_delete = true;
this.display.show_hide = true;
this.display.show_more = (this.params.thread_level == 0);
// not used currently
this.intlinks_mapping = {};
},
start: function() {
var self = this;
this._super.apply(this, arguments);
// customize display
this.$element.find('p.oe_mail_p_nomore').hide();
if (! this.display.show_post_comment) this.$element.find('div.oe_mail_thread_act').hide();
if (! this.display.show_more) this.$element.find('div.oe_mail_thread_more').hide();
// add events
this.add_events();
/* get record name */
var name_get_done = self.ds.name_get([this.params.res_id]).then(function (records) { self.name = records[0][1]; });
var display_done = $.when(name_get_done).then(function () {
/* display user, fetch comments */
self.display_current_user();
if (self.params.records) return self.display_comments(self.params.records);
else return self.init_comments();
});
return display_done
},
add_events: function() {
var self = this;
// event: click on 'more'
this.$element.find('button.oe_mail_button_more').click(function () {
self.do_more();
});
// event: writing in textarea
this.$element.find('textarea.oe_mail_action_textarea').keyup(function (event) {
var charCode = (event.which) ? event.which : window.event.keyCode;
if (event.shiftKey && charCode == 13) { this.value = this.value+"\n"; }
else if (charCode == 13) { self.do_comment(); }
});
// event: click on 'reply'
this.$element.find('div.oe_mail_thread_display').delegate('a.oe_mail_msg_reply', 'click', function (event) {
var act_dom = $(this).parents('div.oe_mail_thread_display').find('div.oe_mail_thread_act:first');
act_dom.toggle();
});
// event: click on 'delete'
this.$element.find('div.oe_mail_thread_display').delegate('a.oe_mail_msg_delete', 'click', function (event) {
console.log('deleting');
var msg_id = event.srcElement.dataset.id;
if (! msg_id) return false;
self.ds_msg.unlink([parseInt(msg_id)]).then();
return false;
});
// event: click on 'hide'
this.$element.find('div.oe_mail_thread_display').delegate('a.oe_mail_msg_hide', 'click', function (event) {
console.log('hiding');
var msg_id = event.srcElement.dataset.id;
if (! msg_id) return false;
return false;
});
// event: click on an internal link
this.$element.find('div.oe_mail_thread_display').delegate('a.intlink', 'click', function (event) {
// lazy implementation: fetch data and try to redirect
if (! event.srcElement.dataset.resModel) return false;
else var res_model = event.srcElement.dataset.resModel;
var res_login = event.srcElement.dataset.resLogin;
var res_id = event.srcElement.dataset.resId;
if ((! res_login) && (! res_id)) return false;
if (! res_id) {
var ds = new session.web.DataSet(self, res_model);
var defer = ds.call('search', [[['login', '=', res_login]]]).then(function (records) {
if (records[0]) {
self.do_action({ type: 'ir.actions.act_window', res_model: res_model, res_id: parseInt(records[0]), views: [[false, 'form']]});
}
else return false;
});
}
else self.do_action({ type: 'ir.actions.act_window', res_model: res_model, res_id: parseInt(res_id), views: [[false, 'form']]});
});
},
stop: function () {
//this._super.apply(this, arguments);
},
init_comments: function() {
var self = this;
this.params.offset = 0;
this.sorted_comments = {'root_ids': [], 'root_id_msg_list': {}};
this.$element.find('div.oe_mail_thread_display').empty();
domain = this.get_fetch_domain(this.sorted_comments);
return this.fetch_comments(this.params.limit, this.params.offset, domain).then();
},
fetch_comments: function (limit, offset, domain) {
console.log('fetch comments');
var self = this;
var defer = this.ds.call('message_load', [[this.params.res_id], (limit||this.params.limit), (offset||this.params.offset), (domain||[]), (this.params.thread_level > 0), (this.sorted_comments['root_ids'])]);
$.when(defer).then(function (records) {
if (records.length < self.params.limit) self.display.show_more = false;
self.display_comments(records);
if (self.display.show_more == false) {
self.$element.find('button.oe_mail_button_more:last').hide();
self.$element.find('p.oe_mail_p_nomore:last').show(); }
});
return defer;
},
display_comments: function (records) {
var self = this;
if (this.params.thread_level > 0) {
this.sc = this.sort_comments(records);
}
/* WIP: map matched regexp -> records to browse with name */
//_(records).each(function (record) {
//self.do_check_internal_links(record.body_text);
//});
_(records).each(function (record) {
var sub_msgs = [];
if ((record.parent_id == false || record.parent_id[0] == self.params.parent_id) && self.params.thread_level > 0 ) {
var sub_list = self.sc['root_id_msg_list'][record.id];
_(records).each(function (record) {
if (record.parent_id == false || record.parent_id[0] == self.params.parent_id) return;
if (_.indexOf(_.pluck(sub_list, 'id'), record.id) != -1) {
sub_msgs.push(record);
}
});
self.display_comment(record);
self.thread = new mail.Thread(self, {'res_model': self.params.res_model, 'res_id': self.params.res_id, 'uid': self.params.uid,
'records': sub_msgs, 'thread_level': (self.params.thread_level-1), 'parent_id': record.id});
self.$element.find('div.oe_mail_thread_display:last').append('<div class="oe_mail_thread_subthread"/>');
self.thread.appendTo(self.$element.find('div.oe_mail_thread_subthread:last'));
}
else if (self.params.thread_level == 0) {
self.display_comment(record);
}
});
// update offset for "More" buttons
if (this.params.thread_level == 0) this.params.offset += records.length;
},
/**
* Display a record
*/
display_comment: function (record) {
if (record.type == 'email') { record.mini_url = ('/mail/static/src/img/email_icon.png'); }
else { record.mini_url = this.thread_get_avatar_mini('res.users', 'avatar_mini', record.user_id[0]); }
// body text manipulation
record.body_text = this.do_clean_text(record.body_text);
record.tr_body_text = this.do_truncate_string(record.body_text, this.params.msg_more_limit);
record.body_text = this.do_replace_internal_links(record.body_text);
if (record.tr_body_text) record.tr_body_text = this.do_replace_internal_links(record.tr_body_text);
// render
$(session.web.qweb.render('ThreadMsg', {'record': record, 'res_model': this.params.res_model, 'res_id': this.params.res_id, 'name': this.name,
'display': this.display})).appendTo(this.$element.children('div.oe_mail_thread_display:first'));
// truncated: hide full-text, show summary, add buttons
if (record.tr_body_text) {
var node_body = this.$element.find('span.oe_mail_msg_body:last').append(' <a href="#" class="reduce">[ ... Show less]</a>');
var node_body_short = this.$element.find('span.oe_mail_msg_body_short:last').append(' <a href="#" class="expand">[ ... Show more]</a>');
node_body.hide();
node_body.find('a:last').click(function() { node_body.hide(); node_body_short.show(); return false; });
node_body_short.find('a:last').click(function() { node_body_short.hide(); node_body.show(); return false; });
}
},
/**
* Add records to sorted_comments array
* @param {Array} records records from mail.message sorted by date desc
* @returns {Object} sc sorted_comments: dict {
* 'root_id_list': list or root_ids
* 'root_id_msg_list': {'record_id': [ancestor_ids]}, still sorted by date desc
* 'id_to_root': {'root_id': [records]}, still sorted by date desc
* }
*/
sort_comments: function (records) {
var self = this;
sc = {'root_id_list': [], 'root_id_msg_list': {}, 'id_to_root': {}}
var cur_iter = 0; var max_iter = 10; var modif = true;
/* step1: get roots */
while ( modif && (cur_iter++) < max_iter) {
modif = false;
_(records).each(function (record) {
if ( (record.parent_id == false || record.parent_id[0] == self.params.parent_id) && (_.indexOf(sc['root_id_list'], record.id) == -1)) {
sc['root_id_list'].push(record.id);
sc['root_id_msg_list'][record.id] = [];
self.sorted_comments['root_ids'].push(record.id);
modif = true;
}
else {
if (_.indexOf(sc['root_id_list'], record.parent_id[0]) != -1) {
sc['id_to_root'][record.id] = record.parent_id[0];
modif = true;
}
else if ( sc['id_to_root'][record.parent_id[0]] ) {
sc['id_to_root'][record.id] = sc['id_to_root'][record.parent_id[0]];
modif = true;
}
}
});
}
/* step2: add records */
_(records).each(function (record) {
var root_id = sc['id_to_root'][record.id];
if (! root_id) return;
sc['root_id_msg_list'][root_id].push(record);
//self.sorted_comments['root_id_msg_list'][root_id].push(record.id);
});
return sc;
},
display_current_user: function () {
return this.$element.find('img.oe_mail_msg_image').attr('src', this.thread_get_avatar_mini('res.users', 'avatar_mini', this.params.uid));
},
do_comment: function () {
var comment_node = this.$element.find('textarea');
var body_text = comment_node.val();
comment_node.val('');
return this.ds.call('message_append_note', [[this.params.res_id], 'Reply comment', body_text, this.params.parent_id, 'comment']).then(
this.proxy('init_comments'));
},
/**
* Create a domain to fetch new comments according to
* comment already present in sorted_comments
* @param {Object} sorted_comments (see sort_comments)
* @returns {Array} fetch_domain (OpenERP domain style)
*/
get_fetch_domain: function (sorted_comments) {
var domain = [];
var ids = [];
var dis2 = [];
if (this.params.parent_id) {
domain.push(['id', 'child_of', this.params.parent_id]);
}
_(sorted_comments.root_ids).each(function (id) { // each record
ids.push(id);
dis2.push(id);
});
if (ids.length > 0) {
domain.push('&');
domain.push('!');
domain.push(['id', 'child_of', ids]);
}
if (this.params.parent_id != false) {
dis2.push(this.params.parent_id);
}
if (dis2.length > 0) {
domain.push(['id', 'not in', dis2]);
}
return domain;
},
do_more: function () {
domain = this.get_fetch_domain(this.sorted_comments);
console.log(domain);
console.log(this.params.limit + '-' + this.params.offset + '-' + this.params.parent_id + '-' + this.params.res_id);
//return true;
return this.fetch_comments(this.params.limit, this.params.offset, domain);
},
do_replace_internal_links: function (string) {
var self = this;
/* shortcut to user: @login */
var regex_login = new RegExp(/(^|\s)@(\w*)/g);
var regex_res = regex_login.exec(string);
while (regex_res != null) {
var login = regex_res[2];
string = string.replace(regex_res[0], '<a href="#" class="intlink oe_mail_oe_intlink" data-res-model="res.users" data-res-login = ' + login + '>@' + login + '</a>');
regex_res = regex_login.exec(string);
}
return string;
},
thread_get_avatar_mini: function(model, field, id) {
return this.session.prefix + '/web/binary/image?session_id=' + this.session.session_id + '&model=' + model + '&field=' + field + '&id=' + (id || '');
},
do_truncate_string: function(string, max_length) {
if (string.length <= (max_length * 1.2)) return false;
else return string.slice(0, max_length);
},
do_clean_text: function (string) {
var html = $('<div/>').text(string.replace(/\s+/g, ' ')).html().replace(new RegExp('&lt;(/)?(b|em)\\s*&gt;', 'gi'), '<$1$2>');
return html;
},
/**
*
* var regex_login = new RegExp(/(^|\s)@(\w*[a-zA-Z_.]+\w*\s)/g);
* var regex_login = new RegExp(/(^|\s)@(\w*)/g);
* var regex_intlink = new RegExp(/(^|\s)#(\w*[a-zA-Z_]+\w*)\.(\w+[a-zA-Z_]+\w*),(\w+)/g);
*/
do_check_internal_links: function(string) {
/* shortcut to user: @login */
var regex_login = new RegExp(/(^|\s)@(\w*)/g);
var regex_res = regex_login.exec(string);
while (regex_res != null) {
var login = regex_res[2];
if (! ('res.users' in this.map_hash)) { this.map_hash['res.users']['name'] = []; }
this.map_hash['res.users']['login'].push(login);
regex_res = regex_login.exec(string);
}
/* internal links: #res.model,name */
var regex_intlink = new RegExp(/(^|\s)#(\w*[a-zA-Z_]+\w*)\.(\w+[a-zA-Z_]+\w*),(\w+)/g);
regex_res = regex_intlink.exec(string);
while (regex_res != null) {
var res_model = regex_res[2] + '.' + regex_res[3];
var res_name = regex_res[4];
if (! (res_model in this.map_hash)) { this.map_hash[res_model]['name'] = []; }
this.map_hash[res_model]['name'].push(res_name);
regex_res = regex_intlink.exec(string);
}
},
});
/* Add ThreadView widget to registry */
session.web.form.widgets.add(
'ThreadView', 'openerp.mail.RecordThread');
session.web.page.readonly.add(
'ThreadView', 'openerp.mail.RecordThread');
/* ThreadView widget: thread of comments */
mail.RecordThread = session.web.form.Field.extend({
// QWeb template to use when rendering the object
form_template: 'RecordThread',
init: function() {
this.is_sub = 0;
this.see_sub = 1;
this._super.apply(this, arguments);
this.thread = null;
/* DataSets */
this.ds = new session.web.DataSet(this, this.view.model);
this.ds_users = new session.web.DataSet(this, 'res.users');
},
start: function() {
var self = this;
this._super.apply(this, arguments);
/* bind and hide buttons */
self.$element.find('button.oe_mail_button_followers').bind('click', function () { self.do_toggle_followers(); }).hide();
self.$element.find('button.oe_mail_button_follow').click(function () { self.do_follow(); }).hide()
.mouseover(function () { $(this).html('Follow'); }).mouseleave(function () { $(this).html('Not following'); });
self.$element.find('button.oe_mail_button_unfollow').click(function () { self.do_unfollow(); }).hide()
.mouseover(function () { $(this).html('Unfollow'); }).mouseleave(function () { $(this).html('Following'); });
},
stop: function () {
this._super.apply(this, arguments);
},
set_value: function() {
var self = this;
this._super.apply(this, arguments);
this.see_sub = 1;
/* hide follow/unfollow/see followers buttons */
this.$element.find('button.oe_mail_button_followers').html('Hide followers')
this.$element.find('button.oe_mail_button_follow').hide();
this.$element.find('button.oe_mail_button_unfollow').hide();
if (! this.view.datarecord.id) { return; }
/* find wich (un)follow buttons to show */
var fetch_fol_done = this.ds.call('message_is_subscriber', [[this.view.datarecord.id]]).then(function (records) {
if (records == true) { self.is_sub = 1; self.$element.find('button.oe_mail_button_unfollow').show(); }
else { self.is_sub = 0; self.$element.find('button.oe_mail_button_follow').show(); }
});
/* fetch subscribers */
var fetch_sub_done = this.fetch_subscribers();
/* create ThreadDisplay widget and render it */
this.$element.find('div.oe_mail_recthread_left').empty();
if (this.thread) this.thread.stop();
this.thread = new mail.Thread(this, {'res_model': this.view.model, 'res_id': this.view.datarecord.id, 'uid': this.session.uid, 'show_post_comment': true});
this.thread.appendTo(this.$element.find('div.oe_mail_recthread_left'));
return fetch_fol_done && fetch_sub_done;
},
fetch_subscribers: function () {
return this.ds.call('message_get_subscribers', [[this.view.datarecord.id]]).then(this.proxy('display_subscribers'));
},
display_subscribers: function (records) {
var self = this;
var sub_node = this.$element.find('div.oe_mail_followers')
sub_node.empty();
$('<h4/>').html('Followers (' + records.length + ')').appendTo(sub_node);
_(records).each(function (record) {
var mini_url = self.thread_get_avatar_mini('res.users', 'avatar_mini', record.id);
$('<img class="oe_mail_oe_left oe_mail_msg_image" src="' + mini_url + '" title="' + record.name + '" alt="' + record.name + '"/>').appendTo(sub_node);
});
},
do_follow: function () {
this.do_toggle_follow();
return this.ds.call('message_subscribe', [[this.view.datarecord.id]]).then(this.proxy('fetch_subscribers'));
},
do_unfollow: function () {
this.do_toggle_follow();
return this.ds.call('message_unsubscribe', [[this.view.datarecord.id]]).then(this.proxy('fetch_subscribers'));
},
do_toggle_follow: function () {
this.is_sub = 1 - this.is_sub;
this.$element.find('button.oe_mail_button_unfollow').toggle();
this.$element.find('button.oe_mail_button_follow').toggle();
},
do_toggle_followers: function () {
this.see_sub = 1 - this.see_sub;
if (this.see_sub == 1) { this.$element.find('button.oe_mail_button_followers').html('Hide followers'); }
else { this.$element.find('button.oe_mail_button_followers').html('Display followers'); }
this.$element.find('div.oe_mail_followers').toggle();
},
thread_get_avatar_mini: function(model, field, id) {
return this.session.prefix + '/web/binary/image?session_id=' + this.session.session_id + '&model=' + model + '&field=' + field + '&id=' + (id || '');
},
});
/* Add WallView widget to registry */
session.web.client_actions.add('mail.all_feeds', 'session.mail.WallView');
/* WallView widget: a wall of messages */
mail.WallView = session.web.Widget.extend({
template: 'Wall',
/**
* @param {Object} parent parent
* @param {Object} [params]
* @param {Number} [params.limit=20] number of messages to show and fetch
* @param {Number} [params.search_view_id=false] search view id for messages
* @var {Array} sorted_comments records sorted by res_model and res_id
* records.res_model = {res_ids}
* records.res_model.res_id = [records]
*/
init: function (parent, params) {
this._super(parent);
this.params = params || {};
this.params.limit = params.limit || 2;
this.params.search_view_id = params.search_view_id || false;
this.params.thread_level = params.thread_level || 1;
this.params.search = {};
this.params.domain = [];
this.sorted_comments = {'models': {}};
this.display_show_more = true;
/* DataSets */
this.ds_msg = new session.web.DataSet(this, 'mail.message');
this.ds_thread = new session.web.DataSet(this, 'mail.thread');
this.ds_users = new session.web.DataSet(this, 'res.users');
},
start: function() {
var self = this;
this._super.apply(this, arguments);
/* events and buttons */
this.$element.find('button.oe_mail_wall_button_comment').bind('click', function () { self.do_comment(); });
this.$element.find('button.oe_mail_wall_button_more').bind('click', function () { self.do_more(); });
this.$element.find('p.oe_mail_wall_nomore').hide();
/* load mail.message search view */
var search_view_loaded = this.load_search_view(this.params.search_view_id, {}, false);
var search_view_ready = $.when(search_view_loaded).then(function () {
self.searchview.on_search.add(self.do_searchview_search);
});
/* fetch comments */
var comments_ready = this.init_comments(this.params.domain, {}, 0, this.params.limit);
return (search_view_ready && comments_ready);
},
stop: function () {
this._super.apply(this, arguments);
},
/**
* Loads the mail.message search view
* @param {Number} view_id id of the search view to load
* @param {Object} defaults ??
* @param {Boolean} hidden ??
*/
load_search_view: function (view_id, defaults, hidden) {
this.searchview = new session.web.SearchView(this, this.ds_msg, view_id || false, defaults || {}, hidden || false);
return this.searchview.appendTo(this.$element.find('div.oe_mail_wall_search'));
},
/**
* Aggregate the domains, contexts and groupbys in parameter
* with those from search form, and then calls fetch_comments
* to actually fetch comments
* @param {Array} domains
* @param {Array} contexts
* @param {Array} groupbys
*/
do_searchview_search: function(domains, contexts, groupbys) {
var self = this;
this.rpc('/web/session/eval_domain_and_context', {
domains: domains || [],
contexts: contexts || [],
group_by_seq: groupbys || []
}, function (results) {
self.params.search['context'] = results.context;
self.params.search['domain'] = results.domain;
self.params.search['groupby'] = results.group_by;
self.init_comments(self.params.search['domain'], self.params.search['context']);
});
},
/**
* Initializes Wall and calls fetch_comments
* @param {Array} domains
* @param {Array} contexts
* @param {Array} groupbys
* @param {Number} limit number of messages to fetch
*/
init_comments: function(domain, context, offset, limit) {
this.params.domain = [];
this.display_show_more = true;
this.sorted_comments = {'models': {}};
this.$element.find('div.oe_mail_wall_threads').empty();
return this.fetch_comments(domain, context, offset, limit);
},
/**
* Fetches Wall comments (mail.thread.get_pushed_messages)
* @param {Array} domains
* @param {Array} contexts
* @param {Array} groupbys
* @param {Number} limit number of messages to fetch
*/
fetch_comments: function (domain, context, offset, limit) {
var self = this;
var load_res = this.ds_thread.call('get_pushed_messages',
[[this.session.uid], (limit || 2), (offset || 0), (domain || []), true, [false], (context || null)]).then(function (records) {
if (records.length < self.params.limit) self.display_show_more = false;
self.display_comments(records);
if (! self.display_show_more) {
self.$element.find('button.oe_mail_wall_button_more:last').hide();
self.$element.find('p.oe_mail_wall_nomore:last').show();
}
});
return load_res;
},
/**
* @param {Array} records records to show in threads
*/
display_comments: function (records) {
var sorted_comments = this.sort_comments(records);
console.log(sorted_comments);
var self = this;
_(sorted_comments.model_list).each(function (model_name) {
_(sorted_comments.models[model_name].root_ids).each(function (id) {
var records = sorted_comments.models[model_name].msgs[id];
var template = 'WallThreadContainer';
var render_res = session.web.qweb.render(template, {});
$('<div class="oe_mail_wall_thread">').html(render_res).appendTo(self.$element.find('div.oe_mail_wall_threads'));
var thread = new mail.Thread(self, {
'res_model': model_name, 'res_id': records[0]['res_id'], 'uid': self.session.uid, 'records': records,
'parent_id': false, 'thread_level': self.params.thread_level}
);
var thread_displayed = thread.appendTo(self.$element.find('div.oe_mail_wall_thread:last'));
});
});
},
/**
* Add records to sorted_comments array
* @param {Array} records records from mail.message sorted by date desc
* @returns {Object} sc sorted_comments: dict
* sc.model_list = [record.model names]
* sc.models[model_name] = {
* 'root_ids': [root record.ids],
* 'id_to_root': {record.id: root.id, ..},
* 'msgs': {'root_id': [records]}, still sorted by date desc
* }, for each model
*/
sort_comments: function (records) {
var self = this;
var sc = {'model_list': [], 'models': {}}
var cur_iter = 0; var max_iter = 10; var modif = true;
/* step1: get roots */
while ( modif && (cur_iter++) < max_iter) {
modif = false;
_(records).each(function (record) {
if (_.indexOf(sc['model_list'], record.model) == -1) {
sc['model_list'].push(record.model);
sc['models'][record.model] = {'root_ids': [], 'id_to_root': {}, 'msgs': {}};
}
if (! self.sorted_comments['models'][record.model]) {
self.sorted_comments['models'][record.model] = {'root_ids': []};
}
var rmod = sc['models'][record.model];
if (record.parent_id == false && (_.indexOf(rmod['root_ids'], record.id) == -1)) {
rmod['root_ids'].push(record.id);
rmod['msgs'][record.id] = [];
self.sorted_comments['models'][record.model]['root_ids'].push(record.id);
modif = true;
}
else {
if ((_.indexOf(rmod['root_ids'], record.parent_id[0]) != -1) && (! rmod['id_to_root'][record.id])) {
rmod['id_to_root'][record.id] = record.parent_id[0];
modif = true;
}
else if ( rmod['id_to_root'][record.parent_id[0]] && (! rmod['id_to_root'][record.id])) {
rmod['id_to_root'][record.id] = rmod['id_to_root'][record.parent_id[0]];
modif = true;
}
}
});
}
/* step2: add records */
_(records).each(function (record) {
var root_id = sc['models'][record.model]['id_to_root'][record.id];
if (! root_id) root_id = record.id;
sc['models'][record.model]['msgs'][root_id].push(record);
});
return sc;
},
/**
* Create a domain to fetch new comments according to
* comment already present in sorted_comments
* @param {Object} sorted_comments (see sort_comments)
* @returns {Array} fetch_domain (OpenERP domain style)
*/
get_fetch_domain: function (sorted_comments) {
var domain = [];
_(sorted_comments.models).each(function (sc_model, model_name) { //each model
var ids = [];
_(sc_model.root_ids).each(function (id) { // each record
ids.push(id);
});
domain.push('|', ['model', '!=', model_name], '!', ['id', 'child_of', ids]);
});
return domain;
},
/**
* Action: Shows more discussions
*/
do_more: function () {
var domain = this.get_fetch_domain(this.sorted_comments);
return this.fetch_comments(domain);
},
/**
* Action: Posts a comment
*/
do_comment: function () {
var body_text = this.$element.find('textarea.oe_mail_wall_action_textarea').val();
return this.ds_users.call('message_append_note', [[this.session.uid], 'Tweet', body_text, false, 'comment']).then(this.init_comments());
},
/**
* Tools: get avatar mini (TODO: should be moved in some tools ?)
*/
thread_get_mini: function(model, field, id) {
return this.session.prefix + '/web/binary/image?session_id=' + this.session.session_id + '&model=' + model + '&field=' + field + '&id=' + (id || '');
},
});
};
// vim:et fdc=0 fdl=0 foldnestmax=3 fdm=syntax:
+96
View File
@@ -0,0 +1,96 @@
<?xml version="1.0" encoding="UTF-8"?>
<template>
<div t-name="Wall" class="oe_mail_wall">
<div class="oe_mail_wall_search">
</div>
<div class="oe_mail_wall_left">
<div class="oe_mail_wall_act">
<div class="oe_mail_wall_tweet">
<textarea class="oe_mail oe_mail_wall_action_textarea" placeholder="Add a personnal message here..."/><br />
<button class="oe_mail_wall_button_comment" type="button">Post comment</button>
</div>
</div>
<div class="oe_mail_wall_threads">
</div>
<div class="oe_mail_wall_more">
<button class="oe_mail_wall_button_more" type="button">See more discussions</button>
<p class="oe_mail_wall_nomore">You have loaded all discussions.</p>
</div>
</div>
<div class="oe_mail_wall_right">
</div>
</div>
<t t-name="WallThreadContainer">
</t>
<div t-name="RecordThread" class="oe_mail_recthread">
<div class="separator horizontal">OpenSocial</div>
<div class="oe_mail_recthread_left">
</div>
<div class="oe_mail_recthread_right">
<div class="oe_mail_actions">
<button type="button" class="oe_mail_button_follow">Not following</button>
<button type="button" class="oe_mail_button_unfollow">Following</button>
<button type="button" class="oe_mail_button_followers">Display followers</button>
</div>
<div class="oe_mail_followers">
<h4>Followers</h4>
<div class="oe_mail_followers_display">
</div>
</div>
</div>
</div>
<div t-name="Thread" class="oe_mail_thread">
<div class="oe_mail_thread_act">
<img class="oe_mail_msg_image oe_mail_oe_left" alt="User img"/>
<div class="oe_mail_msg_content">
<textarea class="oe_mail oe_mail_action_textarea" placeholder="Add your comment here..." onfocus="this.value = '';"/><br />
</div>
</div>
<div class="oe_mail_thread_display"></div>
<div class="oe_mail_thread_more">
<button class="oe_mail_button_more" type="button">Load more messages</button>
<p class="oe_mail_p_nomore">You have loaded all messages in this thread.</p>
</div>
</div>
<div t-name="ThreadMsg" t-attf-class="{record.type} oe_mail_thread_msg">
<img class="oe_mail_msg_image oe_mail_oe_left" t-att-src="record.mini_url"/>
<div class="oe_mail_msg_content">
<t t-if="record.type == 'email'"><t t-call="EmailDisplay" /></t>
<t t-if="record.type == 'notification' || record.type == 'comment'"><t t-call="NoteDisplay" /></t>
</div>
<t t-if="record.type == 'tmp'"><t t-call="ThreadDisplay" /></t>
</div>
<t t-name="NoteDisplay">
<p class="oe_mail_msg">
<a href="#" class="intlink oe_mail_oe_intlink" t-attf-data-res-model='{res_model}' t-attf-data-res-id='{res_id}'><t t-raw="name"/></a>
<span class="oe_mail_msg_body"><t t-raw="record.body_text"/></span>
<t t-if="record.tr_body_text"><span class="oe_mail_msg_body_short"><t t-raw="record.tr_body_text"/></span></t>
<br />
<span class="oe_mail_oe_fade">
<a href="#" class="intlink oe_mail_oe_intlink" data-res-model='res.users' t-attf-data-res-id='{record.user_id[0]}'><t t-raw="record.user_id[1]"/></a>
on <t t-raw="record.date"/>
</span>
<t t-if="display.show_reply"><span class="oe_mail_oe_space"><a href="#" class="oe_mail_msg_reply">Reply</a></span></t>
<t t-if="display.show_delete"><span class="oe_mail_oe_space"><a href="#" t-attf-data-id='{record.id}' class="oe_mail_msg_delete">Delete</a></span></t>
<t t-if="display.show_hide"><span class="oe_mail_oe_space"><a href="#" t-attf-data-id='{record.id}' class="oe_mail_msg_hide">Hide</a></span></t>
</p>
</t>
<t t-name="EmailDisplay">
<p class="oe_mail_msg oe_mail_msg_p_email_header">
<span class="oe_mail_oe_bold">From:</span> <t t-esc="email_from"/> on <span class="oe_mail_oe_fade"><t t-raw="record.date"/></span><br />
<span>To:</span> <t t-sec="mail_to"/><br />
<span>Subject:</span> <t t-sec="subject"/><br />
</p>
<p class="oe_mail_msg_p">
<span class="oe_mail_msg_body"><t t-raw="record.body_text"/></span>
</p>
</t>
</template>
+38
View File
@@ -0,0 +1,38 @@
# Slovenian translation for openobject-addons
# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012
# This file is distributed under the same license as the openobject-addons package.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2012.
#
msgid ""
msgstr ""
"Project-Id-Version: openobject-addons\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2012-02-08 00:36+0000\n"
"PO-Revision-Date: 2012-03-13 10:50+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: Slovenian <sl@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: 2012-03-14 04:43+0000\n"
"X-Generator: Launchpad (build 14933)\n"
#. module: pad_project
#: constraint:project.task:0
msgid "Error ! Task end-date must be greater then task start-date"
msgstr ""
#. module: pad_project
#: model:ir.model,name:pad_project.model_project_task
msgid "Task"
msgstr ""
#. module: pad_project
#: view:project.task:0
msgid "Pad"
msgstr ""
#. module: pad_project
#: constraint:project.task:0
msgid "Error ! You cannot create recursive tasks."
msgstr ""
@@ -43,5 +43,4 @@
I test that the total of the attached invoice is correct
-
!assert {model: pos.order, id: pos_order_pos1, string: Invoice not correct}:
- bool(invoice_id)
- invoice_id.amount_total == (450*2 + 300*3*1.05)*0.95
- amount_total == (450*2 + 300*3*1.05)*0.95
+285
View File
@@ -0,0 +1,285 @@
# Gujarati translation for openobject-addons
# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012
# This file is distributed under the same license as the openobject-addons package.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2012.
#
msgid ""
msgstr ""
"Project-Id-Version: openobject-addons\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2012-02-08 00:37+0000\n"
"PO-Revision-Date: 2012-03-12 15:08+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: Gujarati <gu@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: 2012-03-13 05:08+0000\n"
"X-Generator: Launchpad (build 14933)\n"
#. module: product_margin
#: view:product.product:0
#: field:product.product,turnover:0
msgid "Turnover"
msgstr ""
#. module: product_margin
#: field:product.product,expected_margin_rate:0
msgid "Expected Margin (%)"
msgstr ""
#. module: product_margin
#: field:product.margin,from_date:0
msgid "From"
msgstr "તરફથી"
#. module: product_margin
#: help:product.product,sale_expected:0
msgid ""
"Sum of Multification of Sale Catalog price and quantity of Customer Invoices"
msgstr ""
#. module: product_margin
#: help:product.product,total_margin:0
msgid "Turnorder - Standard price"
msgstr ""
#. module: product_margin
#: field:product.margin,to_date:0
msgid "To"
msgstr "પ્રતિ"
#. module: product_margin
#: field:product.product,date_to:0
msgid "To Date"
msgstr ""
#. module: product_margin
#: field:product.product,date_from:0
msgid "From Date"
msgstr ""
#. module: product_margin
#: selection:product.margin,invoice_state:0
#: selection:product.product,invoice_state:0
msgid "Draft, Open and Paid"
msgstr ""
#. module: product_margin
#: field:product.product,purchase_avg_price:0
#: field:product.product,sale_avg_price:0
msgid "Avg. Unit Price"
msgstr ""
#. module: product_margin
#: model:ir.model,name:product_margin.model_product_product
msgid "Product"
msgstr ""
#. module: product_margin
#: view:product.product:0
msgid "Catalog Price"
msgstr ""
#. module: product_margin
#: selection:product.margin,invoice_state:0
#: selection:product.product,invoice_state:0
msgid "Paid"
msgstr ""
#. module: product_margin
#: help:product.product,sales_gap:0
msgid "Expected Sale - Turn Over"
msgstr ""
#. module: product_margin
#: field:product.product,sale_expected:0
msgid "Expected Sale"
msgstr ""
#. module: product_margin
#: view:product.product:0
msgid "Standard Price"
msgstr ""
#. module: product_margin
#: help:product.product,purchase_num_invoiced:0
msgid "Sum of Quantity in Supplier Invoices"
msgstr ""
#. module: product_margin
#: help:product.product,normal_cost:0
msgid "Sum of Multification of Cost price and quantity of Supplier Invoices"
msgstr ""
#. module: product_margin
#: help:product.product,expected_margin:0
msgid "Expected Sale - Normal Cost"
msgstr ""
#. module: product_margin
#: field:product.product,purchase_num_invoiced:0
#: field:product.product,sale_num_invoiced:0
msgid "# Invoiced"
msgstr ""
#. module: product_margin
#: view:product.product:0
#: field:product.product,total_cost:0
msgid "Total Cost"
msgstr ""
#. module: product_margin
#: field:product.product,expected_margin:0
msgid "Expected Margin"
msgstr ""
#. module: product_margin
#: view:product.product:0
msgid "#Purchased"
msgstr ""
#. module: product_margin
#: help:product.product,turnover:0
msgid ""
"Sum of Multification of Invoice price and quantity of Customer Invoices"
msgstr ""
#. module: product_margin
#: help:product.product,expected_margin_rate:0
msgid "Expected margin * 100 / Expected Sale"
msgstr ""
#. module: product_margin
#: help:product.product,sale_avg_price:0
msgid "Avg. Price in Customer Invoices)"
msgstr ""
#. module: product_margin
#: help:product.product,total_cost:0
msgid ""
"Sum of Multification of Invoice price and quantity of Supplier Invoices "
msgstr ""
#. module: product_margin
#: field:product.margin,invoice_state:0
#: field:product.product,invoice_state:0
msgid "Invoice State"
msgstr ""
#. module: product_margin
#: help:product.product,purchase_gap:0
msgid "Normal Cost - Total Cost"
msgstr ""
#. module: product_margin
#: field:product.product,total_margin:0
msgid "Total Margin"
msgstr ""
#. module: product_margin
#: view:product.product:0
#: field:product.product,sales_gap:0
msgid "Sales Gap"
msgstr ""
#. module: product_margin
#: field:product.product,normal_cost:0
msgid "Normal Cost"
msgstr ""
#. module: product_margin
#: view:product.product:0
msgid "Purchases"
msgstr "ખરીદીઓ"
#. module: product_margin
#: help:product.product,purchase_avg_price:0
msgid "Avg. Price in Supplier Invoices "
msgstr ""
#. module: product_margin
#: view:product.margin:0
msgid "Properties categories"
msgstr ""
#. module: product_margin
#: help:product.product,total_margin_rate:0
msgid "Total margin * 100 / Turnover"
msgstr ""
#. module: product_margin
#: view:product.product:0
msgid "Analysis Criteria"
msgstr ""
#. module: product_margin
#: selection:product.margin,invoice_state:0
#: selection:product.product,invoice_state:0
msgid "Open and Paid"
msgstr ""
#. module: product_margin
#: view:product.product:0
msgid "Sales"
msgstr "વેચાણ"
#. module: product_margin
#: code:addons/product_margin/wizard/product_margin.py:73
#: model:ir.actions.act_window,name:product_margin.product_margin_act_window
#: model:ir.ui.menu,name:product_margin.menu_action_product_margin
#: view:product.product:0
#, python-format
msgid "Product Margins"
msgstr ""
#. module: product_margin
#: view:product.margin:0
msgid "General Information"
msgstr ""
#. module: product_margin
#: constraint:product.product:0
msgid "Error: Invalid ean code"
msgstr ""
#. module: product_margin
#: field:product.product,purchase_gap:0
msgid "Purchase Gap"
msgstr ""
#. module: product_margin
#: field:product.product,total_margin_rate:0
msgid "Total Margin (%)"
msgstr ""
#. module: product_margin
#: view:product.margin:0
msgid "Open Margins"
msgstr ""
#. module: product_margin
#: view:product.margin:0
msgid "Cancel"
msgstr "રદ કરો"
#. module: product_margin
#: view:product.product:0
msgid "Margins"
msgstr ""
#. module: product_margin
#: help:product.product,sale_num_invoiced:0
msgid "Sum of Quantity in Customer Invoices"
msgstr ""
#. module: product_margin
#: model:ir.model,name:product_margin.model_product_margin
msgid "Product Margin"
msgstr ""
#~ msgid "Reporting"
#~ msgstr "અહેવાલીકરણ"
#~ msgid "Invalid XML for View Architecture!"
#~ msgstr "દ્રશ્ય બંધારણ માટે અમાન્ય એક્સ.એમ.એલ!"
+6
View File
@@ -0,0 +1,6 @@
:orphan:
Project module documentation
============================
.. include:: index.rst.inc
+8
View File
@@ -0,0 +1,8 @@
Project Module
''''''''''''''
.. toctree::
:maxdepth: 1
task_kanban_avatar
@@ -0,0 +1,4 @@
Task kanban avatar specs
========================
Kanban view of tasks has been updated to use the newly-introduced avatar_mini field of res.users. The avatar is placed in the right-side of the containing kanban box.
+2 -1
View File
@@ -354,7 +354,8 @@
<field name="name"/>
</td>
<td valign="top" width="22">
<img t-att-src="kanban_gravatar(record.user_email.value, 22)" class="oe_kanban_gravatar" t-att-title="record.user_id.value"/>
<img t-att-src="kanban_image('res.users', 'avatar_mini', record.user_id.raw_value[0])" t-att-title="record.user_id.value"
width="22" height="22" class="oe_kanban_gravatar"/>
</td>
</tr>
</table>
+127
View File
@@ -0,0 +1,127 @@
# Gujarati translation for openobject-addons
# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012
# This file is distributed under the same license as the openobject-addons package.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2012.
#
msgid ""
msgstr ""
"Project-Id-Version: openobject-addons\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2012-02-08 00:37+0000\n"
"PO-Revision-Date: 2012-03-12 15:08+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: Gujarati <gu@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: 2012-03-13 05:08+0000\n"
"X-Generator: Launchpad (build 14933)\n"
#. module: project_mrp
#: sql_constraint:sale.order:0
msgid "Order Reference must be unique per Company!"
msgstr ""
#. module: project_mrp
#: model:process.node,note:project_mrp.process_node_procuretasktask0
msgid "For each product, on type service and on order"
msgstr ""
#. module: project_mrp
#: model:process.transition,name:project_mrp.process_transition_procuretask0
msgid "Procurement Task"
msgstr ""
#. module: project_mrp
#: model:ir.model,name:project_mrp.model_sale_order
msgid "Sales Order"
msgstr ""
#. module: project_mrp
#: field:procurement.order,sale_line_id:0
msgid "Sale order line"
msgstr ""
#. module: project_mrp
#: model:process.transition,note:project_mrp.process_transition_createtask0
msgid "Product type is service, then its creates the task."
msgstr ""
#. module: project_mrp
#: model:process.transition,note:project_mrp.process_transition_procuretask0
msgid "if product type is 'service' then it creates the task."
msgstr ""
#. module: project_mrp
#: constraint:project.task:0
msgid "Error ! Task end-date must be greater then task start-date"
msgstr ""
#. module: project_mrp
#: model:process.node,name:project_mrp.process_node_saleordertask0
msgid "Sale Order Task"
msgstr ""
#. module: project_mrp
#: model:ir.model,name:project_mrp.model_product_product
msgid "Product"
msgstr ""
#. module: project_mrp
#: constraint:product.product:0
msgid "Error: Invalid ean code"
msgstr ""
#. module: project_mrp
#: field:product.product,project_id:0
msgid "Project"
msgstr ""
#. module: project_mrp
#: model:ir.model,name:project_mrp.model_procurement_order
#: field:project.task,procurement_id:0
msgid "Procurement"
msgstr ""
#. module: project_mrp
#: model:ir.model,name:project_mrp.model_project_task
#: model:process.node,name:project_mrp.process_node_mrptask0
#: model:process.node,name:project_mrp.process_node_procuretasktask0
#: field:procurement.order,task_id:0
msgid "Task"
msgstr "કાર્ય"
#. module: project_mrp
#: model:process.node,note:project_mrp.process_node_mrptask0
msgid "A task is created to provide the service."
msgstr ""
#. module: project_mrp
#: model:process.transition,name:project_mrp.process_transition_ordertask0
msgid "Order Task"
msgstr ""
#. module: project_mrp
#: constraint:project.task:0
msgid "Error ! You cannot create recursive tasks."
msgstr ""
#. module: project_mrp
#: model:process.transition,name:project_mrp.process_transition_createtask0
msgid "Create Task"
msgstr ""
#. module: project_mrp
#: model:process.transition,note:project_mrp.process_transition_ordertask0
msgid "If procurement method is Make to order and supply method is produce"
msgstr ""
#. module: project_mrp
#: model:process.node,note:project_mrp.process_node_saleordertask0
msgid "In case you sell services on sale order"
msgstr ""
#. module: project_mrp
#: field:project.task,sale_line_id:0
msgid "Sale Order Line"
msgstr ""
+6 -6
View File
@@ -7,14 +7,14 @@ msgstr ""
"Project-Id-Version: OpenERP Server 6.0dev\n"
"Report-Msgid-Bugs-To: support@openerp.com\n"
"POT-Creation-Date: 2012-02-08 00:37+0000\n"
"PO-Revision-Date: 2011-11-25 14:47+0000\n"
"Last-Translator: lholivier <olivier.lenoir@free.fr>\n"
"PO-Revision-Date: 2012-03-12 08:37+0000\n"
"Last-Translator: Numérigraphe <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: 2012-02-09 06:06+0000\n"
"X-Generator: Launchpad (build 14763)\n"
"X-Launchpad-Export-Date: 2012-03-13 05:08+0000\n"
"X-Generator: Launchpad (build 14933)\n"
#. module: project_scrum
#: view:project.scrum.backlog.assign.sprint:0
@@ -549,7 +549,7 @@ msgstr "Mois"
#. module: project_scrum
#: view:project.scrum.product.backlog:0
msgid "Draft Backlogs"
msgstr "Retards Brouillon"
msgstr "Arriérés brouillons"
#. module: project_scrum
#: field:project.scrum.meeting,question_blocks:0
@@ -798,7 +798,7 @@ msgstr "Informations"
#: code:addons/project_scrum/wizard/project_scrum_backlog_sprint.py:57
#, python-format
msgid "Product Backlog '%s' is converted into Task %d."
msgstr ""
msgstr "L'arriéré sur le produit '%s' a été converti en la tâche %d."
#. module: project_scrum
#: view:project.scrum.product.backlog:0
+2 -2
View File
@@ -246,7 +246,7 @@
<field name="product_uom" ref="product.product_uom_unit"/>
<field name="product_uom_qty">15</field>
<field name="price_unit">250</field>
<field name="type">make_to_order</field>
<field name="type">make_to_stock</field>
<field name="delay">15</field>
</record>
<record id="order7_line1" model="sale.order.line">
@@ -256,7 +256,7 @@
<field name="product_uom" ref="product.product_uom_unit"/>
<field name="product_uom_qty">5</field>
<field name="price_unit">500</field>
<field name="type">make_to_order</field>
<field name="type">make_to_stock</field>
<field name="delay">15</field>
</record>
+6 -6
View File
@@ -7,14 +7,14 @@ msgstr ""
"Project-Id-Version: OpenERP Server 6.0dev\n"
"Report-Msgid-Bugs-To: support@openerp.com\n"
"POT-Creation-Date: 2012-02-08 01:37+0100\n"
"PO-Revision-Date: 2012-03-05 14:29+0000\n"
"Last-Translator: gde (OpenERP) <Unknown>\n"
"PO-Revision-Date: 2012-03-12 08:35+0000\n"
"Last-Translator: Numérigraphe <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: 2012-03-06 04:48+0000\n"
"X-Generator: Launchpad (build 14900)\n"
"X-Launchpad-Export-Date: 2012-03-13 05:07+0000\n"
"X-Generator: Launchpad (build 14933)\n"
#. module: stock
#: field:stock.inventory.line,product_uom:0
@@ -105,7 +105,7 @@ msgstr "Mouvements du produit"
#. module: stock
#: model:ir.ui.menu,name:stock.menu_stock_uom_categ_form_action
msgid "UoM Categories"
msgstr ""
msgstr "Catégories des UdM"
#. module: stock
#: model:ir.actions.act_window,name:stock.action_stock_move_report
@@ -1231,7 +1231,7 @@ msgstr "Mars"
#: model:ir.model,name:stock.model_stock_inventory_line_split
#: view:stock.inventory:0 view:stock.inventory.line:0
msgid "Split inventory lines"
msgstr "Eclater les lignes d'inventaire"
msgstr "Diviser les lignes d'inventaire"
#. module: stock
#: view:stock.inventory:0